From 0b3cbf57875fd692e4ba0b336fefa4bee1ed00dc Mon Sep 17 00:00:00 2001 From: Simeon Simeonov Date: Mon, 26 Feb 2018 11:23:00 +0100 Subject: Initial commit for sylpheed 3.7.0 --- src/Makefile.am | 191 + src/Makefile.in | 1256 +++++ src/about.c | 274 + src/about.h | 25 + src/account_dialog.c | 847 +++ src/account_dialog.h | 35 + src/action.c | 1373 +++++ src/action.h | 56 + src/addr_compl.c | 1142 ++++ src/addr_compl.h | 54 + src/addrbook.c | 2020 ++++++++ src/addrbook.h | 116 + src/addrcache.c | 1237 +++++ src/addrcache.h | 124 + src/addressadd.c | 447 ++ src/addressadd.h | 33 + src/addressbook.c | 4919 ++++++++++++++++++ src/addressbook.h | 61 + src/addressitem.h | 157 + src/addrindex.c | 1925 +++++++ src/addrindex.h | 133 + src/addritem.c | 989 ++++ src/addritem.h | 174 + src/alertpanel.c | 414 ++ src/alertpanel.h | 76 + src/colorlabel.c | 435 ++ src/colorlabel.h | 42 + src/compose.c | 8612 +++++++++++++++++++++++++++++++ src/compose.h | 267 + src/editaddress.c | 1044 ++++ src/editaddress.h | 29 + src/editbook.c | 360 ++ src/editbook.h | 29 + src/editgroup.c | 902 ++++ src/editgroup.h | 26 + src/editjpilot.c | 429 ++ src/editjpilot.h | 33 + src/editldap.c | 611 +++ src/editldap.h | 29 + src/editldap_basedn.c | 342 ++ src/editldap_basedn.h | 30 + src/editvcard.c | 309 ++ src/editvcard.h | 29 + src/eggtrayicon.c | 583 +++ src/eggtrayicon.h | 84 + src/export.c | 551 ++ src/export.h | 29 + src/exportcsv.c | 607 +++ src/exportcsv.h | 34 + src/filesel.c | 611 +++ src/filesel.h | 48 + src/foldersel.c | 765 +++ src/foldersel.h | 44 + src/folderview.c | 3405 ++++++++++++ src/folderview.h | 121 + src/gtkshruler.c | 228 + src/gtkshruler.h | 71 + src/gtkutils.c | 1119 ++++ src/gtkutils.h | 238 + src/headerview.c | 428 ++ src/headerview.h | 60 + src/icons/Makefile.am | 99 + src/icons/Makefile.in | 591 +++ src/icons/address.xpm | 87 + src/icons/book.xpm | 23 + src/icons/category.xpm | 35 + src/icons/clip.xpm | 19 + src/icons/complete.xpm | 17 + src/icons/continue.xpm | 42 + src/icons/deleted.xpm | 15 + src/icons/error.xpm | 45 + src/icons/folder-close.h | 59 + src/icons/folder-close.png | Bin 0 -> 468 bytes src/icons/folder-noselect.h | 62 + src/icons/folder-noselect.png | Bin 0 -> 415 bytes src/icons/folder-open.h | 59 + src/icons/folder-open.png | Bin 0 -> 542 bytes src/icons/folder-search.h | 57 + src/icons/folder-search.png | Bin 0 -> 618 bytes src/icons/forwarded.xpm | 23 + src/icons/group.h | 60 + src/icons/group.png | Bin 0 -> 692 bytes src/icons/html.h | 65 + src/icons/html.png | Bin 0 -> 697 bytes src/icons/interface.xpm | 24 + src/icons/jpilot.xpm | 25 + src/icons/ldap.xpm | 25 + src/icons/linewrap.xpm | 29 + src/icons/mail.xpm | 41 + src/icons/mark.xpm | 16 + src/icons/new.xpm | 62 + src/icons/offline.xpm | 228 + src/icons/online.xpm | 232 + src/icons/replied.xpm | 24 + src/icons/stock_addressbook.h | 86 + src/icons/stock_addressbook.png | Bin 0 -> 1284 bytes src/icons/stock_attach.h | 93 + src/icons/stock_attach.png | Bin 0 -> 1039 bytes src/icons/stock_book.h | 60 + src/icons/stock_book.png | Bin 0 -> 433 bytes src/icons/stock_delete.h | 105 + src/icons/stock_delete.png | Bin 0 -> 1282 bytes src/icons/stock_delete_16.h | 61 + src/icons/stock_delete_16.png | Bin 0 -> 778 bytes src/icons/stock_hand-signed.h | 82 + src/icons/stock_hand-signed.png | Bin 0 -> 868 bytes src/icons/stock_inbox.h | 57 + src/icons/stock_inbox.png | Bin 0 -> 626 bytes src/icons/stock_insert-file.h | 92 + src/icons/stock_insert-file.png | Bin 0 -> 959 bytes src/icons/stock_mail-compose.h | 118 + src/icons/stock_mail-compose.png | Bin 0 -> 1061 bytes src/icons/stock_mail-compose_16.h | 58 + src/icons/stock_mail-compose_16.png | Bin 0 -> 492 bytes src/icons/stock_mail-forward.h | 83 + src/icons/stock_mail-forward.png | Bin 0 -> 775 bytes src/icons/stock_mail-receive.h | 87 + src/icons/stock_mail-receive.png | Bin 0 -> 803 bytes src/icons/stock_mail-reply-to-all.h | 82 + src/icons/stock_mail-reply-to-all.png | Bin 0 -> 1132 bytes src/icons/stock_mail-reply.h | 83 + src/icons/stock_mail-reply.png | Bin 0 -> 776 bytes src/icons/stock_mail-send.h | 84 + src/icons/stock_mail-send.png | Bin 0 -> 746 bytes src/icons/stock_mail.h | 68 + src/icons/stock_mail.png | Bin 0 -> 551 bytes src/icons/stock_mail_receive_all.h | 91 + src/icons/stock_mail_receive_all.png | Bin 0 -> 879 bytes src/icons/stock_mail_send_queue.h | 92 + src/icons/stock_mail_send_queue.png | Bin 0 -> 1034 bytes src/icons/stock_notspam.h | 81 + src/icons/stock_notspam.png | Bin 0 -> 1140 bytes src/icons/stock_outbox.h | 57 + src/icons/stock_outbox.png | Bin 0 -> 600 bytes src/icons/stock_person.h | 63 + src/icons/stock_person.png | Bin 0 -> 594 bytes src/icons/stock_spam.h | 86 + src/icons/stock_spam.png | Bin 0 -> 1276 bytes src/icons/stock_spam_16.h | 73 + src/icons/stock_spam_16.png | Bin 0 -> 845 bytes src/icons/stock_sylpheed.h | 256 + src/icons/stock_sylpheed.png | Bin 0 -> 2718 bytes src/icons/stock_sylpheed_16.h | 67 + src/icons/stock_sylpheed_16.png | Bin 0 -> 690 bytes src/icons/stock_sylpheed_32.h | 157 + src/icons/stock_sylpheed_32.png | Bin 0 -> 1925 bytes src/icons/stock_sylpheed_newmail.h | 246 + src/icons/stock_sylpheed_newmail.png | Bin 0 -> 2483 bytes src/icons/stock_sylpheed_newmail_16.h | 68 + src/icons/stock_sylpheed_newmail_16.png | Bin 0 -> 769 bytes src/icons/sylpheed-logo.h | 515 ++ src/icons/sylpheed-logo.png | Bin 0 -> 8421 bytes src/icons/sylpheed.ico | Bin 0 -> 18718 bytes src/icons/unread.xpm | 50 + src/icons/vcard.xpm | 25 + src/imageview.c | 314 ++ src/imageview.h | 59 + src/import.c | 785 +++ src/import.h | 31 + src/importcsv.c | 1008 ++++ src/importcsv.h | 34 + src/importldif.c | 883 ++++ src/importldif.h | 37 + src/inc.c | 1936 +++++++ src/inc.h | 115 + src/inputdialog.c | 389 ++ src/inputdialog.h | 45 + src/jpilot.c | 1739 +++++++ src/jpilot.h | 117 + src/ldif.c | 986 ++++ src/ldif.h | 121 + src/libsylpheed-plugin-0.def | 114 + src/logwindow.c | 341 ++ src/logwindow.h | 62 + src/main.c | 2083 ++++++++ src/main.h | 30 + src/mainwindow.c | 4276 +++++++++++++++ src/mainwindow.h | 192 + src/manage_window.c | 103 + src/manage_window.h | 59 + src/manual.c | 112 + src/manual.h | 36 + src/menu.c | 300 ++ src/menu.h | 150 + src/message_search.c | 283 + src/message_search.h | 29 + src/messageview.c | 1262 +++++ src/messageview.h | 112 + src/mgutils.c | 220 + src/mgutils.h | 62 + src/mimeview.c | 1431 +++++ src/mimeview.h | 109 + src/notificationwindow.c | 347 ++ src/notificationwindow.h | 33 + src/passphrase.c | 354 ++ src/passphrase.h | 35 + src/plugin-marshal.c | 260 + src/plugin-marshal.h | 58 + src/plugin-marshal.list | 7 + src/plugin-types.h | 38 + src/plugin.c | 1535 ++++++ src/plugin.h | 376 ++ src/plugin_manager.c | 255 + src/plugin_manager.h | 29 + src/prefs_account_dialog.c | 2723 ++++++++++ src/prefs_account_dialog.h | 29 + src/prefs_actions.c | 674 +++ src/prefs_actions.h | 29 + src/prefs_common_dialog.c | 4807 +++++++++++++++++ src/prefs_common_dialog.h | 33 + src/prefs_customheader.c | 495 ++ src/prefs_customheader.h | 27 + src/prefs_display_header.c | 640 +++ src/prefs_display_header.h | 27 + src/prefs_display_items.c | 623 +++ src/prefs_display_items.h | 87 + src/prefs_filter.c | 875 ++++ src/prefs_filter.h | 46 + src/prefs_filter_edit.c | 2202 ++++++++ src/prefs_filter_edit.h | 196 + src/prefs_folder_item.c | 639 +++ src/prefs_folder_item.h | 29 + src/prefs_search_folder.c | 453 ++ src/prefs_search_folder.h | 29 + src/prefs_summary_column.c | 592 +++ src/prefs_summary_column.h | 31 + src/prefs_template.c | 685 +++ src/prefs_template.h | 25 + src/prefs_toolbar.c | 409 ++ src/prefs_toolbar.h | 99 + src/prefs_ui.c | 578 +++ src/prefs_ui.h | 183 + src/printing.c | 747 +++ src/printing.h | 49 + src/progressdialog.c | 300 ++ src/progressdialog.h | 93 + src/query_search.c | 1698 ++++++ src/query_search.h | 29 + src/quick_search.c | 447 ++ src/quick_search.h | 66 + src/quote_fmt.h | 13 + src/quote_fmt_lex.c | 1900 +++++++ src/quote_fmt_lex.h | 47 + src/quote_fmt_lex.l | 46 + src/quote_fmt_parse.c | 2199 ++++++++ src/quote_fmt_parse.h | 132 + src/quote_fmt_parse.y | 461 ++ src/rfc2015.c | 1992 +++++++ src/rfc2015.h | 66 + src/rpop3.c | 1418 +++++ src/rpop3.h | 34 + src/select-keys.c | 567 ++ src/select-keys.h | 29 + src/send_message.c | 1034 ++++ src/send_message.h | 58 + src/setup.c | 1281 +++++ src/setup.h | 32 + src/sigstatus.c | 280 + src/sigstatus.h | 34 + src/simple-gettext.c | 386 ++ src/sourcewindow.c | 212 + src/sourcewindow.h | 45 + src/sslmanager.c | 225 + src/sslmanager.h | 40 + src/statusbar.c | 112 + src/statusbar.h | 38 + src/stock_pixmap.c | 335 ++ src/stock_pixmap.h | 122 + src/subscribedialog.c | 661 +++ src/subscribedialog.h | 29 + src/summaryview.c | 6683 ++++++++++++++++++++++++ src/summaryview.h | 306 ++ src/syldap.c | 1127 ++++ src/syldap.h | 111 + src/sylpheed-marshal.c | 88 + src/sylpheed-marshal.h | 23 + src/sylpheed-marshal.list | 2 + src/sylpheed.exe.manifest | 12 + src/sylpheed.rc.in | 34 + src/template.c | 245 + src/template.h | 49 + src/textview.c | 2765 ++++++++++ src/textview.h | 93 + src/trayicon.c | 434 ++ src/trayicon.h | 53 + src/undo.c | 673 +++ src/undo.h | 80 + src/update_check.c | 826 +++ src/update_check.h | 55 + src/vcard.c | 777 +++ src/vcard.h | 105 + src/version.h | 33 + src/version.h.in | 33 + 293 files changed, 118373 insertions(+) create mode 100644 src/Makefile.am create mode 100644 src/Makefile.in create mode 100644 src/about.c create mode 100644 src/about.h create mode 100644 src/account_dialog.c create mode 100644 src/account_dialog.h create mode 100644 src/action.c create mode 100644 src/action.h create mode 100644 src/addr_compl.c create mode 100644 src/addr_compl.h create mode 100644 src/addrbook.c create mode 100644 src/addrbook.h create mode 100644 src/addrcache.c create mode 100644 src/addrcache.h create mode 100644 src/addressadd.c create mode 100644 src/addressadd.h create mode 100644 src/addressbook.c create mode 100644 src/addressbook.h create mode 100644 src/addressitem.h create mode 100644 src/addrindex.c create mode 100644 src/addrindex.h create mode 100644 src/addritem.c create mode 100644 src/addritem.h create mode 100644 src/alertpanel.c create mode 100644 src/alertpanel.h create mode 100644 src/colorlabel.c create mode 100644 src/colorlabel.h create mode 100644 src/compose.c create mode 100644 src/compose.h create mode 100644 src/editaddress.c create mode 100644 src/editaddress.h create mode 100644 src/editbook.c create mode 100644 src/editbook.h create mode 100644 src/editgroup.c create mode 100644 src/editgroup.h create mode 100644 src/editjpilot.c create mode 100644 src/editjpilot.h create mode 100644 src/editldap.c create mode 100644 src/editldap.h create mode 100644 src/editldap_basedn.c create mode 100644 src/editldap_basedn.h create mode 100644 src/editvcard.c create mode 100644 src/editvcard.h create mode 100644 src/eggtrayicon.c create mode 100644 src/eggtrayicon.h create mode 100644 src/export.c create mode 100644 src/export.h create mode 100644 src/exportcsv.c create mode 100644 src/exportcsv.h create mode 100644 src/filesel.c create mode 100644 src/filesel.h create mode 100644 src/foldersel.c create mode 100644 src/foldersel.h create mode 100644 src/folderview.c create mode 100644 src/folderview.h create mode 100644 src/gtkshruler.c create mode 100644 src/gtkshruler.h create mode 100644 src/gtkutils.c create mode 100644 src/gtkutils.h create mode 100644 src/headerview.c create mode 100644 src/headerview.h create mode 100644 src/icons/Makefile.am create mode 100644 src/icons/Makefile.in create mode 100644 src/icons/address.xpm create mode 100644 src/icons/book.xpm create mode 100644 src/icons/category.xpm create mode 100644 src/icons/clip.xpm create mode 100644 src/icons/complete.xpm create mode 100644 src/icons/continue.xpm create mode 100644 src/icons/deleted.xpm create mode 100644 src/icons/error.xpm create mode 100644 src/icons/folder-close.h create mode 100644 src/icons/folder-close.png create mode 100644 src/icons/folder-noselect.h create mode 100644 src/icons/folder-noselect.png create mode 100644 src/icons/folder-open.h create mode 100644 src/icons/folder-open.png create mode 100644 src/icons/folder-search.h create mode 100644 src/icons/folder-search.png create mode 100644 src/icons/forwarded.xpm create mode 100644 src/icons/group.h create mode 100644 src/icons/group.png create mode 100644 src/icons/html.h create mode 100644 src/icons/html.png create mode 100644 src/icons/interface.xpm create mode 100644 src/icons/jpilot.xpm create mode 100644 src/icons/ldap.xpm create mode 100644 src/icons/linewrap.xpm create mode 100644 src/icons/mail.xpm create mode 100644 src/icons/mark.xpm create mode 100644 src/icons/new.xpm create mode 100644 src/icons/offline.xpm create mode 100644 src/icons/online.xpm create mode 100644 src/icons/replied.xpm create mode 100644 src/icons/stock_addressbook.h create mode 100644 src/icons/stock_addressbook.png create mode 100644 src/icons/stock_attach.h create mode 100644 src/icons/stock_attach.png create mode 100644 src/icons/stock_book.h create mode 100644 src/icons/stock_book.png create mode 100644 src/icons/stock_delete.h create mode 100644 src/icons/stock_delete.png create mode 100644 src/icons/stock_delete_16.h create mode 100644 src/icons/stock_delete_16.png create mode 100644 src/icons/stock_hand-signed.h create mode 100644 src/icons/stock_hand-signed.png create mode 100644 src/icons/stock_inbox.h create mode 100644 src/icons/stock_inbox.png create mode 100644 src/icons/stock_insert-file.h create mode 100644 src/icons/stock_insert-file.png create mode 100644 src/icons/stock_mail-compose.h create mode 100644 src/icons/stock_mail-compose.png create mode 100644 src/icons/stock_mail-compose_16.h create mode 100644 src/icons/stock_mail-compose_16.png create mode 100644 src/icons/stock_mail-forward.h create mode 100644 src/icons/stock_mail-forward.png create mode 100644 src/icons/stock_mail-receive.h create mode 100644 src/icons/stock_mail-receive.png create mode 100644 src/icons/stock_mail-reply-to-all.h create mode 100644 src/icons/stock_mail-reply-to-all.png create mode 100644 src/icons/stock_mail-reply.h create mode 100644 src/icons/stock_mail-reply.png create mode 100644 src/icons/stock_mail-send.h create mode 100644 src/icons/stock_mail-send.png create mode 100644 src/icons/stock_mail.h create mode 100644 src/icons/stock_mail.png create mode 100644 src/icons/stock_mail_receive_all.h create mode 100644 src/icons/stock_mail_receive_all.png create mode 100644 src/icons/stock_mail_send_queue.h create mode 100644 src/icons/stock_mail_send_queue.png create mode 100644 src/icons/stock_notspam.h create mode 100644 src/icons/stock_notspam.png create mode 100644 src/icons/stock_outbox.h create mode 100644 src/icons/stock_outbox.png create mode 100644 src/icons/stock_person.h create mode 100644 src/icons/stock_person.png create mode 100644 src/icons/stock_spam.h create mode 100644 src/icons/stock_spam.png create mode 100644 src/icons/stock_spam_16.h create mode 100644 src/icons/stock_spam_16.png create mode 100644 src/icons/stock_sylpheed.h create mode 100644 src/icons/stock_sylpheed.png create mode 100644 src/icons/stock_sylpheed_16.h create mode 100644 src/icons/stock_sylpheed_16.png create mode 100644 src/icons/stock_sylpheed_32.h create mode 100644 src/icons/stock_sylpheed_32.png create mode 100644 src/icons/stock_sylpheed_newmail.h create mode 100644 src/icons/stock_sylpheed_newmail.png create mode 100644 src/icons/stock_sylpheed_newmail_16.h create mode 100644 src/icons/stock_sylpheed_newmail_16.png create mode 100644 src/icons/sylpheed-logo.h create mode 100644 src/icons/sylpheed-logo.png create mode 100644 src/icons/sylpheed.ico create mode 100644 src/icons/unread.xpm create mode 100644 src/icons/vcard.xpm create mode 100644 src/imageview.c create mode 100644 src/imageview.h create mode 100644 src/import.c create mode 100644 src/import.h create mode 100644 src/importcsv.c create mode 100644 src/importcsv.h create mode 100644 src/importldif.c create mode 100644 src/importldif.h create mode 100644 src/inc.c create mode 100644 src/inc.h create mode 100644 src/inputdialog.c create mode 100644 src/inputdialog.h create mode 100644 src/jpilot.c create mode 100644 src/jpilot.h create mode 100644 src/ldif.c create mode 100644 src/ldif.h create mode 100644 src/libsylpheed-plugin-0.def create mode 100644 src/logwindow.c create mode 100644 src/logwindow.h create mode 100644 src/main.c create mode 100644 src/main.h create mode 100644 src/mainwindow.c create mode 100644 src/mainwindow.h create mode 100644 src/manage_window.c create mode 100644 src/manage_window.h create mode 100644 src/manual.c create mode 100644 src/manual.h create mode 100644 src/menu.c create mode 100644 src/menu.h create mode 100644 src/message_search.c create mode 100644 src/message_search.h create mode 100644 src/messageview.c create mode 100644 src/messageview.h create mode 100644 src/mgutils.c create mode 100644 src/mgutils.h create mode 100644 src/mimeview.c create mode 100644 src/mimeview.h create mode 100644 src/notificationwindow.c create mode 100644 src/notificationwindow.h create mode 100644 src/passphrase.c create mode 100644 src/passphrase.h create mode 100644 src/plugin-marshal.c create mode 100644 src/plugin-marshal.h create mode 100644 src/plugin-marshal.list create mode 100644 src/plugin-types.h create mode 100644 src/plugin.c create mode 100644 src/plugin.h create mode 100644 src/plugin_manager.c create mode 100644 src/plugin_manager.h create mode 100644 src/prefs_account_dialog.c create mode 100644 src/prefs_account_dialog.h create mode 100644 src/prefs_actions.c create mode 100644 src/prefs_actions.h create mode 100644 src/prefs_common_dialog.c create mode 100644 src/prefs_common_dialog.h create mode 100644 src/prefs_customheader.c create mode 100644 src/prefs_customheader.h create mode 100644 src/prefs_display_header.c create mode 100644 src/prefs_display_header.h create mode 100644 src/prefs_display_items.c create mode 100644 src/prefs_display_items.h create mode 100644 src/prefs_filter.c create mode 100644 src/prefs_filter.h create mode 100644 src/prefs_filter_edit.c create mode 100644 src/prefs_filter_edit.h create mode 100644 src/prefs_folder_item.c create mode 100644 src/prefs_folder_item.h create mode 100644 src/prefs_search_folder.c create mode 100644 src/prefs_search_folder.h create mode 100644 src/prefs_summary_column.c create mode 100644 src/prefs_summary_column.h create mode 100644 src/prefs_template.c create mode 100644 src/prefs_template.h create mode 100644 src/prefs_toolbar.c create mode 100644 src/prefs_toolbar.h create mode 100644 src/prefs_ui.c create mode 100644 src/prefs_ui.h create mode 100644 src/printing.c create mode 100644 src/printing.h create mode 100644 src/progressdialog.c create mode 100644 src/progressdialog.h create mode 100644 src/query_search.c create mode 100644 src/query_search.h create mode 100644 src/quick_search.c create mode 100644 src/quick_search.h create mode 100644 src/quote_fmt.h create mode 100644 src/quote_fmt_lex.c create mode 100644 src/quote_fmt_lex.h create mode 100644 src/quote_fmt_lex.l create mode 100644 src/quote_fmt_parse.c create mode 100644 src/quote_fmt_parse.h create mode 100644 src/quote_fmt_parse.y create mode 100644 src/rfc2015.c create mode 100644 src/rfc2015.h create mode 100644 src/rpop3.c create mode 100644 src/rpop3.h create mode 100644 src/select-keys.c create mode 100644 src/select-keys.h create mode 100644 src/send_message.c create mode 100644 src/send_message.h create mode 100644 src/setup.c create mode 100644 src/setup.h create mode 100644 src/sigstatus.c create mode 100644 src/sigstatus.h create mode 100644 src/simple-gettext.c create mode 100644 src/sourcewindow.c create mode 100644 src/sourcewindow.h create mode 100644 src/sslmanager.c create mode 100644 src/sslmanager.h create mode 100644 src/statusbar.c create mode 100644 src/statusbar.h create mode 100644 src/stock_pixmap.c create mode 100644 src/stock_pixmap.h create mode 100644 src/subscribedialog.c create mode 100644 src/subscribedialog.h create mode 100644 src/summaryview.c create mode 100644 src/summaryview.h create mode 100644 src/syldap.c create mode 100644 src/syldap.h create mode 100644 src/sylpheed-marshal.c create mode 100644 src/sylpheed-marshal.h create mode 100644 src/sylpheed-marshal.list create mode 100644 src/sylpheed.exe.manifest create mode 100644 src/sylpheed.rc.in create mode 100644 src/template.c create mode 100644 src/template.h create mode 100644 src/textview.c create mode 100644 src/textview.h create mode 100644 src/trayicon.c create mode 100644 src/trayicon.h create mode 100644 src/undo.c create mode 100644 src/undo.h create mode 100644 src/update_check.c create mode 100644 src/update_check.h create mode 100644 src/vcard.c create mode 100644 src/vcard.h create mode 100644 src/version.h create mode 100644 src/version.h.in (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..c247f1c --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,191 @@ +SUBDIRS = icons + +bin_PROGRAMS = sylpheed + +sylpheed_SOURCES = \ + version.h \ + main.c main.h \ + mainwindow.c mainwindow.h \ + folderview.c folderview.h \ + summaryview.c summaryview.h \ + messageview.c messageview.h \ + headerview.c headerview.h \ + textview.c textview.h \ + imageview.c imageview.h \ + mimeview.c mimeview.h \ + quick_search.c quick_search.h \ + query_search.c query_search.h \ + message_search.c message_search.h \ + colorlabel.c colorlabel.h \ + action.c action.h \ + compose.c compose.h \ + gtkshruler.c gtkshruler.h \ + menu.c menu.h \ + stock_pixmap.c stock_pixmap.h \ + prefs_ui.c prefs_ui.h \ + prefs_common_dialog.c prefs_common_dialog.h \ + prefs_filter.c prefs_filter.h \ + prefs_filter_edit.c prefs_filter_edit.h \ + prefs_account_dialog.c prefs_account_dialog.h \ + prefs_folder_item.c prefs_folder_item.h \ + prefs_display_items.c prefs_display_items.h \ + prefs_display_header.c prefs_display_header.h \ + prefs_customheader.c prefs_customheader.h \ + prefs_summary_column.c prefs_summary_column.h \ + prefs_template.c prefs_template.h \ + prefs_actions.c prefs_actions.h \ + prefs_search_folder.c prefs_search_folder.h \ + prefs_toolbar.c prefs_toolbar.h \ + account_dialog.c account_dialog.h \ + template.c template.h \ + addressbook.c addressbook.h \ + addr_compl.c addr_compl.h \ + addressitem.h \ + addritem.c addritem.h \ + addrcache.c addrcache.h \ + addrbook.c addrbook.h \ + addrindex.c addrindex.h \ + mgutils.c mgutils.h \ + vcard.c vcard.h \ + ldif.c ldif.h \ + importldif.c importldif.h \ + importcsv.c importcsv.h \ + exportcsv.c exportcsv.h \ + jpilot.c jpilot.h \ + syldap.c syldap.h \ + editbook.c editbook.h \ + editgroup.c editgroup.h \ + editaddress.c editaddress.h \ + editvcard.c editvcard.h \ + editjpilot.c editjpilot.h \ + editldap.c editldap.h \ + editldap_basedn.c editldap_basedn.h \ + addressadd.c addressadd.h \ + filesel.c filesel.h \ + foldersel.c foldersel.h \ + statusbar.c statusbar.h \ + logwindow.c logwindow.h \ + sourcewindow.c sourcewindow.h \ + manage_window.c manage_window.h \ + undo.c undo.h \ + alertpanel.c alertpanel.h \ + inputdialog.c inputdialog.h \ + progressdialog.c progressdialog.h \ + subscribedialog.c subscribedialog.h \ + about.c about.h \ + setup.c setup.h \ + gtkutils.c gtkutils.h \ + send_message.c send_message.h \ + inc.c inc.h \ + rpop3.c rpop3.h \ + import.c import.h \ + export.c export.h \ + rfc2015.c rfc2015.h \ + passphrase.c passphrase.h \ + select-keys.c select-keys.h \ + sigstatus.c sigstatus.h \ + simple-gettext.c \ + manual.c manual.h \ + eggtrayicon.c eggtrayicon.h \ + trayicon.c trayicon.h \ + printing.c printing.h \ + sslmanager.c sslmanager.h \ + plugin_manager.c plugin_manager.h \ + update_check.c update_check.h \ + quote_fmt_lex.l quote_fmt_lex.h \ + quote_fmt_parse.y quote_fmt.h \ + sylpheed-marshal.c sylpheed-marshal.h \ + notificationwindow.c notificationwindow.h + +BUILT_SOURCES = \ + quote_fmt_lex.c \ + quote_fmt_parse.c \ + quote_fmt_parse.h \ + sylpheed-marshal.c \ + sylpheed-marshal.h \ + plugin-marshal.c \ + plugin-marshal.h + +lib_LTLIBRARIES = libsylpheed-plugin-0.la + +libsylpheed_plugin_0_la_SOURCES = \ + plugin-marshal.c plugin-marshal.h \ + plugin.c + +libsylpheed_plugin_0includedir=$(includedir)/sylpheed +libsylpheed_plugin_0include_HEADERS = \ + plugin.h \ + plugin-types.h + +if NATIVE_WIN32 +no_undefined = -no-undefined +export_symbols = -export-symbols libsylpheed-plugin-0.def +endif + +libsylpheed_plugin_0_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -export-dynamic $(no_undefined) $(export_symbols) + +libsylpheed_plugin_0_la_LIBADD = \ + $(GTK_LIBS) \ + $(GLIB_LIBS) \ + ../libsylph/libsylph-0.la + +EXTRA_DIST = \ + quote_fmt_parse.h \ + sylpheed-marshal.list \ + plugin-marshal.list \ + version.h.in \ + sylpheed.rc.in \ + sylpheed.exe.manifest \ + libsylpheed-plugin-0.def + +INCLUDES = \ + -DG_LOG_DOMAIN=\"Sylpheed\" \ + -I$(top_srcdir)/libsylph \ + $(GTK_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + $(GPGME_CFLAGS) + +if NATIVE_WIN32 +SYLPHEED_RES = sylpheed.res +sylpheed.res: sylpheed.rc icons/sylpheed.ico sylpheed.exe.manifest + $(WINDRES) $< -O coff -o $@ +else +SYLPHEED_RES = +endif + +sylpheed_LDADD = \ + $(INTLLIBS) \ + $(GTK_LIBS) \ + $(GLIB_LIBS) \ + $(GPGME_LIBS) \ + $(LDAP_LIBS) \ + $(LIBICONV) \ + $(SYLPHEED_LIBS) \ + libsylpheed-plugin-0.la \ + ../libsylph/libsylph-0.la \ + $(SYLPHEED_RES) + +AM_CPPFLAGS = \ + -DLOCALEDIR=\""$(localedir)"\" \ + -DMANUALDIR=\""$(manualdir)"\" \ + -DFAQDIR=\""$(faqdir)"\" \ + -DPLUGINDIR=\""$(plugindir)"\" \ + -DTHEMEDIR=\""$(themedir)"\" \ + -DTARGET_ALIAS=\""$(target_triplet)"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" + +AM_YFLAGS = -d + +sylpheed-marshal.h: sylpheed-marshal.list + $(GLIB_GENMARSHAL) $< --header --prefix=sylpheed_marshal > $@ + +sylpheed-marshal.c: sylpheed-marshal.list + $(GLIB_GENMARSHAL) $< --body --prefix=sylpheed_marshal > $@ + +plugin-marshal.h: plugin-marshal.list + $(GLIB_GENMARSHAL) $< --header --prefix=syl_plugin_marshal > $@ + +plugin-marshal.c: plugin-marshal.list + $(GLIB_GENMARSHAL) $< --body --prefix=syl_plugin_marshal > $@ diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..4708707 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,1256 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +bin_PROGRAMS = sylpheed$(EXEEXT) +subdir = src +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/mkinstalldirs $(srcdir)/version.h.in \ + $(srcdir)/sylpheed.rc.in quote_fmt_lex.c quote_fmt_parse.h \ + quote_fmt_parse.c $(top_srcdir)/depcomp $(top_srcdir)/ylwrap \ + $(libsylpheed_plugin_0include_HEADERS) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/ac/aclocal-include.m4 \ + $(top_srcdir)/ac/gnupg-check-typedef.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = version.h sylpheed.rc +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ + "$(DESTDIR)$(libsylpheed_plugin_0includedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libsylpheed_plugin_0_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) ../libsylph/libsylph-0.la +am_libsylpheed_plugin_0_la_OBJECTS = plugin-marshal.lo plugin.lo +libsylpheed_plugin_0_la_OBJECTS = \ + $(am_libsylpheed_plugin_0_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libsylpheed_plugin_0_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libsylpheed_plugin_0_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +PROGRAMS = $(bin_PROGRAMS) +am_sylpheed_OBJECTS = main.$(OBJEXT) mainwindow.$(OBJEXT) \ + folderview.$(OBJEXT) summaryview.$(OBJEXT) \ + messageview.$(OBJEXT) headerview.$(OBJEXT) textview.$(OBJEXT) \ + imageview.$(OBJEXT) mimeview.$(OBJEXT) quick_search.$(OBJEXT) \ + query_search.$(OBJEXT) message_search.$(OBJEXT) \ + colorlabel.$(OBJEXT) action.$(OBJEXT) compose.$(OBJEXT) \ + gtkshruler.$(OBJEXT) menu.$(OBJEXT) stock_pixmap.$(OBJEXT) \ + prefs_ui.$(OBJEXT) prefs_common_dialog.$(OBJEXT) \ + prefs_filter.$(OBJEXT) prefs_filter_edit.$(OBJEXT) \ + prefs_account_dialog.$(OBJEXT) prefs_folder_item.$(OBJEXT) \ + prefs_display_items.$(OBJEXT) prefs_display_header.$(OBJEXT) \ + prefs_customheader.$(OBJEXT) prefs_summary_column.$(OBJEXT) \ + prefs_template.$(OBJEXT) prefs_actions.$(OBJEXT) \ + prefs_search_folder.$(OBJEXT) prefs_toolbar.$(OBJEXT) \ + account_dialog.$(OBJEXT) template.$(OBJEXT) \ + addressbook.$(OBJEXT) addr_compl.$(OBJEXT) addritem.$(OBJEXT) \ + addrcache.$(OBJEXT) addrbook.$(OBJEXT) addrindex.$(OBJEXT) \ + mgutils.$(OBJEXT) vcard.$(OBJEXT) ldif.$(OBJEXT) \ + importldif.$(OBJEXT) importcsv.$(OBJEXT) exportcsv.$(OBJEXT) \ + jpilot.$(OBJEXT) syldap.$(OBJEXT) editbook.$(OBJEXT) \ + editgroup.$(OBJEXT) editaddress.$(OBJEXT) editvcard.$(OBJEXT) \ + editjpilot.$(OBJEXT) editldap.$(OBJEXT) \ + editldap_basedn.$(OBJEXT) addressadd.$(OBJEXT) \ + filesel.$(OBJEXT) foldersel.$(OBJEXT) statusbar.$(OBJEXT) \ + logwindow.$(OBJEXT) sourcewindow.$(OBJEXT) \ + manage_window.$(OBJEXT) undo.$(OBJEXT) alertpanel.$(OBJEXT) \ + inputdialog.$(OBJEXT) progressdialog.$(OBJEXT) \ + subscribedialog.$(OBJEXT) about.$(OBJEXT) setup.$(OBJEXT) \ + gtkutils.$(OBJEXT) send_message.$(OBJEXT) inc.$(OBJEXT) \ + rpop3.$(OBJEXT) import.$(OBJEXT) export.$(OBJEXT) \ + rfc2015.$(OBJEXT) passphrase.$(OBJEXT) select-keys.$(OBJEXT) \ + sigstatus.$(OBJEXT) simple-gettext.$(OBJEXT) manual.$(OBJEXT) \ + eggtrayicon.$(OBJEXT) trayicon.$(OBJEXT) printing.$(OBJEXT) \ + sslmanager.$(OBJEXT) plugin_manager.$(OBJEXT) \ + update_check.$(OBJEXT) quote_fmt_lex.$(OBJEXT) \ + quote_fmt_parse.$(OBJEXT) sylpheed-marshal.$(OBJEXT) \ + notificationwindow.$(OBJEXT) +sylpheed_OBJECTS = $(am_sylpheed_OBJECTS) +@NATIVE_WIN32_TRUE@am__DEPENDENCIES_2 = sylpheed.res +sylpheed_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) libsylpheed-plugin-0.la \ + ../libsylph/libsylph-0.la $(am__DEPENDENCIES_2) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +@MAINTAINER_MODE_FALSE@am__skiplex = test -f $@ || +LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS) +LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS) +AM_V_LEX = $(am__v_LEX_@AM_V@) +am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@) +am__v_LEX_0 = @echo " LEX " $@; +am__v_LEX_1 = +YLWRAP = $(top_srcdir)/ylwrap +@MAINTAINER_MODE_FALSE@am__skipyacc = test -f $@ || +am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \ + -e s/c++$$/h++/ -e s/c$$/h/ +YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS) +LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS) +AM_V_YACC = $(am__v_YACC_@AM_V@) +am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@) +am__v_YACC_0 = @echo " YACC " $@; +am__v_YACC_1 = +SOURCES = $(libsylpheed_plugin_0_la_SOURCES) $(sylpheed_SOURCES) +DIST_SOURCES = $(libsylpheed_plugin_0_la_SOURCES) $(sylpheed_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(libsylpheed_plugin_0include_HEADERS) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_REVISION = @BUILD_REVISION@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEVEL_VERSION = @DEVEL_VERSION@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_VERSION = @EXTRA_VERSION@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GOBJECT_QUERY = @GOBJECT_QUERY@ +GPGME_CFLAGS = @GPGME_CFLAGS@ +GPGME_CONFIG = @GPGME_CONFIG@ +GPGME_LIBS = @GPGME_LIBS@ +GREP = @GREP@ +GTK_CFLAGS = @GTK_CFLAGS@ +GTK_LIBS = @GTK_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +LD = @LD@ +LDAP_LIBS = @LDAP_LIBS@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBICONV = @LIBICONV@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSYLPH_CFLAGS = @LIBSYLPH_CFLAGS@ +LIBSYLPH_LIBS = @LIBSYLPH_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_RELEASE = @LT_RELEASE@ +LT_REVISION = @LT_REVISION@ +MAINT = @MAINT@ +MAJOR_VERSION = @MAJOR_VERSION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MICRO_VERSION = @MICRO_VERSION@ +MINOR_VERSION = @MINOR_VERSION@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POFILES = @POFILES@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYLPHEED_LIBS = @SYLPHEED_LIBS@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +XGETTEXT = @XGETTEXT@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +faqdir = @faqdir@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +manualdir = @manualdir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +plugindir = @plugindir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +themedir = @themedir@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = icons +sylpheed_SOURCES = \ + version.h \ + main.c main.h \ + mainwindow.c mainwindow.h \ + folderview.c folderview.h \ + summaryview.c summaryview.h \ + messageview.c messageview.h \ + headerview.c headerview.h \ + textview.c textview.h \ + imageview.c imageview.h \ + mimeview.c mimeview.h \ + quick_search.c quick_search.h \ + query_search.c query_search.h \ + message_search.c message_search.h \ + colorlabel.c colorlabel.h \ + action.c action.h \ + compose.c compose.h \ + gtkshruler.c gtkshruler.h \ + menu.c menu.h \ + stock_pixmap.c stock_pixmap.h \ + prefs_ui.c prefs_ui.h \ + prefs_common_dialog.c prefs_common_dialog.h \ + prefs_filter.c prefs_filter.h \ + prefs_filter_edit.c prefs_filter_edit.h \ + prefs_account_dialog.c prefs_account_dialog.h \ + prefs_folder_item.c prefs_folder_item.h \ + prefs_display_items.c prefs_display_items.h \ + prefs_display_header.c prefs_display_header.h \ + prefs_customheader.c prefs_customheader.h \ + prefs_summary_column.c prefs_summary_column.h \ + prefs_template.c prefs_template.h \ + prefs_actions.c prefs_actions.h \ + prefs_search_folder.c prefs_search_folder.h \ + prefs_toolbar.c prefs_toolbar.h \ + account_dialog.c account_dialog.h \ + template.c template.h \ + addressbook.c addressbook.h \ + addr_compl.c addr_compl.h \ + addressitem.h \ + addritem.c addritem.h \ + addrcache.c addrcache.h \ + addrbook.c addrbook.h \ + addrindex.c addrindex.h \ + mgutils.c mgutils.h \ + vcard.c vcard.h \ + ldif.c ldif.h \ + importldif.c importldif.h \ + importcsv.c importcsv.h \ + exportcsv.c exportcsv.h \ + jpilot.c jpilot.h \ + syldap.c syldap.h \ + editbook.c editbook.h \ + editgroup.c editgroup.h \ + editaddress.c editaddress.h \ + editvcard.c editvcard.h \ + editjpilot.c editjpilot.h \ + editldap.c editldap.h \ + editldap_basedn.c editldap_basedn.h \ + addressadd.c addressadd.h \ + filesel.c filesel.h \ + foldersel.c foldersel.h \ + statusbar.c statusbar.h \ + logwindow.c logwindow.h \ + sourcewindow.c sourcewindow.h \ + manage_window.c manage_window.h \ + undo.c undo.h \ + alertpanel.c alertpanel.h \ + inputdialog.c inputdialog.h \ + progressdialog.c progressdialog.h \ + subscribedialog.c subscribedialog.h \ + about.c about.h \ + setup.c setup.h \ + gtkutils.c gtkutils.h \ + send_message.c send_message.h \ + inc.c inc.h \ + rpop3.c rpop3.h \ + import.c import.h \ + export.c export.h \ + rfc2015.c rfc2015.h \ + passphrase.c passphrase.h \ + select-keys.c select-keys.h \ + sigstatus.c sigstatus.h \ + simple-gettext.c \ + manual.c manual.h \ + eggtrayicon.c eggtrayicon.h \ + trayicon.c trayicon.h \ + printing.c printing.h \ + sslmanager.c sslmanager.h \ + plugin_manager.c plugin_manager.h \ + update_check.c update_check.h \ + quote_fmt_lex.l quote_fmt_lex.h \ + quote_fmt_parse.y quote_fmt.h \ + sylpheed-marshal.c sylpheed-marshal.h \ + notificationwindow.c notificationwindow.h + +BUILT_SOURCES = \ + quote_fmt_lex.c \ + quote_fmt_parse.c \ + quote_fmt_parse.h \ + sylpheed-marshal.c \ + sylpheed-marshal.h \ + plugin-marshal.c \ + plugin-marshal.h + +lib_LTLIBRARIES = libsylpheed-plugin-0.la +libsylpheed_plugin_0_la_SOURCES = \ + plugin-marshal.c plugin-marshal.h \ + plugin.c + +libsylpheed_plugin_0includedir = $(includedir)/sylpheed +libsylpheed_plugin_0include_HEADERS = \ + plugin.h \ + plugin-types.h + +@NATIVE_WIN32_TRUE@no_undefined = -no-undefined +@NATIVE_WIN32_TRUE@export_symbols = -export-symbols libsylpheed-plugin-0.def +libsylpheed_plugin_0_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -export-dynamic $(no_undefined) $(export_symbols) + +libsylpheed_plugin_0_la_LIBADD = \ + $(GTK_LIBS) \ + $(GLIB_LIBS) \ + ../libsylph/libsylph-0.la + +EXTRA_DIST = \ + quote_fmt_parse.h \ + sylpheed-marshal.list \ + plugin-marshal.list \ + version.h.in \ + sylpheed.rc.in \ + sylpheed.exe.manifest \ + libsylpheed-plugin-0.def + +INCLUDES = \ + -DG_LOG_DOMAIN=\"Sylpheed\" \ + -I$(top_srcdir)/libsylph \ + $(GTK_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + $(GPGME_CFLAGS) + +@NATIVE_WIN32_FALSE@SYLPHEED_RES = +@NATIVE_WIN32_TRUE@SYLPHEED_RES = sylpheed.res +sylpheed_LDADD = \ + $(INTLLIBS) \ + $(GTK_LIBS) \ + $(GLIB_LIBS) \ + $(GPGME_LIBS) \ + $(LDAP_LIBS) \ + $(LIBICONV) \ + $(SYLPHEED_LIBS) \ + libsylpheed-plugin-0.la \ + ../libsylph/libsylph-0.la \ + $(SYLPHEED_RES) + +AM_CPPFLAGS = \ + -DLOCALEDIR=\""$(localedir)"\" \ + -DMANUALDIR=\""$(manualdir)"\" \ + -DFAQDIR=\""$(faqdir)"\" \ + -DPLUGINDIR=\""$(plugindir)"\" \ + -DTHEMEDIR=\""$(themedir)"\" \ + -DTARGET_ALIAS=\""$(target_triplet)"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" + +AM_YFLAGS = -d +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +.SUFFIXES: .c .l .lo .o .obj .y +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +version.h: $(top_builddir)/config.status $(srcdir)/version.h.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +sylpheed.rc: $(top_builddir)/config.status $(srcdir)/sylpheed.rc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libsylpheed-plugin-0.la: $(libsylpheed_plugin_0_la_OBJECTS) $(libsylpheed_plugin_0_la_DEPENDENCIES) $(EXTRA_libsylpheed_plugin_0_la_DEPENDENCIES) + $(AM_V_CCLD)$(libsylpheed_plugin_0_la_LINK) -rpath $(libdir) $(libsylpheed_plugin_0_la_OBJECTS) $(libsylpheed_plugin_0_la_LIBADD) $(LIBS) +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +quote_fmt_parse.h: quote_fmt_parse.c + @if test ! -f $@; then rm -f quote_fmt_parse.c; else :; fi + @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) quote_fmt_parse.c; else :; fi + +sylpheed$(EXEEXT): $(sylpheed_OBJECTS) $(sylpheed_DEPENDENCIES) $(EXTRA_sylpheed_DEPENDENCIES) + @rm -f sylpheed$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(sylpheed_OBJECTS) $(sylpheed_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/about.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/account_dialog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/action.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addr_compl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addrbook.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addrcache.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addressadd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addressbook.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addrindex.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addritem.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alertpanel.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/colorlabel.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compose.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/editaddress.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/editbook.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/editgroup.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/editjpilot.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/editldap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/editldap_basedn.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/editvcard.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eggtrayicon.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/export.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exportcsv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesel.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/foldersel.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/folderview.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtkshruler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtkutils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/headerview.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imageview.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/import.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/importcsv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/importldif.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inputdialog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpilot.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldif.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logwindow.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mainwindow.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/manage_window.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/manual.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/menu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message_search.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/messageview.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mgutils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mimeview.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notificationwindow.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passphrase.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin-marshal.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin_manager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_account_dialog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_actions.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_common_dialog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_customheader.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_display_header.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_display_items.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_filter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_filter_edit.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_folder_item.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_search_folder.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_summary_column.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_template.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_toolbar.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_ui.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/printing.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/progressdialog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/query_search.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quick_search.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quote_fmt_lex.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quote_fmt_parse.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rfc2015.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpop3.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/select-keys.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/send_message.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/setup.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sigstatus.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simple-gettext.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sourcewindow.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sslmanager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/statusbar.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stock_pixmap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subscribedialog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/summaryview.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/syldap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sylpheed-marshal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/template.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/textview.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trayicon.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/undo.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/update_check.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vcard.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +.l.c: + $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE) + +.y.c: + $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-libsylpheed_plugin_0includeHEADERS: $(libsylpheed_plugin_0include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(libsylpheed_plugin_0include_HEADERS)'; test -n "$(libsylpheed_plugin_0includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libsylpheed_plugin_0includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libsylpheed_plugin_0includedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libsylpheed_plugin_0includedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(libsylpheed_plugin_0includedir)" || exit $$?; \ + done + +uninstall-libsylpheed_plugin_0includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(libsylpheed_plugin_0include_HEADERS)'; test -n "$(libsylpheed_plugin_0includedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(libsylpheed_plugin_0includedir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-recursive +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) +install-binPROGRAMS: install-libLTLIBRARIES + +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libsylpheed_plugin_0includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -rm -f quote_fmt_lex.c + -rm -f quote_fmt_parse.c + -rm -f quote_fmt_parse.h + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-recursive + +clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-libsylpheed_plugin_0includeHEADERS + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-libLTLIBRARIES + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ + uninstall-libsylpheed_plugin_0includeHEADERS + +.MAKE: $(am__recursive_targets) all check install install-am \ + install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-binPROGRAMS clean-generic \ + clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-libLTLIBRARIES \ + install-libsylpheed_plugin_0includeHEADERS install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ + uninstall-libsylpheed_plugin_0includeHEADERS + +@NATIVE_WIN32_TRUE@sylpheed.res: sylpheed.rc icons/sylpheed.ico sylpheed.exe.manifest +@NATIVE_WIN32_TRUE@ $(WINDRES) $< -O coff -o $@ + +sylpheed-marshal.h: sylpheed-marshal.list + $(GLIB_GENMARSHAL) $< --header --prefix=sylpheed_marshal > $@ + +sylpheed-marshal.c: sylpheed-marshal.list + $(GLIB_GENMARSHAL) $< --body --prefix=sylpheed_marshal > $@ + +plugin-marshal.h: plugin-marshal.list + $(GLIB_GENMARSHAL) $< --header --prefix=syl_plugin_marshal > $@ + +plugin-marshal.c: plugin-marshal.list + $(GLIB_GENMARSHAL) $< --body --prefix=syl_plugin_marshal > $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/about.c b/src/about.c new file mode 100644 index 0000000..cfbbc2f --- /dev/null +++ b/src/about.c @@ -0,0 +1,274 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2018 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_SYS_UTSNAME_H +# include +#endif + +#include "about.h" +#include "gtkutils.h" +#include "stock_pixmap.h" +#include "prefs_common.h" +#include "utils.h" +#include "version.h" + +static GtkWidget *window; + +static void about_create(void); +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event); +static void about_uri_clicked(GtkButton *button, gpointer data); + +void about_show(void) +{ + if (!window) + about_create(); + else + gtk_window_present(GTK_WINDOW(window)); +} + +static void about_create(void) +{ + GtkWidget *vbox; + GtkWidget *pixmap; + GtkWidget *label; + GtkWidget *hbox; + GtkWidget *button; + GtkWidget *scrolledwin; + GtkWidget *text; + GtkWidget *confirm_area; + GtkWidget *ok_button; + GtkTextBuffer *buffer; + GtkTextIter iter; + GtkStyle *style; + GdkColormap *cmap; + GdkColor uri_color[2] = {{0, 0, 0, 0xffff}, {0, 0xffff, 0, 0}}; + gboolean success[2]; + +#if HAVE_SYS_UTSNAME_H + struct utsname utsbuf; +#endif + gchar buf[1024]; + gint i; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), _("About")); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + gtk_widget_set_size_request(window, 518 * gtkut_get_dpi_multiplier(), 358 * gtkut_get_dpi_multiplier()); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(gtk_widget_hide_on_delete), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(key_pressed), NULL); + gtk_widget_realize(window); + + vbox = gtk_vbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(window), vbox); + + pixmap = stock_pixbuf_widget(window, STOCK_PIXMAP_SYLPHEED_LOGO); + gtk_box_pack_start(GTK_BOX(vbox), pixmap, FALSE, FALSE, 0); + + label = gtk_label_new("Version " VERSION + " (Build " Xstr(BUILD_REVISION) ")"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + +#if HAVE_SYS_UTSNAME_H + uname(&utsbuf); + g_snprintf(buf, sizeof(buf), + "GTK+ %d.%d.%d / GLib %d.%d.%d\n" + "Operating System: %s %s (%s)", + gtk_major_version, gtk_minor_version, gtk_micro_version, + glib_major_version, glib_minor_version, glib_micro_version, + utsbuf.sysname, utsbuf.release, utsbuf.machine); +#elif defined(G_OS_WIN32) + g_snprintf(buf, sizeof(buf), + "GTK+ %d.%d.%d / GLib %d.%d.%d\n" + "Operating System: %s", + gtk_major_version, gtk_minor_version, gtk_micro_version, + glib_major_version, glib_minor_version, glib_micro_version, + "Win32"); +#else + g_snprintf(buf, sizeof(buf), + "GTK+ %d.%d.%d / GLib %d.%d.%d\n" + "Operating System: unknown", + gtk_major_version, gtk_minor_version, gtk_micro_version, + glib_major_version, glib_minor_version, glib_micro_version); +#endif + + label = gtk_label_new(buf); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + + g_snprintf(buf, sizeof(buf), + "Compiled-in features:%s", +#if USE_THREADS + " gthread" +#endif +#if INET6 + " IPv6" +#endif +#if HAVE_ICONV + " iconv" +#endif +#if HAVE_LIBCOMPFACE + " compface" +#endif +#if USE_GPGME + " GnuPG" +#endif +#if USE_SSL + " OpenSSL" +#endif +#if USE_LDAP + " LDAP" +#endif +#if USE_JPILOT + " JPilot" +#endif +#if USE_GTKSPELL + " GtkSpell" +#endif +#if USE_ONIGURUMA + " Oniguruma" +#endif + ""); + + label = gtk_label_new(buf); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + + label = gtk_label_new + ("Copyright (C) 1999-2018 Hiroyuki Yamamoto "); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + button = gtk_button_new_with_label(" "HOMEPAGE_URI" "); + gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0); + gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(about_uri_clicked), NULL); + buf[0] = ' '; + for (i = 1; i <= strlen(HOMEPAGE_URI); i++) buf[i] = '_'; + strcpy(buf + i, " "); + gtk_label_set_pattern(GTK_LABEL(GTK_BIN(button)->child), buf); + cmap = gdk_window_get_colormap(window->window); + gdk_colormap_alloc_colors(cmap, uri_color, 2, FALSE, TRUE, success); + if (success[0] == TRUE && success[1] == TRUE) { + gtk_widget_ensure_style(GTK_BIN(button)->child); + style = gtk_style_copy + (gtk_widget_get_style(GTK_BIN(button)->child)); + style->fg[GTK_STATE_NORMAL] = uri_color[0]; + style->fg[GTK_STATE_ACTIVE] = uri_color[1]; + style->fg[GTK_STATE_PRELIGHT] = uri_color[0]; + gtk_widget_set_style(GTK_BIN(button)->child, style); + } else + g_warning("about_create(): color allocation failed.\n"); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0); + + text = gtk_text_view_new(); + gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6); + gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6); + gtk_container_add(GTK_CONTAINER(scrolledwin), text); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); + gtk_text_buffer_get_iter_at_offset(buffer, &iter, 0); + +#if USE_GPGME + gtk_text_buffer_insert(buffer, &iter, + _("GPGME is copyright 2001 by Werner Koch \n\n"), -1); +#endif /* USE_GPGME */ + + gtk_text_buffer_insert(buffer, &iter, + _("This program is free software; you can redistribute it and/or modify " + "it under the terms of the GNU General Public License as published by " + "the Free Software Foundation; either version 2, or (at your option) " + "any later version.\n\n"), -1); + + gtk_text_buffer_insert(buffer, &iter, + _("This program is distributed in the hope that it will be useful, " + "but WITHOUT ANY WARRANTY; without even the implied warranty of " + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. " + "See the GNU General Public License for more details.\n\n"), -1); + + gtk_text_buffer_insert(buffer, &iter, + _("You should have received a copy of the GNU General Public License " + "along with this program; if not, write to the Free Software " + "Foundation, Inc., 59 Temple Place - Suite 330, Boston, " + "MA 02111-1307, USA."), -1); + + gtk_text_buffer_get_start_iter(buffer, &iter); + gtk_text_buffer_place_cursor(buffer, &iter); + + gtkut_stock_button_set_create(&confirm_area, &ok_button, GTK_STOCK_OK, + NULL, NULL, NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_button); + gtk_widget_grab_focus(ok_button); + g_signal_connect_closure + (G_OBJECT(ok_button), "clicked", + g_cclosure_new_swap(G_CALLBACK(gtk_widget_hide_on_delete), + window, NULL), FALSE); + + gtk_widget_show_all(window); +} + +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event) +{ + if (event && event->keyval == GDK_Escape) + gtk_widget_hide(window); + return FALSE; +} + +static void about_uri_clicked(GtkButton *button, gpointer data) +{ + open_uri(HOMEPAGE_URI, prefs_common.uri_cmd); +} diff --git a/src/about.h b/src/about.h new file mode 100644 index 0000000..0923654 --- /dev/null +++ b/src/about.h @@ -0,0 +1,25 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2007 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __ABOUT_H__ +#define __ABOUT_H__ + +void about_show(void); + +#endif /* __ABOUT_H__ */ diff --git a/src/account_dialog.c b/src/account_dialog.c new file mode 100644 index 0000000..89f3054 --- /dev/null +++ b/src/account_dialog.c @@ -0,0 +1,847 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "mainwindow.h" +#include "folderview.h" +#include "folder.h" +#include "account.h" +#include "prefs.h" +#include "prefs_account.h" +#include "prefs_account_dialog.h" +#include "procmsg.h" +#include "procheader.h" +#include "compose.h" +#include "manage_window.h" +#include "stock_pixmap.h" +#include "statusbar.h" +#include "inc.h" +#include "gtkutils.h" +#include "utils.h" +#include "alertpanel.h" + +enum +{ + COL_DEFAULT, + COL_GETALL, + COL_NAME, + COL_PROTOCOL, + COL_SERVER, + COL_ACCOUNT, + COL_CAN_GETALL, + N_COLS +}; + +static struct EditAccount { + GtkWidget *window; + + GtkWidget *treeview; + GtkListStore *store; + GtkTreeSelection *selection; + + GtkWidget *close_btn; +} edit_account; + +static void account_edit_create (void); + +static GtkWidget *account_wait_window_create (const gchar *str); + +static void account_edit_prefs (void); +static void account_delete (void); + +static void account_up (void); +static void account_down (void); + +static void account_set_default (void); + +static void account_edit_close (void); + +static gboolean account_selected (GtkTreeSelection *selection, + GtkTreeModel *model, + GtkTreePath *path, + gboolean cur_selected, + gpointer data); + +static void account_default_toggled (GtkCellRenderer *cell, + gchar *path, + gpointer data); +static void account_getall_toggled (GtkCellRenderer *cell, + gchar *path, + gpointer data); + +static void account_row_activated (GtkTreeView *treeview, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data); +static void account_row_reordered (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data); + +static gint account_delete_event (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean account_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); + +static void account_set_row (PrefsAccount *ac_prefs, + GtkTreeIter *iter, + GtkTreeIter *new, + gboolean move_view); +static void account_set_dialog (void); +static void account_update_dialog (void); + +static void account_set_list (void); + + +void account_set_menu(void) +{ + main_window_set_account_menu(account_get_list()); +} + +void account_edit_open(void) +{ + inc_lock(); + + if (compose_get_compose_list()) { + alertpanel_notice(_("Some composing windows are open.\n" + "Please close all the composing windows before editing the accounts.")); + inc_unlock(); + return; + } + + debug_print(_("Opening account edit window...\n")); + + if (!edit_account.window) + account_edit_create(); + + account_set_dialog(); + + manage_window_set_transient(GTK_WINDOW(edit_account.window)); + gtk_widget_grab_focus(edit_account.close_btn); + gtk_widget_show(edit_account.window); + + manage_window_focus_in(edit_account.window, NULL, NULL); +} + +void account_add(void) +{ + PrefsAccount *ac_prefs; + + ac_prefs = prefs_account_open(NULL); + gtk_window_present(GTK_WINDOW(edit_account.window)); + + if (!ac_prefs) return; + + account_append(ac_prefs); + + if (ac_prefs->is_default) + account_set_as_default(ac_prefs); + + account_set_row(ac_prefs, NULL, NULL, TRUE); + + if (ac_prefs->protocol == A_IMAP4 || ac_prefs->protocol == A_NNTP) { + Folder *folder; + + if (ac_prefs->protocol == A_IMAP4) { + folder = folder_new(F_IMAP, ac_prefs->account_name, + ac_prefs->recv_server); + } else { + folder = folder_new(F_NEWS, ac_prefs->account_name, + ac_prefs->nntp_server); + } + + folder->account = ac_prefs; + ac_prefs->folder = REMOTE_FOLDER(folder); + folder_add(folder); + if (ac_prefs->protocol == A_IMAP4) { + if (main_window_toggle_online_if_offline + (main_window_get())) { + GtkWidget *window; + window = account_wait_window_create(_("Creating folder tree. Please wait...")); + if (folder->klass->create_tree(folder) < 0) + alertpanel_error(_("Creation of the folder tree failed.")); + statusbar_pop_all(); + gtk_widget_destroy(window); + } + } + folderview_set_all(); + } +} + +void account_open(PrefsAccount *ac_prefs) +{ + gboolean prev_default; + gchar *prev_name; + + g_return_if_fail(ac_prefs != NULL); + + prev_default = ac_prefs->is_default; + prev_name = g_strdup(ac_prefs->account_name ? ac_prefs->account_name : ""); + + prefs_account_open(ac_prefs); + if (edit_account.window && GTK_WIDGET_VISIBLE(edit_account.window)) + gtk_window_present(GTK_WINDOW(edit_account.window)); + else + main_window_popup(main_window_get()); + + if (!prev_default && ac_prefs->is_default) + account_set_as_default(ac_prefs); + + if (ac_prefs->folder && + strcmp2(prev_name, ac_prefs->account_name) != 0) { + folder_set_name(FOLDER(ac_prefs->folder), + ac_prefs->account_name); + folderview_set_all(); + folder_write_list(); + } + + g_free(prev_name); + + account_write_config_all(); + account_set_menu(); + main_window_reflect_prefs_all(); + account_updated(); +} + +void account_set_missing_folder(void) +{ + PrefsAccount *ap; + GList *cur; + + for (cur = account_get_list(); cur != NULL; cur = cur->next) { + ap = (PrefsAccount *)cur->data; + if ((ap->protocol == A_IMAP4 || ap->protocol == A_NNTP) && + !ap->folder) { + Folder *folder; + + if (ap->protocol == A_IMAP4) { + folder = folder_new(F_IMAP, ap->account_name, + ap->recv_server); + } else { + folder = folder_new(F_NEWS, ap->account_name, + ap->nntp_server); + } + + folder->account = ap; + ap->folder = REMOTE_FOLDER(folder); + folder_add(folder); + if (ap->protocol == A_IMAP4) { + if (main_window_toggle_online_if_offline + (main_window_get())) { + folder->klass->create_tree(folder); + statusbar_pop_all(); + } + } + } + } +} + +static void account_edit_create(void) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *label; + GtkWidget *hbox; + GtkWidget *scrolledwin; + GtkWidget *treeview; + GtkListStore *store; + GtkTreeSelection *selection; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + GtkWidget *vbox2; + GtkWidget *add_btn; + GtkWidget *edit_btn; + GtkWidget *del_btn; + GtkWidget *up_btn; + GtkWidget *down_btn; + GtkWidget *image; + + GtkWidget *default_btn; + + GtkWidget *hbbox; + GtkWidget *close_btn; + + debug_print(_("Creating account edit window...\n")); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request (window, 500 * gtkut_get_dpi_multiplier(), 320 * gtkut_get_dpi_multiplier()); + gtk_container_set_border_width (GTK_CONTAINER (window), 8); + gtk_window_set_title (GTK_WINDOW (window), _("Edit accounts")); + gtk_window_set_position (GTK_WINDOW (window), + GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal (GTK_WINDOW (window), TRUE); + g_signal_connect (G_OBJECT (window), "delete_event", + G_CALLBACK (account_delete_event), NULL); + g_signal_connect (G_OBJECT (window), "key_press_event", + G_CALLBACK (account_key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT (window); + gtk_widget_realize(window); + + vbox = gtk_vbox_new (FALSE, 10); + gtk_widget_show (vbox); + gtk_container_add (GTK_CONTAINER (window), vbox); + + hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new + (_("New messages will be checked in this order. Check the boxes\n" + "on the `G' column to enable message retrieval by `Get all'.")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 4); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 2); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(scrolledwin); + gtk_box_pack_start(GTK_BOX(hbox), scrolledwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + + store = gtk_list_store_new + (N_COLS, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN); + + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), COL_NAME); + gtk_tree_view_set_reorderable(GTK_TREE_VIEW(treeview), TRUE); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); + gtk_tree_selection_set_select_function(selection, account_selected, + NULL, NULL); + + renderer = gtk_cell_renderer_toggle_new(); + gtk_cell_renderer_toggle_set_radio(GTK_CELL_RENDERER_TOGGLE(renderer), + TRUE); + g_signal_connect(renderer, "toggled", + G_CALLBACK(account_default_toggled), NULL); + column = gtk_tree_view_column_new_with_attributes + ("D", renderer, "active", COL_DEFAULT, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + renderer = gtk_cell_renderer_toggle_new(); + g_signal_connect(renderer, "toggled", + G_CALLBACK(account_getall_toggled), NULL); + column = gtk_tree_view_column_new_with_attributes + ("G", renderer, "active", COL_GETALL, "visible", COL_CAN_GETALL, + NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes + (_("Name"), renderer, "text", COL_NAME, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes + (_("Protocol"), renderer, "text", COL_PROTOCOL, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes + (_("Server"), renderer, "text", COL_SERVER, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + gtk_widget_show(treeview); + gtk_container_add(GTK_CONTAINER(scrolledwin), treeview); + + g_signal_connect(G_OBJECT(treeview), "row-activated", + G_CALLBACK(account_row_activated), NULL); + g_signal_connect_after(G_OBJECT(store), "rows-reordered", + G_CALLBACK(account_row_reordered), NULL); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0); + + add_btn = gtk_button_new_from_stock (GTK_STOCK_ADD); + gtk_widget_show (add_btn); + gtk_box_pack_start (GTK_BOX (vbox2), add_btn, FALSE, FALSE, 4); + g_signal_connect (G_OBJECT(add_btn), "clicked", + G_CALLBACK (account_add), NULL); + +#ifdef GTK_STOCK_EDIT + edit_btn = gtk_button_new_from_stock (GTK_STOCK_EDIT); +#else + edit_btn = gtk_button_new_with_label (_("Edit")); +#endif + gtk_widget_show (edit_btn); + gtk_box_pack_start (GTK_BOX (vbox2), edit_btn, FALSE, FALSE, 4); + g_signal_connect (G_OBJECT(edit_btn), "clicked", + G_CALLBACK (account_edit_prefs), NULL); + + del_btn = gtk_button_new_from_stock (GTK_STOCK_DELETE); + gtk_widget_show (del_btn); + gtk_box_pack_start (GTK_BOX (vbox2), del_btn, FALSE, FALSE, 4); + g_signal_connect (G_OBJECT(del_btn), "clicked", + G_CALLBACK (account_delete), NULL); + + down_btn = gtk_button_new (); + image = gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_BUTTON); + gtk_widget_show (image); + gtk_button_set_image (GTK_BUTTON(down_btn), image); + gtk_widget_show (down_btn); + gtk_box_pack_end (GTK_BOX (vbox2), down_btn, FALSE, FALSE, 4); + g_signal_connect (G_OBJECT(down_btn), "clicked", + G_CALLBACK (account_down), NULL); + + up_btn = gtk_button_new (); + image = gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_BUTTON); + gtk_widget_show (image); + gtk_button_set_image (GTK_BUTTON(up_btn), image); + gtk_widget_show (up_btn); + gtk_box_pack_end (GTK_BOX (vbox2), up_btn, FALSE, FALSE, 4); + g_signal_connect (G_OBJECT(up_btn), "clicked", + G_CALLBACK (account_up), NULL); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + vbox2 = gtk_vbox_new(FALSE, 0); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0); + + default_btn = gtk_button_new_with_mnemonic + (_(" _Set as default account ")); + gtk_widget_show (default_btn); + gtk_box_pack_start (GTK_BOX (vbox2), default_btn, TRUE, FALSE, 0); + g_signal_connect (G_OBJECT(default_btn), "clicked", + G_CALLBACK (account_set_default), NULL); + + gtkut_stock_button_set_create(&hbbox, &close_btn, GTK_STOCK_CLOSE, + NULL, NULL, NULL, NULL); + gtk_widget_show(hbbox); + gtk_box_pack_end (GTK_BOX (hbox), hbbox, FALSE, FALSE, 0); + gtk_widget_grab_default (close_btn); + + g_signal_connect (G_OBJECT (close_btn), "clicked", + G_CALLBACK (account_edit_close), NULL); + + edit_account.window = window; + edit_account.treeview = treeview; + edit_account.store = store; + edit_account.selection = selection; + edit_account.close_btn = close_btn; +} + +static GtkWidget *account_wait_window_create(const gchar *str) +{ + GtkWidget *window; + GtkWidget *label; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request(window, 380 * gtkut_get_dpi_multiplier(), 60 * gtkut_get_dpi_multiplier()); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + gtk_window_set_position(GTK_WINDOW(window), + GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_title(GTK_WINDOW(window), str); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE); + manage_window_focus_in(edit_account.window, NULL, NULL); + manage_window_set_transient(GTK_WINDOW(window)); + g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(gtk_true), + NULL); + + label = gtk_label_new(str); + gtk_container_add(GTK_CONTAINER(window), label); + gtk_widget_show(label); + + gtk_widget_show(window); + + return window; +} + +static void account_edit_prefs(void) +{ + GtkTreeIter iter; + PrefsAccount *ac_prefs; + + if (!gtk_tree_selection_get_selected(edit_account.selection, + NULL, &iter)) + return; + + gtk_tree_model_get(GTK_TREE_MODEL(edit_account.store), &iter, + COL_ACCOUNT, &ac_prefs, -1); + account_open(ac_prefs); + + account_set_row(ac_prefs, &iter, NULL, FALSE); +} + +static void account_delete(void) +{ + GtkTreeIter iter; + PrefsAccount *ac_prefs; + gchar buf[BUFFSIZE]; + + if (!gtk_tree_selection_get_selected(edit_account.selection, + NULL, &iter)) + return; + + gtk_tree_model_get(GTK_TREE_MODEL(edit_account.store), &iter, + COL_ACCOUNT, &ac_prefs, -1); + g_return_if_fail(ac_prefs != NULL); + + g_snprintf(buf, sizeof(buf), + _("Do you really want to delete the account '%s'?"), + ac_prefs->account_name ? ac_prefs->account_name : + _("(Untitled)")); + if (alertpanel_full(_("Delete account"), buf, + ALERT_QUESTION, G_ALERTALTERNATE, FALSE, + GTK_STOCK_YES, GTK_STOCK_NO, NULL) + != G_ALERTDEFAULT) + return; + + if (ac_prefs->folder) { + FolderItem *item; + + item = main_window_get()->summaryview->folder_item; + if (item && item->folder == FOLDER(ac_prefs->folder)) + summary_clear_all(main_window_get()->summaryview); + folder_destroy(FOLDER(ac_prefs->folder)); + folderview_set_all(); + } + + account_destroy(ac_prefs); + gtk_list_store_remove(edit_account.store, &iter); + account_update_dialog(); +} + +static void account_up(void) +{ + GtkTreeModel *model = GTK_TREE_MODEL(edit_account.store); + GtkTreeIter iter, prev; + GtkTreePath *path; + + if (!gtk_tree_selection_get_selected(edit_account.selection, + NULL, &iter)) + return; + + path = gtk_tree_model_get_path(model, &iter); + if (gtk_tree_path_prev(path)) { + gtk_tree_model_get_iter(model, &prev, path); + gtk_list_store_swap(edit_account.store, &iter, &prev); + } + gtk_tree_path_free(path); +} + +static void account_down(void) +{ + GtkTreeIter iter, next; + + if (!gtk_tree_selection_get_selected(edit_account.selection, + NULL, &iter)) + return; + + next = iter; + if (gtk_tree_model_iter_next(GTK_TREE_MODEL(edit_account.store), + &next)) + gtk_list_store_swap(edit_account.store, &iter, &next); +} + +static void account_set_default(void) +{ + GtkTreeIter iter; + PrefsAccount *ac_prefs; + + if (!gtk_tree_selection_get_selected(edit_account.selection, + NULL, &iter)) + return; + + gtk_tree_model_get(GTK_TREE_MODEL(edit_account.store), &iter, + COL_ACCOUNT, &ac_prefs, -1); + g_return_if_fail(ac_prefs != NULL); + + account_set_as_default(ac_prefs); + account_update_dialog(); + + cur_account = ac_prefs; + account_set_menu(); + main_window_reflect_prefs_all(); +} + +static void account_edit_close(void) +{ + GList *account_list; + + account_update_lock(); + account_set_list(); + account_write_config_all(); + + account_list = account_get_list(); + + if (!cur_account && account_list) { + PrefsAccount *ac_prefs = (PrefsAccount *)account_list->data; + account_set_as_default(ac_prefs); + cur_account = ac_prefs; + } + + account_set_menu(); + main_window_reflect_prefs_all(); + account_update_unlock(); + account_updated(); + + gtk_widget_hide(edit_account.window); + main_window_popup(main_window_get()); + + inc_unlock(); +} + +static gint account_delete_event(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + account_edit_close(); + return TRUE; +} + +static gboolean account_selected(GtkTreeSelection *selection, + GtkTreeModel *model, GtkTreePath *path, + gboolean cur_selected, gpointer data) +{ + return TRUE; +} + +static void account_default_toggled(GtkCellRenderer *cell, gchar *path_str, + gpointer data) +{ + GtkTreeIter iter; + PrefsAccount *ac; + GtkTreePath *path; + + path = gtk_tree_path_new_from_string(path_str); + gtk_tree_model_get_iter(GTK_TREE_MODEL(edit_account.store), + &iter, path); + gtk_tree_path_free(path); + gtk_tree_model_get(GTK_TREE_MODEL(edit_account.store), &iter, + COL_ACCOUNT, &ac, -1); + + account_set_as_default(ac); + account_update_dialog(); +} + +static void account_getall_toggled(GtkCellRenderer *cell, gchar *path_str, + gpointer data) +{ + GtkTreeIter iter; + PrefsAccount *ac; + GtkTreePath *path; + gboolean can_getall; + + path = gtk_tree_path_new_from_string(path_str); + gtk_tree_model_get_iter(GTK_TREE_MODEL(edit_account.store), + &iter, path); + gtk_tree_path_free(path); + gtk_tree_model_get(GTK_TREE_MODEL(edit_account.store), &iter, + COL_ACCOUNT, &ac, COL_CAN_GETALL, &can_getall, -1); + + if (can_getall) { + ac->recv_at_getall ^= TRUE; + account_set_row(ac, &iter, NULL, FALSE); + } +} + +static void account_row_activated(GtkTreeView *treeview, GtkTreePath *path, + GtkTreeViewColumn *column, gpointer data) +{ + account_edit_prefs(); +} + +static void account_row_reordered (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GtkTreeIter iter_; + GtkTreePath *path_; + + if (!gtk_tree_selection_get_selected(edit_account.selection, + NULL, &iter_)) + return; + path_ = gtk_tree_model_get_path + (GTK_TREE_MODEL(edit_account.store), &iter_); + gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(edit_account.treeview), + path_, NULL, FALSE, 0.0, 0.0); + gtk_tree_path_free(path_); +} + +static gboolean account_key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + account_edit_close(); + return FALSE; +} + +static void account_set_row(PrefsAccount *ac_prefs, GtkTreeIter *iter, + GtkTreeIter *new, gboolean move_view) +{ + GtkListStore *store = edit_account.store; + GtkTreeIter iter_; + const gchar *protocol, *server; + gboolean has_getall; + + g_return_if_fail(ac_prefs != NULL); + +#if USE_SSL + protocol = ac_prefs->protocol == A_POP3 ? + (ac_prefs->ssl_pop == SSL_TUNNEL ? + "POP3 (SSL)" : + ac_prefs->ssl_pop == SSL_STARTTLS ? + "POP3 (TLS)" : "POP3") : + ac_prefs->protocol == A_IMAP4 ? + (ac_prefs->ssl_imap == SSL_TUNNEL ? + "IMAP4 (SSL)" : + ac_prefs->ssl_imap == SSL_STARTTLS ? + "IMAP4 (TLS)" : "IMAP4") : + ac_prefs->protocol == A_NNTP ? + (ac_prefs->ssl_nntp == SSL_TUNNEL ? + "NNTP (SSL)" : "NNTP") : + ""; +#else + protocol = ac_prefs->protocol == A_POP3 ? "POP3" : + ac_prefs->protocol == A_IMAP4 ? "IMAP4" : + ac_prefs->protocol == A_NNTP ? "NNTP" : ""; +#endif + server = ac_prefs->protocol == A_NNTP ? + ac_prefs->nntp_server : ac_prefs->recv_server; + + has_getall = (ac_prefs->protocol == A_POP3 || + ac_prefs->protocol == A_IMAP4 || + ac_prefs->protocol == A_NNTP); + + if (!iter) + gtk_list_store_append(store, &iter_); + else + iter_ = *iter; + + gtk_list_store_set(store, &iter_, + COL_DEFAULT, ac_prefs->is_default, + COL_GETALL, has_getall && ac_prefs->recv_at_getall, + COL_NAME, ac_prefs->account_name, + COL_PROTOCOL, protocol, + COL_SERVER, server, + COL_ACCOUNT, ac_prefs, + COL_CAN_GETALL, has_getall, + -1); + + if (new) + *new = iter_; + + if (move_view) { + GtkTreePath *path; + + path = gtk_tree_model_get_path + (GTK_TREE_MODEL(edit_account.store), &iter_); + gtk_tree_view_scroll_to_cell + (GTK_TREE_VIEW(edit_account.treeview), + path, NULL, TRUE, 0.5, 0.0); + gtk_tree_path_free(path); + } +} + +/* set dialog from account list */ +static void account_set_dialog(void) +{ + GList *cur; + GtkTreeIter iter; + GtkTreePath *path; + + gtk_list_store_clear(edit_account.store); + + for (cur = account_get_list(); cur != NULL; cur = cur->next) { + account_set_row((PrefsAccount *)cur->data, NULL, &iter, FALSE); + if ((PrefsAccount *)cur->data == cur_account) { + gtk_tree_selection_select_iter(edit_account.selection, + &iter); + } + } + + if (!gtk_tree_selection_get_selected(edit_account.selection, NULL, + &iter)) + return; + path = gtk_tree_model_get_path(GTK_TREE_MODEL(edit_account.store), + &iter); + gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(edit_account.treeview), + path, NULL, FALSE, 0.0, 0.0); + gtk_tree_path_free(path); +} + +/* update dialog to the latest state */ +static void account_update_dialog(void) +{ + GtkTreeIter iter; + GtkTreeModel *model = GTK_TREE_MODEL(edit_account.store); + PrefsAccount *ac; + + if (!gtk_tree_model_get_iter_first(model, &iter)) + return; + + do { + gtk_tree_model_get(model, &iter, COL_ACCOUNT, &ac, -1); + if (ac) + account_set_row(ac, &iter, NULL, FALSE); + } while (gtk_tree_model_iter_next(model, &iter)); +} + +/* set account list from dialog */ +static void account_set_list(void) +{ + GtkTreeIter iter; + GtkTreeModel *model = GTK_TREE_MODEL(edit_account.store); + PrefsAccount *ac; + + account_list_free(); + + if (!gtk_tree_model_get_iter_first(model, &iter)) + return; + + do { + gtk_tree_model_get(model, &iter, COL_ACCOUNT, &ac, -1); + if (ac) + account_append(ac); + } while (gtk_tree_model_iter_next(model, &iter)); +} diff --git a/src/account_dialog.h b/src/account_dialog.h new file mode 100644 index 0000000..c952124 --- /dev/null +++ b/src/account_dialog.h @@ -0,0 +1,35 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __ACCOUNT_DIALOG_H__ +#define __ACCOUNT_DIALOG_H__ + +#include + +#include "prefs_account.h" + +void account_set_menu (void); + +void account_edit_open (void); +void account_add (void); +void account_open (PrefsAccount *ac_prefs); + +void account_set_missing_folder(void); + +#endif /* __ACCOUNT_DIALOG_H__ */ diff --git a/src/action.c b/src/action.c new file mode 100644 index 0000000..d4c6d60 --- /dev/null +++ b/src/action.c @@ -0,0 +1,1373 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2015 Hiroyuki Yamamoto & The Sylpheed Claws Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#ifdef GDK_WINDOWING_X11 +# include +#endif /* GDK_WINDOWING_X11 */ +#include +#include +#include +#include +#include +#if HAVE_SYS_WAIT_H +# include +#endif +#include +#include + +#include "utils.h" +#include "gtkutils.h" +#include "manage_window.h" +#include "mainwindow.h" +#include "prefs_common.h" +#include "alertpanel.h" +#include "inputdialog.h" +#include "action.h" +#include "compose.h" +#include "procmsg.h" +#include "textview.h" + +typedef struct _Children Children; +typedef struct _ChildInfo ChildInfo; +typedef struct _UserStringDialog UserStringDialog; + +struct _Children +{ + GtkWidget *dialog; + GtkWidget *text; + GtkWidget *input_entry; + GtkWidget *input_hbox; + GtkWidget *abort_btn; + GtkWidget *close_btn; + GtkWidget *scrolledwin; + + gchar *action; + ActionType action_type; + GSList *list; + gint nb; + gint open_in; + gboolean output; + + GtkWidget *msg_text; + + gboolean is_selection; +}; + +struct _ChildInfo +{ + Children *children; + gchar *cmd; + pid_t pid; + gint chld_in; + gint chld_out; + gint chld_err; + gint chld_status; + gint tag_in; + gint tag_out; + gint tag_err; + gint tag_status; + gint new_out; + + GString *output; +}; + +static void action_update_menu (GtkItemFactory *ifactory, + gchar *branch_path, + gpointer callback, + gpointer data); +static void compose_actions_execute_cb (Compose *compose, + guint action_nb, + GtkWidget *widget); +static void mainwin_actions_execute_cb (MainWindow *mainwin, + guint action_nb, + GtkWidget *widget); +static void msgview_actions_execute_cb (MessageView *msgview, + guint action_nb, + GtkWidget *widget); +static void message_actions_execute (MessageView *msgview, + guint action_nb, + GSList *msg_list); + +static gboolean execute_actions (gchar *action, + GSList *msg_list, + GtkWidget *text, + gint body_pos, + MimeInfo *partinfo); + +static gchar *parse_action_cmd (gchar *action, + MsgInfo *msginfo, + GSList *msg_list, + MimeInfo *partinfo, + const gchar *user_str, + const gchar *user_hidden_str, + const gchar *sel_str); +static gboolean parse_append_filename (GString *cmd, + MsgInfo *msginfo); + +static gboolean parse_append_msgpart (GString *cmd, + MsgInfo *msginfo, + MimeInfo *partinfo); + +static ChildInfo *fork_child (gchar *cmd, + const gchar *msg_str, + Children *children); + +static gint wait_for_children (Children *children); + +static void free_children (Children *children); + +static void childinfo_close_pipes (ChildInfo *child_info); + +static void create_io_dialog (Children *children); +static void update_io_dialog (Children *children); + +static void hide_io_dialog_cb (GtkWidget *widget, + gpointer data); +static gint io_dialog_key_pressed_cb (GtkWidget *widget, + GdkEventKey *event, + gpointer data); + +static void catch_output (gpointer data, + gint source, + GdkInputCondition cond); +static void catch_input (gpointer data, + gint source, + GdkInputCondition cond); +static void catch_status (gpointer data, + gint source, + GdkInputCondition cond); + +static gchar *get_user_string (const gchar *action, + ActionType type); + + +ActionType action_get_type(const gchar *action_str) +{ + const gchar *p; + ActionType action_type = ACTION_NONE; + + g_return_val_if_fail(action_str, ACTION_ERROR); + g_return_val_if_fail(*action_str, ACTION_ERROR); + + p = action_str; + + if (p[0] == '|') { + action_type |= ACTION_PIPE_IN; + p++; + } else if (p[0] == '>') { + action_type |= ACTION_USER_IN; + p++; + } else if (p[0] == '*') { + action_type |= ACTION_USER_HIDDEN_IN; + p++; + } + + if (p[0] == '\0') + return ACTION_ERROR; + + while (*p && action_type != ACTION_ERROR) { + if (p[0] == '%') { + switch (p[1]) { + case 'f': + action_type |= ACTION_SINGLE; + break; + case 'F': + action_type |= ACTION_MULTIPLE; + break; + case 'p': + action_type |= ACTION_SINGLE; + break; + case 's': + action_type |= ACTION_SELECTION_STR; + break; + case 'u': + action_type |= ACTION_USER_STR; + break; + case 'h': + action_type |= ACTION_USER_HIDDEN_STR; + break; + default: + action_type = ACTION_ERROR; + break; + } + } else if (p[0] == '|') { + if (p[1] == '\0') + action_type |= ACTION_PIPE_OUT; + } else if (p[0] == '>') { + if (p[1] == '\0') + action_type |= ACTION_INSERT; + } else if (p[0] == '&') { + if (p[1] == '\0') + action_type |= ACTION_ASYNC; + } + p++; + } + + return action_type; +} + +static gchar *parse_action_cmd(gchar *action, MsgInfo *msginfo, + GSList *msg_list, MimeInfo *partinfo, + const gchar *user_str, + const gchar *user_hidden_str, + const gchar *sel_str) +{ + GString *cmd; + gchar *p; + GSList *cur; + + p = action; + + if (p[0] == '|' || p[0] == '>' || p[0] == '*') + p++; + + cmd = g_string_sized_new(strlen(action)); + + while (p[0] && + !((p[0] == '|' || p[0] == '>' || p[0] == '&') && !p[1])) { + if (p[0] == '%' && p[1]) { + switch (p[1]) { + case 'f': + if (!parse_append_filename(cmd, msginfo)) { + g_string_free(cmd, TRUE); + return NULL; + } + p++; + break; + case 'F': + for (cur = msg_list; cur != NULL; + cur = cur->next) { + MsgInfo *msg = (MsgInfo *)cur->data; + + if (!parse_append_filename(cmd, msg)) { + g_string_free(cmd, TRUE); + return NULL; + } + if (cur->next) + g_string_append_c(cmd, ' '); + } + p++; + break; + case 'p': + if (!parse_append_msgpart(cmd, msginfo, + partinfo)) { + g_string_free(cmd, TRUE); + return NULL; + } + p++; + break; + case 's': + if (sel_str) + g_string_append(cmd, sel_str); + p++; + break; + case 'u': + if (user_str) + g_string_append(cmd, user_str); + p++; + break; + case 'h': + if (user_hidden_str) + g_string_append(cmd, user_hidden_str); + p++; + break; + default: + g_string_append_c(cmd, p[0]); + g_string_append_c(cmd, p[1]); + p++; + } + } else { + g_string_append_c(cmd, p[0]); + } + p++; + } + if (cmd->len == 0) { + g_string_free(cmd, TRUE); + return NULL; + } + + p = cmd->str; + g_string_free(cmd, FALSE); + return p; +} + +static gboolean parse_append_filename(GString *cmd, MsgInfo *msginfo) +{ + gchar *filename; + gchar *p, *q; + gchar escape_ch[] = "\\ "; + + g_return_val_if_fail(msginfo, FALSE); + + filename = procmsg_get_message_file(msginfo); + + if (!filename) { + alertpanel_error(_("Could not get message file %d"), + msginfo->msgnum); + return FALSE; + } + + p = filename; + while ((q = strpbrk(p, "$\"`'\\ \t*?[]&|;<>()!#~")) != NULL) { + escape_ch[1] = *q; + *q = '\0'; + g_string_append(cmd, p); + g_string_append(cmd, escape_ch); + p = q + 1; + } + g_string_append(cmd, p); + + g_free(filename); + + return TRUE; +} + +static gboolean parse_append_msgpart(GString *cmd, MsgInfo *msginfo, + MimeInfo *partinfo) +{ + gboolean single_part = FALSE; + gchar *filename; + gchar *part_filename; + gint ret; + + if (!partinfo) { + partinfo = procmime_scan_message(msginfo); + if (!partinfo) { + alertpanel_error(_("Could not get message part.")); + return FALSE; + } + + single_part = TRUE; + } + + filename = procmsg_get_message_file_path(msginfo); + part_filename = procmime_get_tmp_file_name(partinfo); + + ret = procmime_get_part(part_filename, filename, partinfo); + + if (single_part) + procmime_mimeinfo_free_all(partinfo); + g_free(filename); + + if (ret < 0) { + alertpanel_error(_("Can't get part of multipart message")); + g_free(part_filename); + return FALSE; + } + + g_string_append(cmd, part_filename); + + g_free(part_filename); + + return TRUE; +} + +void action_update_mainwin_menu(GtkItemFactory *ifactory, MainWindow *mainwin) +{ + action_update_menu(ifactory, "/Tools/Actions", + mainwin_actions_execute_cb, mainwin); +} + +void action_update_msgview_menu(GtkItemFactory *ifactory, MessageView *msgview) +{ + action_update_menu(ifactory, "/Tools/Actions", + msgview_actions_execute_cb, msgview); +} + +void action_update_compose_menu(GtkItemFactory *ifactory, Compose *compose) +{ + action_update_menu(ifactory, "/Tools/Actions", + compose_actions_execute_cb, compose); +} + +static void action_update_menu(GtkItemFactory *ifactory, gchar *branch_path, + gpointer callback, gpointer data) +{ + GtkWidget *menuitem; + gchar *menu_path; + GSList *cur; + gchar *action, *action_p; + GList *amenu; + GtkItemFactoryEntry ifentry = {NULL, NULL, NULL, 0, ""}; + + ifentry.path = branch_path; + menuitem = gtk_item_factory_get_widget(ifactory, branch_path); + g_return_if_fail(menuitem != NULL); + + amenu = GTK_MENU_SHELL(menuitem)->children; + while (amenu != NULL) { + GList *alist = amenu->next; + gtk_widget_destroy(GTK_WIDGET(amenu->data)); + amenu = alist; + } + + ifentry.accelerator = NULL; + ifentry.callback_action = 0; + ifentry.callback = callback; + ifentry.item_type = NULL; + + for (cur = prefs_common.actions_list; cur; cur = cur->next) { + action = g_strdup((gchar *)cur->data); + action_p = strstr(action, ": "); + if (action_p && action_p[2] && + action_get_type(&action_p[2]) != ACTION_ERROR) { + action_p[0] = '\0'; + menu_path = g_strdup_printf("%s/%s", branch_path, + action); + ifentry.path = menu_path; + gtk_item_factory_create_item(ifactory, &ifentry, data, + 1); + g_free(menu_path); + } + g_free(action); + ifentry.callback_action++; + } +} + +static void compose_actions_execute_cb(Compose *compose, guint action_nb, + GtkWidget *widget) +{ + gchar *buf, *action; + ActionType action_type; + + g_return_if_fail(action_nb < g_slist_length(prefs_common.actions_list)); + + buf = (gchar *)g_slist_nth_data(prefs_common.actions_list, action_nb); + g_return_if_fail(buf != NULL); + action = strstr(buf, ": "); + g_return_if_fail(action != NULL); + + /* Point to the beginning of the command-line */ + action += 2; + + action_type = action_get_type(action); + if (action_type & (ACTION_SINGLE | ACTION_MULTIPLE)) { + alertpanel_warning + (_("The selected action cannot be used in the compose window\n" + "because it contains %%f, %%F or %%p.")); + return; + } + + execute_actions(action, NULL, compose->text, 0, NULL); +} + +static void mainwin_actions_execute_cb(MainWindow *mainwin, guint action_nb, + GtkWidget *widget) +{ + GSList *msg_list; + + msg_list = summary_get_selected_msg_list(mainwin->summaryview); + message_actions_execute(mainwin->messageview, action_nb, msg_list); + g_slist_free(msg_list); +} + +static void msgview_actions_execute_cb(MessageView *msgview, guint action_nb, + GtkWidget *widget) +{ + GSList *msg_list = NULL; + + if (msgview->msginfo) + msg_list = g_slist_append(msg_list, msgview->msginfo); + message_actions_execute(msgview, action_nb, msg_list); + g_slist_free(msg_list); +} + +static void message_actions_execute(MessageView *msgview, guint action_nb, + GSList *msg_list) +{ + TextView *textview; + MimeInfo *partinfo; + gchar *buf; + gchar *action; + GtkWidget *text = NULL; + guint body_pos = 0; + + g_return_if_fail(action_nb < g_slist_length(prefs_common.actions_list)); + + buf = (gchar *)g_slist_nth_data(prefs_common.actions_list, action_nb); + + g_return_if_fail(buf); + g_return_if_fail((action = strstr(buf, ": "))); + + /* Point to the beginning of the command-line */ + action += 2; + + textview = messageview_get_current_textview(msgview); + if (textview) { + text = textview->text; + body_pos = textview->body_pos; + } + partinfo = messageview_get_selected_mime_part(msgview); + + execute_actions(action, msg_list, text, body_pos, partinfo); +} + +static gboolean execute_actions(gchar *action, GSList *msg_list, + GtkWidget *text, gint body_pos, + MimeInfo *partinfo) +{ + GSList *children_list = NULL; + gint is_ok = TRUE; + gint msg_list_len; + Children *children; + ChildInfo *child_info; + ActionType action_type; + MsgInfo *msginfo; + gchar *cmd; + gchar *sel_str = NULL; + gchar *msg_str = NULL; + gchar *user_str = NULL; + gchar *user_hidden_str = NULL; + GtkTextIter start_iter, end_iter; + gboolean is_selection = FALSE; + + g_return_val_if_fail(action && *action, FALSE); + + action_type = action_get_type(action); + + if (action_type == ACTION_ERROR) + return FALSE; /* ERR: syntax error */ + + if (action_type & (ACTION_SINGLE | ACTION_MULTIPLE) && !msg_list) + return FALSE; /* ERR: file command without selection */ + + msg_list_len = g_slist_length(msg_list); + + if (action_type & (ACTION_PIPE_OUT | ACTION_PIPE_IN | ACTION_INSERT)) { + if (msg_list_len > 1) + return FALSE; /* ERR: pipe + multiple selection */ + if (!text) + return FALSE; /* ERR: pipe and no displayed text */ + } + + if (action_type & ACTION_SELECTION_STR) { + if (!text) + return FALSE; /* ERR: selection string but no text */ + } + + if (text) { + GtkTextBuffer *textbuf; + + textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); + is_selection = gtk_text_buffer_get_selection_bounds + (textbuf, &start_iter, &end_iter); + if (!is_selection) { + gtk_text_buffer_get_iter_at_offset + (textbuf, &start_iter, body_pos); + gtk_text_buffer_get_end_iter(textbuf, &end_iter); + if (!(action_type & ACTION_INSERT)) + gtk_text_buffer_place_cursor + (textbuf, &start_iter); + } + msg_str = gtk_text_buffer_get_text + (textbuf, &start_iter, &end_iter, FALSE); + if (is_selection) + sel_str = g_strdup(msg_str); + } + + if (action_type & ACTION_USER_STR) { + if (!(user_str = get_user_string(action, ACTION_USER_STR))) { + g_free(msg_str); + g_free(sel_str); + return FALSE; + } + } + + if (action_type & ACTION_USER_HIDDEN_STR) { + if (!(user_hidden_str = + get_user_string(action, ACTION_USER_HIDDEN_STR))) { + g_free(msg_str); + g_free(sel_str); + g_free(user_str); + return FALSE; + } + } + + if (text && (action_type & ACTION_PIPE_OUT)) { + GtkTextBuffer *textbuf; + textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); + gtk_text_buffer_delete(textbuf, &start_iter, &end_iter); + } + + children = g_new0(Children, 1); + + children->action = g_strdup(action); + children->action_type = action_type; + children->msg_text = text; + children->is_selection = is_selection; + + if ((action_type & (ACTION_USER_IN | ACTION_USER_HIDDEN_IN)) && + ((action_type & ACTION_SINGLE) == 0 || msg_list_len == 1)) + children->open_in = 1; + + if (action_type & ACTION_SINGLE) { + GSList *cur; + + for (cur = msg_list; cur && is_ok == TRUE; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + if (!msginfo) { + is_ok = FALSE; /* ERR: msginfo missing */ + break; + } + cmd = parse_action_cmd(action, msginfo, msg_list, + partinfo, user_str, + user_hidden_str, sel_str); + if (!cmd) { + debug_print("Action command error\n"); + is_ok = FALSE; /* ERR: incorrect command */ + break; + } + if ((child_info = fork_child(cmd, msg_str, children))) { + children_list = g_slist_append(children_list, + child_info); + } + g_free(cmd); + } + } else { + cmd = parse_action_cmd(action, NULL, msg_list, partinfo, + user_str, user_hidden_str, sel_str); + if (cmd) { + if ((child_info = fork_child(cmd, msg_str, children))) { + children_list = g_slist_append(children_list, + child_info); + } + g_free(cmd); + } else + is_ok = FALSE; /* ERR: incorrect command */ + } + + g_free(msg_str); + g_free(sel_str); + g_free(user_str); + g_free(user_hidden_str); + + if (!children_list) { + /* If not waiting for children, return */ + free_children(children); + } else { + GSList *cur; + + children->list = children_list; + children->nb = g_slist_length(children_list); + + create_io_dialog(children); + + for (cur = children_list; cur; cur = cur->next) { + child_info = (ChildInfo *) cur->data; + child_info->tag_status = + gdk_input_add(child_info->chld_status, + GDK_INPUT_READ, + catch_status, child_info); + } + } + + return is_ok; +} + +static ChildInfo *fork_child(gchar *cmd, const gchar *msg_str, + Children *children) +{ +#ifdef G_OS_UNIX + gint chld_in[2], chld_out[2], chld_err[2], chld_status[2]; + gchar *cmdline[4]; + pid_t pid, gch_pid; + ChildInfo *child_info; + gint sync; + + sync = !(children->action_type & ACTION_ASYNC); + + chld_in[0] = chld_in[1] = chld_out[0] = chld_out[1] = chld_err[0] + = chld_err[1] = chld_status[0] = chld_status[1] = -1; + + if (sync) { + if (pipe(chld_status) || pipe(chld_in) || pipe(chld_out) || + pipe(chld_err)) { + alertpanel_error(_("Command could not be started. " + "Pipe creation failed.\n%s"), + g_strerror(errno)); + /* Closing fd = -1 fails silently */ + close(chld_in[0]); + close(chld_in[1]); + close(chld_out[0]); + close(chld_out[1]); + close(chld_err[0]); + close(chld_err[1]); + close(chld_status[0]); + close(chld_status[1]); + return NULL; /* Pipe error */ + } + } + + debug_print("Forking child and grandchild in %s mode.\n", sync ? "sync" : "async"); + debug_print("Executing: /bin/sh -c %s\n", cmd); + + pid = fork(); + if (pid == 0) { /* Child */ + struct sigaction sa; + + if (setpgid(0, 0)) + perror("setpgid"); + + /* reset signal handlers */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); + +#ifdef GDK_WINDOWING_X11 + close(ConnectionNumber(gdk_display)); +#endif /* GDK_WINDOWING_X11 */ + + gch_pid = fork(); + + if (gch_pid == 0) { + if (setpgid(0, getppid())) + perror("setpgid"); + + if (sync) { + if (children->action_type & + (ACTION_PIPE_IN | + ACTION_USER_IN | + ACTION_USER_HIDDEN_IN)) { + close(fileno(stdin)); + dup (chld_in[0]); + } + close(chld_in[0]); + close(chld_in[1]); + + close(fileno(stdout)); + dup (chld_out[1]); + close(chld_out[0]); + close(chld_out[1]); + + close(fileno(stderr)); + dup (chld_err[1]); + close(chld_err[0]); + close(chld_err[1]); + } + + cmdline[0] = "sh"; + cmdline[1] = "-c"; + cmdline[2] = cmd; + cmdline[3] = NULL; + execvp("/bin/sh", cmdline); + + perror("execvp"); + _exit(1); + } else if (gch_pid < (pid_t) 0) { /* Fork error */ + if (sync) + write(chld_status[1], "1\n", 2); + perror("fork"); + _exit(1); + } else { /* Child */ + if (sync) { + close(chld_in[0]); + close(chld_in[1]); + close(chld_out[0]); + close(chld_out[1]); + close(chld_err[0]); + close(chld_err[1]); + close(chld_status[0]); + + debug_print("Child: Waiting for grandchild (PID: %d)\n", gch_pid); + waitpid(gch_pid, NULL, 0); + debug_print("Child: grandchild ended (PID: %d)\n", gch_pid); + write(chld_status[1], "0\n", 2); + close(chld_status[1]); + } + _exit(0); + } + } else if (pid < 0) { /* Fork error */ + alertpanel_error(_("Could not fork to execute the following " + "command:\n%s\n%s"), + cmd, g_strerror(errno)); + return NULL; + } + + /* Parent */ + + if (!sync) { + waitpid(pid, NULL, 0); + return NULL; + } + + close(chld_in[0]); + if (!(children->action_type & + (ACTION_PIPE_IN | ACTION_USER_IN | ACTION_USER_HIDDEN_IN))) + close(chld_in[1]); + close(chld_out[1]); + close(chld_err[1]); + close(chld_status[1]); + + child_info = g_new0(ChildInfo, 1); + + child_info->children = children; + + child_info->pid = pid; + child_info->cmd = g_strdup(cmd); + child_info->new_out = FALSE; + child_info->output = g_string_new(NULL); + child_info->chld_in = + (children->action_type & + (ACTION_PIPE_IN | ACTION_USER_IN | ACTION_USER_HIDDEN_IN)) + ? chld_in [1] : -1; + child_info->chld_out = chld_out[0]; + child_info->chld_err = chld_err[0]; + child_info->chld_status = chld_status[0]; + child_info->tag_in = -1; + child_info->tag_out = gdk_input_add(chld_out[0], GDK_INPUT_READ, + catch_output, child_info); + child_info->tag_err = gdk_input_add(chld_err[0], GDK_INPUT_READ, + catch_output, child_info); + + debug_print("Monitoring child (PID: %d)\n", pid); + + if (!(children->action_type & + (ACTION_PIPE_IN | ACTION_PIPE_OUT | ACTION_INSERT))) + return child_info; + + if ((children->action_type & ACTION_PIPE_IN) && msg_str) { + write(chld_in[1], msg_str, strlen(msg_str)); + if (!(children->action_type & + (ACTION_USER_IN | ACTION_USER_HIDDEN_IN))) + close(chld_in[1]); + child_info->chld_in = -1; /* No more input */ + } + + return child_info; +#else + return NULL; +#endif /* G_OS_UNIX */ +} + +static void kill_children_cb(GtkWidget *widget, gpointer data) +{ +#ifdef G_OS_UNIX + GSList *cur; + Children *children = (Children *) data; + ChildInfo *child_info; + + for (cur = children->list; cur; cur = cur->next) { + child_info = (ChildInfo *)(cur->data); + debug_print("Killing child: %d\n", child_info->pid); + if (child_info->pid && kill(child_info->pid, SIGTERM) < 0) + perror("kill"); + } +#endif /* G_OS_UNIX */ +} + +static gint wait_for_children(Children *children) +{ + gboolean new_output; + ChildInfo *child_info; + GSList *cur; + gint nb = children->nb; + + debug_print("wait_for_children (%p)\n", children); + + children->nb = 0; + + cur = children->list; + new_output = FALSE; + while (cur) { + child_info = (ChildInfo *)cur->data; + if (child_info->pid) + children->nb++; + new_output |= child_info->new_out; + cur = cur->next; + } + + children->output |= new_output; + + if (new_output || (children->dialog && (nb != children->nb))) + update_io_dialog(children); + + if (children->nb) + return FALSE; + + if (!children->dialog) { + /* free_children(children); */ + } else if (!children->output) { + gtk_widget_destroy(children->dialog); + children->dialog = NULL; + } + + return FALSE; +} + +static void send_input(GtkWidget *w, gpointer data) +{ + Children *children = (Children *) data; + ChildInfo *child_info = (ChildInfo *) children->list->data; + + child_info->tag_in = gdk_input_add(child_info->chld_in, + GDK_INPUT_WRITE, + catch_input, children); + gtk_widget_set_sensitive(children->input_hbox, FALSE); +} + +static gint delete_io_dialog_cb(GtkWidget *w, GdkEvent *e, gpointer data) +{ + hide_io_dialog_cb(w, data); + return TRUE; +} + +static void hide_io_dialog_cb(GtkWidget *w, gpointer data) +{ + + Children *children = (Children *)data; + + if (!children->nb) { + g_signal_handlers_disconnect_matched + (G_OBJECT(children->dialog), + (GSignalMatchType)G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, children); + gtk_widget_destroy(children->dialog); + free_children(children); + } +} + +static gint io_dialog_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + hide_io_dialog_cb(widget, data); + return TRUE; +} + +static void childinfo_close_pipes(ChildInfo *child_info) +{ + /* stdout and stderr pipes are guaranteed to be removed by + * their handler, but in case where we receive child exit notification + * before grand-child's pipes closing signals, we check them and close + * them if necessary + */ + if (child_info->tag_in > 0) + gdk_input_remove(child_info->tag_in); + if (child_info->tag_out > 0) + gdk_input_remove(child_info->tag_out); + if (child_info->tag_err > 0) + gdk_input_remove(child_info->tag_err); + + if (child_info->chld_in >= 0) + close(child_info->chld_in); + if (child_info->chld_out >= 0) + close(child_info->chld_out); + if (child_info->chld_err >= 0) + close(child_info->chld_err); + + close(child_info->chld_status); +} + +static void free_children(Children *children) +{ + ChildInfo *child_info; + + debug_print("Freeing children data %p\n", children); + + g_free(children->action); + while (children->list != NULL) { + child_info = (ChildInfo *)children->list->data; +#ifdef G_OS_UNIX + if (child_info->pid != 0) { + debug_print("free_children: waiting PID %d\n", child_info->pid); + if (waitpid(child_info->pid, NULL, 0) + != child_info->pid) { + perror("waitpid (free_children)"); + } + } +#endif + g_free(child_info->cmd); + g_string_free(child_info->output, TRUE); + children->list = g_slist_remove(children->list, child_info); + g_free(child_info); + } + g_free(children); +} + +static void update_io_dialog(Children *children) +{ + GSList *cur; + + debug_print("Updating actions input/output dialog.\n"); + + if (!children->nb) { + gtk_widget_set_sensitive(children->abort_btn, FALSE); + gtk_widget_set_sensitive(children->close_btn, TRUE); + if (children->input_hbox) + gtk_widget_set_sensitive(children->input_hbox, FALSE); + gtk_widget_grab_focus(children->close_btn); + g_signal_connect(G_OBJECT(children->dialog), + "key_press_event", + G_CALLBACK(io_dialog_key_pressed_cb), + children); + } + + if (children->output) { + GtkWidget *text = children->text; + GtkTextBuffer *textbuf; + GtkTextIter iter, start_iter, end_iter; + gchar *caption; + ChildInfo *child_info; + + gtk_widget_show(children->scrolledwin); + textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); + gtk_text_buffer_get_bounds(textbuf, &start_iter, &end_iter); + gtk_text_buffer_delete(textbuf, &start_iter, &end_iter); + gtk_text_buffer_get_start_iter(textbuf, &iter); + + for (cur = children->list; cur; cur = cur->next) { + child_info = (ChildInfo *)cur->data; + if (child_info->pid) + caption = g_strdup_printf + (_("--- Running: %s\n"), + child_info->cmd); + else + caption = g_strdup_printf + (_("--- Ended: %s\n"), + child_info->cmd); + + gtk_text_buffer_insert(textbuf, &iter, caption, -1); + gtk_text_buffer_insert(textbuf, &iter, + child_info->output->str, -1); + g_free(caption); + child_info->new_out = FALSE; + } + } +} + +static void create_io_dialog(Children *children) +{ + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *entry = NULL; + GtkWidget *input_hbox = NULL; + GtkWidget *send_button; + GtkWidget *label; + GtkWidget *text; + GtkWidget *scrolledwin; + GtkWidget *hbox; + GtkWidget *abort_button; + GtkWidget *close_button; + + debug_print("Creating action IO dialog\n"); + + dialog = gtk_dialog_new(); + gtk_container_set_border_width + (GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), 5); + gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); + gtk_window_set_title(GTK_WINDOW(dialog), _("Action's input/output")); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + manage_window_set_transient(GTK_WINDOW(dialog)); + g_signal_connect(G_OBJECT(dialog), "delete_event", + G_CALLBACK(delete_io_dialog_cb), children); + g_signal_connect(G_OBJECT(dialog), "destroy", + G_CALLBACK(hide_io_dialog_cb), + children); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), vbox); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 8); + gtk_widget_show(vbox); + + label = gtk_label_new(children->action); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + gtk_widget_show(label); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0); + gtk_widget_set_size_request(scrolledwin, + 560 * gtkut_get_dpi_multiplier(), + 200 * gtkut_get_dpi_multiplier()); + gtk_widget_hide(scrolledwin); + + text = gtk_text_view_new(); + + if (prefs_common.textfont) { + PangoFontDescription *font_desc; + font_desc = pango_font_description_from_string + (prefs_common.textfont); + if (font_desc) { + gtk_widget_modify_font(text, font_desc); + pango_font_description_free(font_desc); + } + } + + gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6); + gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6); + gtk_container_add(GTK_CONTAINER(scrolledwin), text); + gtk_widget_show(text); + + if (children->open_in) { + input_hbox = gtk_hbox_new(FALSE, 8); + gtk_widget_show(input_hbox); + + entry = gtk_entry_new(); + gtk_widget_set_size_request + (entry, 320 * gtkut_get_dpi_multiplier(), -1); + g_signal_connect(G_OBJECT(entry), "activate", + G_CALLBACK(send_input), children); + gtk_box_pack_start(GTK_BOX(input_hbox), entry, TRUE, TRUE, 0); + if (children->action_type & ACTION_USER_HIDDEN_IN) + gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); + gtk_widget_show(entry); + + send_button = gtk_button_new_with_label(_(" Send ")); + g_signal_connect(G_OBJECT(send_button), "clicked", + G_CALLBACK(send_input), children); + gtk_box_pack_start(GTK_BOX(input_hbox), send_button, FALSE, + FALSE, 0); + gtk_widget_show(send_button); + + gtk_box_pack_start(GTK_BOX(vbox), input_hbox, FALSE, FALSE, 0); + gtk_widget_grab_focus(entry); + } + + gtkut_stock_button_set_create(&hbox, &abort_button, _("Abort"), + &close_button, GTK_STOCK_CLOSE, + NULL, NULL); + g_signal_connect(G_OBJECT(abort_button), "clicked", + G_CALLBACK(kill_children_cb), children); + g_signal_connect(G_OBJECT(close_button), "clicked", + G_CALLBACK(hide_io_dialog_cb), children); + gtk_widget_show(hbox); + + if (children->nb) + gtk_widget_set_sensitive(close_button, FALSE); + + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), hbox); + + children->dialog = dialog; + children->scrolledwin = scrolledwin; + children->text = text; + children->input_hbox = children->open_in ? input_hbox : NULL; + children->input_entry = children->open_in ? entry : NULL; + children->abort_btn = abort_button; + children->close_btn = close_button; + + gtk_widget_show(dialog); +} + +static void catch_status(gpointer data, gint source, GdkInputCondition cond) +{ + ChildInfo *child_info = (ChildInfo *)data; + gchar buf; + gint c; + + debug_print("Catching child status (child PID: %d).\n", child_info->pid); + + gdk_threads_enter(); + + gdk_input_remove(child_info->tag_status); + + c = read(source, &buf, 1); + if (c == 1) + debug_print("Child (PID: %d) returned %c\n", child_info->pid, buf); + else + debug_print("Could not get child (PID: %d) status\n", child_info->pid); + +#ifdef G_OS_UNIX + waitpid(child_info->pid, NULL, 0); +#endif + childinfo_close_pipes(child_info); + child_info->pid = 0; + + wait_for_children(child_info->children); + + gdk_threads_leave(); +} + +static void catch_input(gpointer data, gint source, GdkInputCondition cond) +{ + Children *children = (Children *)data; + ChildInfo *child_info = (ChildInfo *)children->list->data; + gchar *input; + gint c, count, len; + + debug_print("Sending input to grand child.\n"); + if (!(cond && GDK_INPUT_WRITE)) + return; + + gdk_threads_enter(); + + gdk_input_remove(child_info->tag_in); + child_info->tag_in = -1; + + input = gtk_editable_get_chars(GTK_EDITABLE(children->input_entry), + 0, -1); + len = strlen(input); + count = 0; + + do { + c = write(child_info->chld_in, input + count, len - count); + if (c >= 0) + count += c; + } while (c >= 0 && count < len); + + if (c >= 0) + write(child_info->chld_in, "\n", 2); + + g_free(input); + + gtk_entry_set_text(GTK_ENTRY(children->input_entry), ""); + gtk_widget_set_sensitive(children->input_hbox, TRUE); + close(child_info->chld_in); + child_info->chld_in = -1; + debug_print("Input to grand child sent.\n"); + + gdk_threads_leave(); +} + +static void catch_output(gpointer data, gint source, GdkInputCondition cond) +{ + ChildInfo *child_info = (ChildInfo *)data; + gint c; + gchar buf[BUFFSIZE]; + + gdk_threads_enter(); + + debug_print("Catching grand child's output (child PID: %d).\n", child_info->pid); + if (child_info->children->action_type & + (ACTION_PIPE_OUT | ACTION_INSERT) + && source == child_info->chld_out) { + GtkTextView *text = + GTK_TEXT_VIEW(child_info->children->msg_text); + GtkTextBuffer *textbuf = gtk_text_view_get_buffer(text); + GtkTextIter iter; + GtkTextMark *mark; + gint ins_pos; + + mark = gtk_text_buffer_get_insert(textbuf); + gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark); + ins_pos = gtk_text_iter_get_offset(&iter); + + while (TRUE) { + gsize bytes_read = 0, bytes_written = 0; + gchar *ret_str; + + c = read(source, buf, sizeof(buf) - 1); + if (c == 0) + break; + + ret_str = g_locale_to_utf8 + (buf, c, &bytes_read, &bytes_written, NULL); + if (ret_str && bytes_written > 0) { + gtk_text_buffer_insert + (textbuf, &iter, ret_str, + bytes_written); + g_free(ret_str); + } else + gtk_text_buffer_insert(textbuf, &iter, buf, c); + } + + if (child_info->children->is_selection) { + GtkTextIter ins; + + gtk_text_buffer_get_iter_at_offset + (textbuf, &ins, ins_pos); + gtk_text_buffer_select_range(textbuf, &ins, &iter); + } + } else { + c = read(source, buf, sizeof(buf) - 1); + if (c > 0) { + gsize bytes_read = 0, bytes_written = 0; + gchar *ret_str; + + ret_str = g_locale_to_utf8 + (buf, c, &bytes_read, &bytes_written, NULL); + if (ret_str && bytes_written > 0) { + g_string_append_len + (child_info->output, ret_str, + bytes_written); + g_free(ret_str); + } else + g_string_append_len(child_info->output, buf, c); + + child_info->new_out = TRUE; + } + } + if (c == 0) { + if (source == child_info->chld_out) { + gdk_input_remove(child_info->tag_out); + child_info->tag_out = -1; + close(child_info->chld_out); + child_info->chld_out = -1; + } else { + gdk_input_remove(child_info->tag_err); + child_info->tag_err = -1; + close(child_info->chld_err); + child_info->chld_err = -1; + } + } + + wait_for_children(child_info->children); + + gdk_threads_leave(); +} + +static gchar *get_user_string(const gchar *action, ActionType type) +{ + gchar *message; + gchar *user_str = NULL; + + switch (type) { + case ACTION_USER_HIDDEN_STR: + message = g_strdup_printf + (_("Enter the argument for the following action:\n" + "(`%%h' will be replaced with the argument)\n" + " %s"), + action); + user_str = input_dialog_with_invisible + (_("Action's hidden user argument"), message, NULL); + break; + case ACTION_USER_STR: + message = g_strdup_printf + (_("Enter the argument for the following action:\n" + "(`%%u' will be replaced with the argument)\n" + " %s"), + action); + user_str = input_dialog + (_("Action's user argument"), message, NULL); + break; + default: + g_warning("Unsupported action type %d", type); + } + + return user_str; +} diff --git a/src/action.h b/src/action.h new file mode 100644 index 0000000..373073d --- /dev/null +++ b/src/action.h @@ -0,0 +1,56 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2003 Hiroyuki Yamamoto & The Sylpheed Claws Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __ACTION_H__ +#define __ACTION_H__ + +#include +#include + +#include "mainwindow.h" +#include "messageview.h" +#include "compose.h" + +typedef enum +{ + ACTION_NONE = 1 << 0, + ACTION_PIPE_IN = 1 << 1, + ACTION_PIPE_OUT = 1 << 2, + ACTION_SINGLE = 1 << 3, + ACTION_MULTIPLE = 1 << 4, + ACTION_ASYNC = 1 << 5, + ACTION_USER_IN = 1 << 6, + ACTION_USER_HIDDEN_IN = 1 << 7, + ACTION_INSERT = 1 << 8, + ACTION_USER_STR = 1 << 9, + ACTION_USER_HIDDEN_STR = 1 << 10, + ACTION_SELECTION_STR = 1 << 11, + ACTION_ERROR = 1 << 30 +} ActionType; + +ActionType action_get_type (const gchar *action_str); + +void action_update_mainwin_menu (GtkItemFactory *ifactory, + MainWindow *mainwin); +void action_update_msgview_menu (GtkItemFactory *ifactory, + MessageView *msgview); +void action_update_compose_menu (GtkItemFactory *ifactory, + Compose *compose); + +#endif /* __ACTION_H__ */ diff --git a/src/addr_compl.c b/src/addr_compl.c new file mode 100644 index 0000000..1fcc513 --- /dev/null +++ b/src/addr_compl.c @@ -0,0 +1,1142 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * + * Copyright (C) 2000-2005 by Alfons Hoogervorst & The Sylpheed Claws Team. + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "defs.h" + +#include +#include +#include +#include + +#include +#include + +#include "xml.h" +#include "addr_compl.h" +#include "utils.h" +#include "addressbook.h" +#include "main.h" +#include "prefs_common.h" + +/* How it works: + * + * The address book is read into memory. We set up an address list + * containing all address book entries. Next we make the completion + * list, which contains all the completable strings, and store a + * reference to the address entry it belongs to. + * After calling the g_completion_complete(), we get a reference + * to a valid email address. + * + * Completion is very simplified. We never complete on another prefix, + * i.e. we neglect the next smallest possible prefix for the current + * completion cache. This is simply done so we might break up the + * addresses a little more (e.g. break up alfons@proteus.demon.nl into + * something like alfons, proteus, demon, nl; and then completing on + * any of those words). + */ + +/* address_entry - structure which refers to the original address entry in the + * address book + */ +typedef struct +{ + gchar *name; + gchar *address; +} address_entry; + +/* completion_entry - structure used to complete addresses, with a reference + * the the real address information. + */ +typedef struct +{ + gchar *string; /* string to complete */ + address_entry *ref; /* address the string belongs to */ +} completion_entry; + +/*******************************************************************************/ + +static gint ref_count; /* list ref count */ +static GList *completion_list; /* list of strings to be checked */ +static GList *address_list; /* address storage */ +static GCompletion *completion; /* completion object */ + +/* To allow for continuing completion we have to keep track of the state + * using the following variables. No need to create a context object. */ + +static gint completion_count; /* nr of addresses incl. the prefix */ +static gint completion_next; /* next prev address */ +static GSList *completion_addresses; /* unique addresses found in the + completion cache. */ +static gchar *completion_prefix; /* last prefix. (this is cached here + * because the prefix passed to g_completion + * is g_strdown()'ed */ + +/*******************************************************************************/ + + +static void address_completion_entry_changed (GtkEditable *editable, + gpointer data); + + +/* completion_func() - used by GTK to find the string data to be used for + * completion + */ +static gchar *completion_func(gpointer data) +{ + g_return_val_if_fail(data != NULL, NULL); + + return ((completion_entry *)data)->string; +} + +static void init_all(void) +{ + completion = g_completion_new(completion_func); + g_return_if_fail(completion != NULL); +} + +static void free_all(void) +{ + GList *walk; + + walk = g_list_first(completion_list); + for (; walk != NULL; walk = g_list_next(walk)) { + completion_entry *ce = (completion_entry *) walk->data; + g_free(ce->string); + g_free(ce); + } + g_list_free(completion_list); + completion_list = NULL; + + walk = address_list; + for (; walk != NULL; walk = g_list_next(walk)) { + address_entry *ae = (address_entry *) walk->data; + g_free(ae->name); + g_free(ae->address); + g_free(ae); + } + g_list_free(address_list); + address_list = NULL; + + g_completion_free(completion); + completion = NULL; +} + +static gint address_entry_find_func(gconstpointer a, gconstpointer b) +{ + const address_entry *ae1 = a; + const address_entry *ae2 = b; + gint val; + + if (!a || !b) + return -1; + + val = strcmp(ae1->name, ae2->name); + if (val != 0) + return val; + val = strcmp(ae1->address, ae2->address); + if (val != 0) + return val; + + return 0; +} + +static void add_completion_entry(const gchar *str, address_entry *ae) +{ + completion_entry *ce; + + if (!str || *str == '\0') + return; + if (!ae) + return; + + ce = g_new0(completion_entry, 1); + /* GCompletion list is case sensitive */ + ce->string = g_utf8_strdown(str, -1); + ce->ref = ae; + completion_list = g_list_append(completion_list, ce); +} + +/* add_address() - adds address to the completion list. this function looks + * complicated, but it's only allocation checks. + */ +static gint add_address(const gchar *name, const gchar *firstname, const gchar *lastname, const gchar *nickname, const gchar *address) +{ + address_entry *ae; + GList *found; + + if (!address || *address == '\0') + return -1; + + /* debugg_print("add_address: [%s] [%s] [%s] [%s] [%s]\n", name, firstname, lastname, nickname, address); */ + + ae = g_new0(address_entry, 1); + ae->name = g_strdup(name ? name : ""); + ae->address = g_strdup(address); + if ((found = g_list_find_custom(address_list, ae, + address_entry_find_func))) { + g_free(ae->name); + g_free(ae->address); + g_free(ae); + ae = (address_entry *)found->data; + } else + address_list = g_list_append(address_list, ae); + + if (name) { + const gchar *p = name; + + while (*p != '\0') { + add_completion_entry(p, ae); + while (*p && *p != '-' && *p != '.' && !g_ascii_isspace(*p)) + p++; + while (*p == '-' || *p == '.' || g_ascii_isspace(*p)) + p++; + } + } + add_completion_entry(firstname, ae); + add_completion_entry(lastname, ae); + add_completion_entry(nickname, ae); + add_completion_entry(address, ae); + + return 0; +} + +/* read_address_book() + */ +static void read_address_book(void) { + addressbook_load_completion_full( add_address ); +} + +/* start_address_completion() - returns the number of addresses + * that should be matched for completion. + */ +gint start_address_completion(void) +{ + clear_completion_cache(); + if (!ref_count) { + init_all(); + /* open the address book */ + read_address_book(); + /* merge the completion entry list into g_completion */ + if (completion_list) + g_completion_add_items(completion, completion_list); + } + ref_count++; + debug_print("start_address_completion ref count %d\n", ref_count); + + return g_list_length(completion_list); +} + +/* get_address_from_edit() - returns a possible address (or a part) + * from an entry box. To make life easier, we only look at the last valid address + * component; address completion only works at the last string component in + * the entry box. + */ +gchar *get_address_from_edit(GtkEntry *entry, gint *start_pos) +{ + const gchar *edit_text, *p; + gint cur_pos; + gboolean in_quote = FALSE; + gboolean in_bracket = FALSE; + gchar *str; + + edit_text = gtk_entry_get_text(entry); + if (edit_text == NULL) return NULL; + + cur_pos = gtk_editable_get_position(GTK_EDITABLE(entry)); + + /* scan for a separator. doesn't matter if walk points at null byte. */ + for (p = g_utf8_offset_to_pointer(edit_text, cur_pos); + p > edit_text; + p = g_utf8_prev_char(p)) { + if (*p == '"') + in_quote ^= TRUE; + else if (!in_quote) { + if (!in_bracket && *p == ',') + break; + else if (*p == '>') + in_bracket = TRUE; + else if (*p == '<') + in_bracket = FALSE; + } + } + + /* have something valid */ + if (g_utf8_strlen(p, -1) == 0) + return NULL; + + /* now scan back until we hit a valid character */ + for (; *p && (*p == ',' || g_ascii_isspace(*p)); + p = g_utf8_next_char(p)) + ; + + if (g_utf8_strlen(p, -1) == 0) + return NULL; + + if (start_pos) *start_pos = g_utf8_pointer_to_offset(edit_text, p); + + str = g_strdup(p); + + return str; +} + +/* replace_address_in_edit() - replaces an incompleted address with a completed one. + */ +void replace_address_in_edit(GtkEntry *entry, const gchar *newtext, + gint start_pos) +{ + gchar *origtext; + + if (!newtext) return; + + debug_print("replace_address_in_edit: %s\n", newtext); + + origtext = gtk_editable_get_chars(GTK_EDITABLE(entry), start_pos, -1); + if (!strcmp(origtext, newtext)) { + g_free(origtext); + return; + } + g_free(origtext); + + g_signal_handlers_block_by_func + (entry, address_completion_entry_changed, NULL); + gtk_editable_delete_text(GTK_EDITABLE(entry), start_pos, -1); + gtk_editable_insert_text(GTK_EDITABLE(entry), newtext, strlen(newtext), + &start_pos); + gtk_editable_set_position(GTK_EDITABLE(entry), -1); + g_signal_handlers_unblock_by_func + (entry, address_completion_entry_changed, NULL); +} + +#if 0 +static gint insert_address_func(gconstpointer a, gconstpointer b) +{ + const address_entry *ae1 = a; + const address_entry *ae2 = b; + gchar *s1, *s2; + gint val; + + if (!a || !b) + return -1; + + s1 = g_utf8_casefold(ae1->address, -1); + s2 = g_utf8_casefold(ae2->address, -1); + val = g_utf8_collate(s1, s2); + g_free(s2); + g_free(s1); + if (val != 0) + return val; + s1 = g_utf8_casefold(ae1->name, -1); + s2 = g_utf8_casefold(ae2->name, -1); + val = g_utf8_collate(s1, s2); + g_free(s2); + g_free(s1); + if (val != 0) + return val; + + return 0; +} +#endif + +/* complete_address() - tries to complete an addres, and returns the + * number of addresses found. use get_complete_address() to get one. + * returns zero if no match was found, otherwise the number of addresses, + * with the original prefix at index 0. + */ +guint complete_address(const gchar *str) +{ + GList *result; + gchar *d; + guint count, cpl; + completion_entry *ce; + + g_return_val_if_fail(str != NULL, 0); + + clear_completion_cache(); + completion_prefix = g_strdup(str); + + /* g_completion is case sensitive */ + d = g_utf8_strdown(str, -1); + result = g_completion_complete(completion, d, NULL); + + count = g_list_length(result); + if (count) { + /* create list with unique addresses */ + for (cpl = 0, result = g_list_first(result); + result != NULL; + result = g_list_next(result)) { + ce = (completion_entry *)(result->data); + if (NULL == g_slist_find(completion_addresses, + ce->ref)) { + cpl++; + completion_addresses = + g_slist_append(completion_addresses, + ce->ref); +#if 0 + g_slist_insert_sorted + (completion_addresses, ce->ref, + insert_address_func); +#endif + } + } + count = cpl + 1; /* index 0 is the original prefix */ + completion_next = 1; /* we start at the first completed one */ + } else { + g_free(completion_prefix); + completion_prefix = NULL; + } + + completion_count = count; + + g_free(d); + + return count; +} + +/* get_complete_address() - returns a complete address. the returned + * string should be freed + */ +gchar *get_complete_address(gint index) +{ + const address_entry *p; + gchar *address = NULL; + + if (index < completion_count) { + if (index == 0) + address = g_strdup(completion_prefix); + else { + /* get something from the unique addresses */ + p = (address_entry *)g_slist_nth_data + (completion_addresses, index - 1); + if (p != NULL) { + if (!p->name || p->name[0] == '\0') + address = g_strdup(p->address); + else if (p->name[0] != '"' && + strpbrk(p->name, "(),.:;<>@[]") != NULL) + address = g_strdup_printf + ("\"%s\" <%s>", p->name, p->address); + else + address = g_strdup_printf + ("%s <%s>", p->name, p->address); + } + } + } + + return address; +} + +gchar *get_next_complete_address(void) +{ + if (is_completion_pending()) { + gchar *res; + + res = get_complete_address(completion_next); + completion_next += 1; + if (completion_next >= completion_count) + completion_next = 0; + + return res; + } else + return NULL; +} + +gchar *get_prev_complete_address(void) +{ + if (is_completion_pending()) { + int n = completion_next - 2; + + /* real previous */ + n = (n + (completion_count * 5)) % completion_count; + + /* real next */ + completion_next = n + 1; + if (completion_next >= completion_count) + completion_next = 0; + return get_complete_address(n); + } else + return NULL; +} + +guint get_completion_count(void) +{ + if (is_completion_pending()) + return completion_count; + else + return 0; +} + +/* should clear up anything after complete_address() */ +void clear_completion_cache(void) +{ + if (is_completion_pending()) { + if (completion_prefix) + g_free(completion_prefix); + + if (completion_addresses) { + g_slist_free(completion_addresses); + completion_addresses = NULL; + } + + completion_count = completion_next = 0; + } +} + +gboolean is_completion_pending(void) +{ + /* check if completion pending, i.e. we might satisfy a request for the next + * or previous address */ + return completion_count; +} + +/* invalidate_address_completion() - should be called if address book + * changed; + */ +gint invalidate_address_completion(void) +{ + if (ref_count) { + /* simply the same as start_address_completion() */ + debug_print("Invalidation request for address completion\n"); + free_all(); + init_all(); + read_address_book(); + if (completion_list) + g_completion_add_items(completion, completion_list); + clear_completion_cache(); + } + + return g_list_length(completion_list); +} + +gint end_address_completion(void) +{ + clear_completion_cache(); + + if (0 == --ref_count) + free_all(); + + debug_print("end_address_completion ref count %d\n", ref_count); + + return ref_count; +} + + +/* address completion entry ui. the ui (completion list was inspired by galeon's + * auto completion list). remaining things powered by sylpheed's completion engine. + */ + +#define ENTRY_DATA_TAB_HOOK "tab_hook" /* used to lookup entry */ +#define WINDOW_DATA_COMPL_ENTRY "compl_entry" /* used to store entry for compl. window */ +#define WINDOW_DATA_COMPL_CLIST "compl_clist" /* used to store clist for compl. window */ + +static void address_completion_mainwindow_set_focus (GtkWindow *window, + GtkWidget *widget, + gpointer data); +static gboolean address_completion_entry_key_pressed (GtkEntry *entry, + GdkEventKey *ev, + gpointer data); +static gboolean address_completion_complete_address_in_entry + (GtkEntry *entry, + gboolean next); +static void address_completion_create_completion_window (GtkEntry *entry, + gboolean select_next); + +static void completion_window_select_row(GtkCList *clist, + gint row, + gint col, + GdkEvent *event, + GtkWidget **window); +static gboolean completion_window_button_press + (GtkWidget *widget, + GdkEventButton *event, + GtkWidget **window); +static gboolean completion_window_key_press + (GtkWidget *widget, + GdkEventKey *event, + GtkWidget **window); + + +static void completion_window_advance_to_row(GtkCList *clist, gint row) +{ + g_return_if_fail(row < completion_count); + gtk_clist_select_row(clist, row, 0); +} + +static void completion_window_advance_selection(GtkCList *clist, gboolean forward) +{ + int row; + + g_return_if_fail(clist != NULL); + g_return_if_fail(clist->selection != NULL); + + row = GPOINTER_TO_INT(clist->selection->data); + + row = forward ? (row + 1) % completion_count : + (row - 1) < 0 ? completion_count - 1 : row - 1; + + gtk_clist_freeze(clist); + completion_window_advance_to_row(clist, row); + gtk_clist_thaw(clist); + +#ifdef __APPLE__ + /* workaround for a draw bug in OS X */ + gtk_widget_queue_draw(GTK_WIDGET(clist)); +#endif +} + +#if 0 +/* completion_window_accept_selection() - accepts the current selection in the + * clist, and destroys the window */ +static void completion_window_accept_selection(GtkWidget **window, + GtkCList *clist, + GtkEntry *entry) +{ + gchar *address = NULL, *text = NULL; + gint cursor_pos, row; + + g_return_if_fail(window != NULL); + g_return_if_fail(*window != NULL); + g_return_if_fail(clist != NULL); + g_return_if_fail(entry != NULL); + g_return_if_fail(clist->selection != NULL); + + /* FIXME: I believe it's acceptable to access the selection member directly */ + row = GPOINTER_TO_INT(clist->selection->data); + + /* we just need the cursor position */ + address = get_address_from_edit(entry, &cursor_pos); + g_free(address); + gtk_clist_get_text(clist, row, 0, &text); + replace_address_in_edit(entry, text, cursor_pos); + + clear_completion_cache(); + gtk_widget_destroy(*window); + *window = NULL; +} +#endif + +/* completion_window_apply_selection() - apply the current selection in the + * clist */ +static void completion_window_apply_selection(GtkCList *clist, GtkEntry *entry) +{ + gchar *address = NULL, *text = NULL; + gint cursor_pos, row; + + g_return_if_fail(clist != NULL); + g_return_if_fail(entry != NULL); + g_return_if_fail(clist->selection != NULL); + + row = GPOINTER_TO_INT(clist->selection->data); + + address = get_address_from_edit(entry, &cursor_pos); + g_free(address); + gtk_clist_get_text(clist, row, 0, &text); + replace_address_in_edit(entry, text, cursor_pos); +} + +static void completion_window_apply_selection_address_only(GtkCList *clist, GtkEntry *entry) +{ + gchar *address = NULL; + address_entry *ae; + gint cursor_pos, row; + + g_return_if_fail(clist != NULL); + g_return_if_fail(entry != NULL); + g_return_if_fail(clist->selection != NULL); + + row = GPOINTER_TO_INT(clist->selection->data); + + ae = (address_entry *)g_slist_nth_data(completion_addresses, row - 1); + if (ae && ae->address) { + address = get_address_from_edit(entry, &cursor_pos); + g_free(address); + replace_address_in_edit(entry, ae->address, cursor_pos); + } +} + +/* should be called when creating the main window containing address + * completion entries */ +void address_completion_start(GtkWidget *mainwindow) +{ + start_address_completion(); + + /* register focus change hook */ + g_signal_connect(G_OBJECT(mainwindow), "set_focus", + G_CALLBACK(address_completion_mainwindow_set_focus), + mainwindow); +} + +/* Need unique data to make unregistering signal handler possible for the auto + * completed entry */ +#define COMPLETION_UNIQUE_DATA (GINT_TO_POINTER(0xfeefaa)) + +void address_completion_register_entry(GtkEntry *entry) +{ + g_return_if_fail(entry != NULL); + g_return_if_fail(GTK_IS_ENTRY(entry)); + + /* add hooked property */ + g_object_set_data(G_OBJECT(entry), ENTRY_DATA_TAB_HOOK, entry); + + /* add keypress event */ + g_signal_connect_closure + (G_OBJECT(entry), "key_press_event", + g_cclosure_new + (G_CALLBACK(address_completion_entry_key_pressed), + COMPLETION_UNIQUE_DATA, NULL), + FALSE); + g_signal_connect(G_OBJECT(entry), "changed", + G_CALLBACK(address_completion_entry_changed), + NULL); +} + +void address_completion_unregister_entry(GtkEntry *entry) +{ + GObject *entry_obj; + + g_return_if_fail(entry != NULL); + g_return_if_fail(GTK_IS_ENTRY(entry)); + + entry_obj = g_object_get_data(G_OBJECT(entry), ENTRY_DATA_TAB_HOOK); + g_return_if_fail(entry_obj); + g_return_if_fail(entry_obj == G_OBJECT(entry)); + + /* has the hooked property? */ + g_object_set_data(G_OBJECT(entry), ENTRY_DATA_TAB_HOOK, NULL); + + /* remove the hook */ + g_signal_handlers_disconnect_by_func + (G_OBJECT(entry), + G_CALLBACK(address_completion_entry_key_pressed), + COMPLETION_UNIQUE_DATA); +} + +/* should be called when main window with address completion entries + * terminates. + * NOTE: this function assumes that it is called upon destruction of + * the window */ +void address_completion_end(GtkWidget *mainwindow) +{ + /* if address_completion_end() is really called on closing the window, + * we don't need to unregister the set_focus_cb */ + end_address_completion(); +} + +/* if focus changes to another entry, then clear completion cache */ +static void address_completion_mainwindow_set_focus(GtkWindow *window, + GtkWidget *widget, + gpointer data) +{ + if (widget && GTK_IS_ENTRY(widget) && + g_object_get_data(G_OBJECT(widget), ENTRY_DATA_TAB_HOOK)) { + clear_completion_cache(); + } +} + +static GtkWidget *completion_window; + +/* watch for tabs in one of the address entries. if no tab then clear the + * completion cache */ +static gboolean address_completion_entry_key_pressed(GtkEntry *entry, + GdkEventKey *ev, + gpointer data) +{ + if (!prefs_common.fullauto_completion_mode && ev->keyval == GDK_Tab && + !completion_window) { + if (address_completion_complete_address_in_entry(entry, TRUE)) { + address_completion_create_completion_window(entry, TRUE); + /* route a void character to the default handler */ + /* this is a dirty hack; we're actually changing a key + * reported by the system. */ + ev->keyval = GDK_AudibleBell_Enable; + ev->state &= ~GDK_SHIFT_MASK; + return TRUE; + } + } + + if (!completion_window) + return FALSE; + + if ( ev->keyval == GDK_Up + || ev->keyval == GDK_Down + || ev->keyval == GDK_Page_Up + || ev->keyval == GDK_Page_Down + || ev->keyval == GDK_Return + || ev->keyval == GDK_Escape + || ev->keyval == GDK_Tab + || ev->keyval == GDK_ISO_Left_Tab) { + completion_window_key_press(completion_window, ev, &completion_window); + return TRUE; + } else if (ev->keyval == GDK_Shift_L + || ev->keyval == GDK_Shift_R + || ev->keyval == GDK_Control_L + || ev->keyval == GDK_Control_R + || ev->keyval == GDK_Caps_Lock + || ev->keyval == GDK_Shift_Lock + || ev->keyval == GDK_Meta_L + || ev->keyval == GDK_Meta_R + || ev->keyval == GDK_Alt_L + || ev->keyval == GDK_Alt_R) { + /* these buttons should not clear the cache... */ + } else { + clear_completion_cache(); + gtk_widget_destroy(completion_window); + completion_window = NULL; + } + + return FALSE; +} + +static void address_completion_entry_changed(GtkEditable *editable, + gpointer data) +{ + GtkEntry *entry = GTK_ENTRY(editable); + + if (!prefs_common.fullauto_completion_mode) + return; + + g_signal_handlers_block_by_func + (editable, address_completion_entry_changed, data); + if (address_completion_complete_address_in_entry(entry, TRUE)) { + address_completion_create_completion_window(entry, FALSE); + } else { + clear_completion_cache(); + if (completion_window) { + gtk_widget_destroy(completion_window); + completion_window = NULL; + } + } + g_signal_handlers_unblock_by_func + (editable, address_completion_entry_changed, data); +} + +/* initialize the completion cache and put first completed string + * in entry. this function used to do back cycling but this is not + * currently used. since the address completion behaviour has been + * changed regularly, we keep the feature in case someone changes + * his / her mind again. :) */ +static gboolean address_completion_complete_address_in_entry(GtkEntry *entry, + gboolean next) +{ + gint ncount = 0, cursor_pos; + gchar *address, *new = NULL; + gboolean completed = FALSE; + + g_return_val_if_fail(entry != NULL, FALSE); + + if (!GTK_WIDGET_HAS_FOCUS(entry)) return FALSE; + + /* get an address component from the cursor */ + address = get_address_from_edit(entry, &cursor_pos); + if (!address) return FALSE; + + /* still something in the cache */ + if (is_completion_pending()) { + new = next ? get_next_complete_address() : + get_prev_complete_address(); + } else { + if (0 < (ncount = complete_address(address))) + new = get_next_complete_address(); + } + + if (new) { + /* prevent "change" signal */ + /* replace_address_in_edit(entry, new, cursor_pos); */ + + /* don't complete if entry equals to the completed address */ + if (ncount == 2 && !strcmp(address, new)) + completed = FALSE; + else + completed = TRUE; + g_free(new); + } + + g_free(address); + + return completed; +} + +static void address_completion_create_completion_window(GtkEntry *entry_, + gboolean select_next) +{ + gint x, y, width; + GtkWidget *scroll, *clist; + GtkRequisition r; + guint count = 0; + GtkWidget *entry = GTK_WIDGET(entry_); + + debug_print("address_completion_create_completion_window\n"); + + if (completion_window) { + gtk_widget_destroy(completion_window); + completion_window = NULL; + } + + scroll = gtk_scrolled_window_new(NULL, NULL); + clist = gtk_clist_new(1); + gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_SINGLE); + + completion_window = gtk_window_new(GTK_WINDOW_POPUP); + + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(completion_window), scroll); + gtk_container_add(GTK_CONTAINER(scroll), clist); + + /* set the unique data so we can always get back the entry and + * clist window to which this completion window has been attached */ + g_object_set_data(G_OBJECT(completion_window), + WINDOW_DATA_COMPL_ENTRY, entry_); + g_object_set_data(G_OBJECT(completion_window), + WINDOW_DATA_COMPL_CLIST, clist); + + g_signal_connect(G_OBJECT(clist), "select_row", + G_CALLBACK(completion_window_select_row), + &completion_window); + + for (count = 0; count < get_completion_count(); count++) { + gchar *text[] = {NULL, NULL}; + + text[0] = get_complete_address(count); + gtk_clist_append(GTK_CLIST(clist), text); + g_free(text[0]); + } + + gdk_window_get_origin(entry->window, &x, &y); + gtk_widget_size_request(entry, &r); + width = entry->allocation.width; + y += r.height; + gtk_window_move(GTK_WINDOW(completion_window), x, y); + + gtk_widget_size_request(clist, &r); + gtk_widget_set_size_request(completion_window, width, r.height); + gtk_widget_show_all(completion_window); + gtk_widget_size_request(clist, &r); + + if ((y + r.height) > gdk_screen_height()) { + gtk_window_set_policy(GTK_WINDOW(completion_window), + TRUE, FALSE, FALSE); + gtk_widget_set_size_request(completion_window, width, + gdk_screen_height () - y); + } + + g_signal_connect(G_OBJECT(completion_window), + "button-press-event", + G_CALLBACK(completion_window_button_press), + &completion_window); + g_signal_connect(G_OBJECT(completion_window), + "key-press-event", + G_CALLBACK(completion_window_key_press), + &completion_window); + gdk_pointer_grab(completion_window->window, TRUE, + GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, NULL, GDK_CURRENT_TIME); + gtk_grab_add(completion_window); + + /* this gets rid of the irritating focus rectangle that doesn't + * follow the selection */ + GTK_WIDGET_UNSET_FLAGS(clist, GTK_CAN_FOCUS); + gtk_clist_select_row(GTK_CLIST(clist), select_next ? 1 : 0, 0); + + debug_print("address_completion_create_completion_window done\n"); +} + + +/* row selection sends completed address to entry. + * note: event is NULL if selected by anything else than a mouse button. */ +static void completion_window_select_row(GtkCList *clist, gint row, gint col, + GdkEvent *event, + GtkWidget **window) +{ + GtkEntry *entry; + + g_return_if_fail(window != NULL); + g_return_if_fail(*window != NULL); + + entry = GTK_ENTRY(g_object_get_data(G_OBJECT(*window), + WINDOW_DATA_COMPL_ENTRY)); + g_return_if_fail(entry != NULL); + + completion_window_apply_selection(clist, entry); + + if (!event || event->type != GDK_BUTTON_RELEASE) + return; + + clear_completion_cache(); + gtk_widget_destroy(*window); + *window = NULL; +} + +/* completion_window_button_press() - check is mouse click is anywhere + * else (not in the completion window). in that case the completion + * window is destroyed, and the original prefix is restored */ +static gboolean completion_window_button_press(GtkWidget *widget, + GdkEventButton *event, + GtkWidget **window) +{ + GtkWidget *event_widget, *entry; + gchar *prefix; + gint cursor_pos; + gboolean restore = TRUE; + + g_return_val_if_fail(window != NULL, FALSE); + g_return_val_if_fail(*window != NULL, FALSE); + + entry = GTK_WIDGET(g_object_get_data(G_OBJECT(*window), + WINDOW_DATA_COMPL_ENTRY)); + g_return_val_if_fail(entry != NULL, FALSE); + + event_widget = gtk_get_event_widget((GdkEvent *)event); + if (event_widget != widget) { + while (event_widget) { + if (event_widget == widget) + return FALSE; + else if (event_widget == entry) { + restore = FALSE; + break; + } + event_widget = event_widget->parent; + } + } + + if (restore) { + prefix = get_complete_address(0); + g_free(get_address_from_edit(GTK_ENTRY(entry), &cursor_pos)); + replace_address_in_edit(GTK_ENTRY(entry), prefix, cursor_pos); + g_free(prefix); + } + + clear_completion_cache(); + gtk_widget_destroy(*window); + *window = NULL; + + return TRUE; +} + +static gboolean completion_window_key_press(GtkWidget *widget, + GdkEventKey *event, + GtkWidget **window) +{ + GtkWidget *entry; + gchar *prefix; + gint cursor_pos; + GtkWidget *clist; + + g_return_val_if_fail(window != NULL, FALSE); + g_return_val_if_fail(*window != NULL, FALSE); + + if (!is_completion_pending()) + g_warning("completion is not pending!\n"); + + entry = GTK_WIDGET(g_object_get_data(G_OBJECT(*window), + WINDOW_DATA_COMPL_ENTRY)); + clist = GTK_WIDGET(g_object_get_data(G_OBJECT(*window), + WINDOW_DATA_COMPL_CLIST)); + g_return_val_if_fail(entry != NULL, FALSE); + + /* allow keyboard navigation in the alternatives clist */ + if (event->keyval == GDK_Up || event->keyval == GDK_Down || + event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down) { + completion_window_advance_selection + (GTK_CLIST(clist), + event->keyval == GDK_Down || + event->keyval == GDK_Page_Down ? TRUE : FALSE); + return FALSE; + } + + /* also make tab / shift tab go to next previous completion entry. we're + * changing the key value */ + if (event->keyval == GDK_Tab || event->keyval == GDK_ISO_Left_Tab) { + event->keyval = (event->state & GDK_SHIFT_MASK) + ? GDK_Up : GDK_Down; + /* need to reset shift state if going up */ + if (event->state & GDK_SHIFT_MASK) + event->state &= ~GDK_SHIFT_MASK; + completion_window_advance_selection(GTK_CLIST(clist), + event->keyval == GDK_Down ? TRUE : FALSE); + return FALSE; + } + + /* look for presses that accept the selection */ + if (event->keyval == GDK_Return || + (!prefs_common.fullauto_completion_mode && + event->keyval == GDK_space)) { + /* insert address only if shift or control is pressed */ + if (event->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK) || + prefs_common.always_add_address_only) { + completion_window_apply_selection_address_only + (GTK_CLIST(clist), GTK_ENTRY(entry)); + } + clear_completion_cache(); + gtk_widget_destroy(*window); + *window = NULL; + return FALSE; + } + + /* key state keys should never be handled */ + if (event->keyval == GDK_Shift_L + || event->keyval == GDK_Shift_R + || event->keyval == GDK_Control_L + || event->keyval == GDK_Control_R + || event->keyval == GDK_Caps_Lock + || event->keyval == GDK_Shift_Lock + || event->keyval == GDK_Meta_L + || event->keyval == GDK_Meta_R + || event->keyval == GDK_Alt_L + || event->keyval == GDK_Alt_R) { + return FALSE; + } + + /* other key, let's restore the prefix (orignal text) */ + if (!prefs_common.fullauto_completion_mode || + event->keyval == GDK_Escape) { + prefix = get_complete_address(0); + g_free(get_address_from_edit(GTK_ENTRY(entry), &cursor_pos)); + replace_address_in_edit(GTK_ENTRY(entry), prefix, cursor_pos); + g_free(prefix); + } + + /* make sure anything we typed comes in the edit box */ + if ((!prefs_common.fullauto_completion_mode && event->length > 0 && + event->keyval != GDK_Escape) || + (prefs_common.fullauto_completion_mode && + event->keyval != GDK_Escape)) { + GtkWidget *pwin = entry; + + while ((pwin = gtk_widget_get_parent(pwin)) != NULL) { + if (GTK_WIDGET_TOPLEVEL(pwin)) { + gtk_window_propagate_key_event + (GTK_WINDOW(pwin), event); + if (prefs_common.fullauto_completion_mode) + return TRUE; + } + } + } + + /* and close the completion window */ + clear_completion_cache(); + gtk_widget_destroy(*window); + *window = NULL; + + return TRUE; +} diff --git a/src/addr_compl.h b/src/addr_compl.h new file mode 100644 index 0000000..7eca78a --- /dev/null +++ b/src/addr_compl.h @@ -0,0 +1,54 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * + * Copyright (C) 2000-2004 by Alfons Hoogervorst & The Sylpheed Claws Team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __ADDR_COMPL_H__ +#define __ADDR_COMPL_H__ + +gint start_address_completion (void); +gint invalidate_address_completion (void); + +guint complete_address (const gchar *str); + +gchar *get_address_from_edit (GtkEntry *entry, + gint *start_pos); +void replace_address_in_edit (GtkEntry *entry, + const gchar *newtext, + gint start_pos); + +gchar *get_complete_address (gint index); + +gchar *get_next_complete_address (void); +gchar *get_prev_complete_address (void); +guint get_completion_count (void); + +gboolean is_completion_pending (void); + +void clear_completion_cache (void); + +gint end_address_completion (void); + +/* ui functions */ + +void address_completion_start (GtkWidget *mainwindow); +void address_completion_register_entry (GtkEntry *entry); +void address_completion_unregister_entry (GtkEntry *entry); +void address_completion_end (GtkWidget *mainwindow); + +#endif /* __ADDR_COMPL_H__ */ diff --git a/src/addrbook.c b/src/addrbook.c new file mode 100644 index 0000000..bcc4cab --- /dev/null +++ b/src/addrbook.c @@ -0,0 +1,2020 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * General functions for accessing external address book files. + */ + +#include +#include +#include +#include +#include +#include + +#include "xml.h" +#include "mgutils.h" +#include "addritem.h" +#include "addrcache.h" +#include "addrbook.h" +#include "utils.h" + +#ifndef DEV_STANDALONE +#include "prefs.h" +#include "codeconv.h" +#endif + +#define ADDRBOOK_MAX_SEARCH_COUNT 1000 +#define ADDRBOOK_PREFIX "addrbook-" +#define ADDRBOOK_SUFFIX ".xml" +#define FILE_NUMDIGITS 6 + +#define ID_TIME_OFFSET 998000000 +/* +* Create new address book. +*/ +AddressBookFile *addrbook_create_book() { + AddressBookFile *book; + + book = g_new0( AddressBookFile, 1 ); + book->name = NULL; + book->path = NULL; + book->fileName = NULL; + book->retVal = MGU_SUCCESS; + book->addressCache = addrcache_create(); + + book->tempList = NULL; + book->readFlag = FALSE; + book->dirtyFlag = FALSE; + book->modifyFlag = TRUE; + book->accessFlag = FALSE; + book->tempHash = NULL; + return book; +} + +/* +* Specify name to be used. +*/ +void addrbook_set_name( AddressBookFile *book, const gchar *value ) { + g_return_if_fail( book != NULL ); + book->name = mgu_replace_string( book->name, value ); +} +void addrbook_set_path( AddressBookFile *book, const gchar *value ) { + g_return_if_fail( book != NULL ); + book->path = mgu_replace_string( book->path, value ); + book->dirtyFlag = TRUE; +} +void addrbook_set_file( AddressBookFile *book, const gchar *value ) { + g_return_if_fail( book != NULL ); + book->fileName = mgu_replace_string( book->fileName, value ); + book->dirtyFlag = TRUE; +} +void addrbook_set_accessed( AddressBookFile *book, const gboolean value ) { + g_return_if_fail( book != NULL ); + book->accessFlag = value; +} +gboolean addrbook_get_modified( AddressBookFile *book ) { + g_return_val_if_fail( book != NULL, FALSE ); + return book->modifyFlag; +} +gboolean addrbook_get_accessed( AddressBookFile *book ) { + g_return_val_if_fail( book != NULL, FALSE ); + return book->accessFlag; +} +gboolean addrbook_get_read_flag( AddressBookFile *book ) { + g_return_val_if_fail( book != NULL, FALSE ); + return book->readFlag; +} +gint addrbook_get_status( AddressBookFile *book ) { + g_return_val_if_fail( book != NULL, -1 ); + return book->retVal; +} +ItemFolder *addrbook_get_root_folder( AddressBookFile *book ) { + g_return_val_if_fail( book != NULL, NULL ); + return addrcache_get_root_folder( book->addressCache ); +} +GList *addrbook_get_list_folder( AddressBookFile *book ) { + g_return_val_if_fail( book != NULL, NULL ); + return addrcache_get_list_folder( book->addressCache ); +} +GList *addrbook_get_list_person( AddressBookFile *book ) { + g_return_val_if_fail( book != NULL, NULL ); + return addrcache_get_list_person( book->addressCache ); +} +gchar *addrbook_get_name( AddressBookFile *book ) { + g_return_val_if_fail( book != NULL, NULL ); + return book->name; +} + +/* +* Empty address book. +*/ +void addrbook_empty_book( AddressBookFile *book ) { + g_return_if_fail( book != NULL ); + + /* Free up folders and hash table */ + addrcache_clear( book->addressCache ); + + g_list_free( book->tempList ); + book->tempList = NULL; + + /* Reset to initial state */ + book->retVal = MGU_SUCCESS; + book->tempHash = NULL; + book->readFlag = FALSE; + book->dirtyFlag = FALSE; + book->modifyFlag = FALSE; + book->accessFlag = FALSE; +} + +/* +* Free address book. +*/ +void addrbook_free_book( AddressBookFile *book ) { + g_return_if_fail( book != NULL ); + + g_free( book->name ); + g_free( book->path ); + g_free( book->fileName ); + book->name = NULL; + book->path = NULL; + book->fileName = NULL; + + /* Free up folders and hash table */ + addrcache_free( book->addressCache ); + book->addressCache = NULL; + + g_list_free( book->tempList ); + book->tempList = NULL; + + book->retVal = MGU_SUCCESS; + book->tempHash = NULL; + book->readFlag = FALSE; + book->dirtyFlag = FALSE; + book->modifyFlag = FALSE; + book->accessFlag = FALSE; + + g_free( book ); +} + +/* +* Print list of items. +*/ +void addrbook_print_item_list( GList *list, FILE *stream ) { + GList *node = list; + + while( node ) { + AddrItemObject *obj = node->data; + if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) { + addritem_print_item_person( ( ItemPerson * ) obj, stream ); + } + else if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) { + addritem_print_item_group( ( ItemGroup * ) obj, stream ); + } + else if( ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER ) { + addritem_print_item_folder( ( ItemFolder * ) obj, stream ); + } + node = g_list_next( node ); + } + fprintf( stream, "\t---\n" ); +} + +/* +* Print address book. +*/ +void addrbook_print_book( AddressBookFile *book, FILE *stream ) { + g_return_if_fail( book != NULL ); + + fprintf( stream, "AddressBook:\n" ); + fprintf( stream, "\tname : '%s'\n", book->name ); + fprintf( stream, "\tpath : '%s'\n", book->path ); + fprintf( stream, "\tfile : '%s'\n", book->fileName ); + fprintf( stream, "\tstatus: %d : '%s'\n", book->retVal, mgu_error2string( book->retVal ) ); + addrcache_print( book->addressCache, stream ); +} + +/* +* Dump entire address book traversing folders. +*/ +void addrbook_dump_book( AddressBookFile *book, FILE *stream ) { + ItemFolder *folder; + + g_return_if_fail( book != NULL ); + + addrbook_print_book( book, stream ); + folder = book->addressCache->rootFolder; + addritem_print_item_folder( folder, stream ); +} + +/* +* Remove group from address book. +* param: group Group to remove. +* return: Group, or NULL if not found. Note that object should still be freed. +*/ +ItemGroup *addrbook_remove_group( AddressBookFile *book, ItemGroup *group ) { + ItemGroup *item; + + g_return_val_if_fail( book != NULL, NULL ); + + item = addrcache_remove_group( book->addressCache, group ); + if( item ) book->dirtyFlag = TRUE; + return item; +} + +/* +* Remove specified person from address book. +* param: person Person to remove. +* return: Person, or NULL if not found. Note that object should still be freed. +*/ +ItemPerson *addrbook_remove_person( AddressBookFile *book, ItemPerson *person ) { + ItemPerson *item; + + g_return_val_if_fail( book != NULL, NULL ); + + item = addrcache_remove_person( book->addressCache, person ); + if( item ) book->dirtyFlag = TRUE; + return item; +} + +/* +* Remove email address in address book for specified person. +* param: person Person. +* email EMail to remove. +* return: EMail object, or NULL if not found. Note that object should still be freed. +*/ +ItemEMail *addrbook_person_remove_email( AddressBookFile *book, ItemPerson *person, ItemEMail *email ) { + ItemEMail *item; + + g_return_val_if_fail( book != NULL, NULL ); + + item = addrcache_person_remove_email( book->addressCache, person, email ); + if( item ) book->dirtyFlag = TRUE; + return item; +} + +ItemEMail *addrbook_group_remove_email( AddressBookFile *book, ItemGroup *group, ItemEMail *email ) { + ItemEMail *item; + + g_return_val_if_fail( book != NULL, NULL ); + + item = addritem_group_remove_email( group, email ); + if( item ) book->dirtyFlag = TRUE; + return item; +} + +/* ********************************************************************** +* Read/Write XML data file... +* =========================== +* Notes: +* 1) The address book is structured as follows: +* +* address-book +* person +* address-list +* address +* attribute-list +* attribute +* group +* member-list +* member +* folder +* item-list +* item +* +* 2) This sequence of elements was chosen so that the most important +* elements (person and their email addresses) appear first. +* +* 3) Groups then appear. When groups are loaded, person's email +* addresses have already been loaded and can be found. +* +* 4) Finally folders are loaded. Any forward and backward references +* to folders, groups and persons in the folders are resolved after +* loading. +* +* *********************************************************************** +*/ + +/* Element tag names */ +#define AB_ELTAG_ADDRESS "address" +#define AB_ELTAG_ATTRIBUTE "attribute" +#define AB_ELTAG_ATTRIBUTE_LIST "attribute-list" +#define AB_ELTAG_ADDRESS_LIST "address-list" +#define AB_ELTAG_MEMBER "member" +#define AB_ELTAG_MEMBER_LIST "member-list" +#define AB_ELTAG_ITEM "item" +#define AB_ELTAG_ITEM_LIST "item-list" +#define AB_ELTAG_ADDRESS_BOOK "address-book" +#define AB_ELTAG_PERSON "person" +#define AB_ELTAG_GROUP "group" +#define AB_ELTAG_FOLDER "folder" + +/* Attribute tag names */ +#define AB_ATTAG_TYPE "type" +#define AB_ATTAG_UID "uid" +#define AB_ATTAG_NAME "name" +#define AB_ATTAG_REMARKS "remarks" +#define AB_ATTAG_FIRST_NAME "first-name" +#define AB_ATTAG_LAST_NAME "last-name" +#define AB_ATTAG_NICK_NAME "nick-name" +#define AB_ATTAG_COMMON_NAME "cn" +#define AB_ATTAG_ALIAS "alias" +#define AB_ATTAG_EMAIL "email" +#define AB_ATTAG_EID "eid" +#define AB_ATTAG_PID "pid" + +/* Attribute values */ +#define AB_ATTAG_VAL_PERSON "person" +#define AB_ATTAG_VAL_GROUP "group" +#define AB_ATTAG_VAL_FOLDER "folder" + +/* +* Parse address item for person. +*/ +static void addrbook_parse_address( AddressBookFile *book, XMLFile *file, ItemPerson *person ) { + GList *attr; + gchar *name, *value; + ItemEMail *email = NULL; + + attr = xml_get_current_tag_attr(file); + while( attr ) { + name = ((XMLAttr *)attr->data)->name; + value = ((XMLAttr *)attr->data)->value; + if( ! email ) email = addritem_create_item_email(); + if( strcmp( name, AB_ATTAG_UID ) == 0 ) { + ADDRITEM_ID(email) = g_strdup( value ); + } + else if( strcmp( name, AB_ATTAG_ALIAS ) == 0 ) { + ADDRITEM_NAME(email) = g_strdup( value ); + } + else if( strcmp( name, AB_ATTAG_EMAIL ) == 0 ) { + email->address = g_strdup( value ); + } + else if( strcmp( name, AB_ATTAG_REMARKS ) == 0 ) { + email->remarks = g_strdup( value ); + } + attr = g_list_next( attr ); + } + if( email ) { + if( person ) { + addrcache_person_add_email( book->addressCache, person, email ); + } + else { + addritem_free_item_email( email ); + email = NULL; + } + } +} + +/* +* Parse email address list. +*/ +static void addrbook_parse_addr_list( AddressBookFile *book, XMLFile *file, ItemPerson *person ){ + GList *attr; + guint prev_level; + + for (;;) { + prev_level = file->level; + if( xml_parse_next_tag( file ) ) { + longjmp( book->jumper, 1 ); + } + if (file->level < prev_level) return; + if( xml_compare_tag( file, AB_ELTAG_ADDRESS ) ) { + attr = xml_get_current_tag_attr(file); + addrbook_parse_address( book, file, person ); + addrbook_parse_addr_list( book, file, person ); + } + } +} + +/* +* Parse attibute for person. +*/ +static void addrbook_parse_attribute( XMLFile *file, ItemPerson *person ) { + GList *attr; + gchar *name, *value; + gchar *element; + UserAttribute *uAttr = NULL; + + attr = xml_get_current_tag_attr(file); + while( attr ) { + name = ((XMLAttr *)attr->data)->name; + value = ((XMLAttr *)attr->data)->value; + if( ! uAttr ) uAttr = addritem_create_attribute(); + if( strcmp( name, AB_ATTAG_UID ) == 0 ) { + addritem_attrib_set_id( uAttr, value ); + } + else if( strcmp( name, AB_ATTAG_NAME ) == 0 ) { + addritem_attrib_set_name( uAttr, value ); + } + attr = g_list_next( attr ); + } + + element = xml_get_element( file ); + addritem_attrib_set_value( uAttr, element ); + + if( uAttr ) { + if( person ) { + addritem_person_add_attribute( person, uAttr ); + } + else { + addritem_free_attribute( uAttr ); + uAttr = NULL; + } + } +} + +/* +* Parse attribute list. +*/ +static void addrbook_parse_attr_list( AddressBookFile *book, XMLFile *file, ItemPerson *person ){ + GList *attr; + guint prev_level; + + for (;;) { + prev_level = file->level; + if( xml_parse_next_tag( file ) ) { + longjmp( book->jumper, 1 ); + } + if (file->level < prev_level) return; + if( xml_compare_tag( file, AB_ELTAG_ATTRIBUTE ) ) { + attr = xml_get_current_tag_attr(file); + addrbook_parse_attribute( file, person ); + addrbook_parse_attr_list( book, file, person ); + } + } +} + +/* +* Parse person. +*/ +static void addrbook_parse_person( AddressBookFile *book, XMLFile *file ) { + GList *attr; + gchar *name, *value; + ItemPerson *person = NULL; + + attr = xml_get_current_tag_attr(file); + while( attr ) { + name = ((XMLAttr *)attr->data)->name; + value = ((XMLAttr *)attr->data)->value; + if( ! person ) person = addritem_create_item_person(); + if( strcmp( name, AB_ATTAG_UID ) == 0 ) { + ADDRITEM_ID(person) = g_strdup( value ); + } + else if( strcmp( name, AB_ATTAG_FIRST_NAME ) == 0 ) { + person->firstName = g_strdup( value ); + } + else if( strcmp( name, AB_ATTAG_LAST_NAME ) == 0 ) { + person->lastName = g_strdup( value ); + } + else if( strcmp( name, AB_ATTAG_NICK_NAME ) == 0 ) { + person->nickName = g_strdup( value ); + } + else if( strcmp( name, AB_ATTAG_COMMON_NAME ) == 0 ) { + ADDRITEM_NAME(person) = g_strdup( value ); + } + attr = g_list_next( attr ); + } + if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ + longjmp( book->jumper, 1 ); + } + if( xml_compare_tag( file, AB_ELTAG_ADDRESS_LIST ) ) { + addrbook_parse_addr_list( book, file, person ); + if( person ) { + addrcache_hash_add_person( book->addressCache, person ); + } + } + if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ + longjmp( book->jumper, 1 ); + } + if( xml_compare_tag( file, AB_ELTAG_ATTRIBUTE_LIST ) ) { + addrbook_parse_attr_list( book, file, person ); + } +} + +/* +* Parse group member. +*/ +static void addrbook_parse_member( AddressBookFile *book, XMLFile *file, ItemGroup *group ) { + GList *attr; + gchar *name, *value; + gchar *pid = NULL, *eid = NULL; + ItemEMail *email = NULL; + + attr = xml_get_current_tag_attr(file); + while( attr ) { + name = ((XMLAttr *)attr->data)->name; + value = ((XMLAttr *)attr->data)->value; + if( strcmp( name, AB_ATTAG_PID ) == 0 ) { + pid = g_strdup( value ); + } + else if( strcmp( name, AB_ATTAG_EID ) == 0 ) { + eid = g_strdup( value ); + } + attr = g_list_next( attr ); + } + email = addrcache_get_email( book->addressCache, pid, eid ); + if( email ) { + if( group ) { + addrcache_group_add_email( book->addressCache, group, email ); + } + else { + addritem_free_item_email( email ); + email = NULL; + } + } +} + +/* +* Parse group member list. +*/ +static void addrbook_parse_member_list( AddressBookFile *book, XMLFile *file, ItemGroup *group ){ + GList *attr; + guint prev_level; + + for (;;) { + prev_level = file->level; + if( xml_parse_next_tag( file ) ) { + longjmp( book->jumper, 1 ); + } + if (file->level < prev_level) return; + if( xml_compare_tag( file, AB_ELTAG_MEMBER ) ) { + attr = xml_get_current_tag_attr(file); + addrbook_parse_member( book, file, group ); + addrbook_parse_member_list( book, file, group ); + } + else { + attr = xml_get_current_tag_attr( file ); + } + } +} + +/* +* Parse group. +*/ +static void addrbook_parse_group( AddressBookFile *book, XMLFile *file ) { + GList *attr; + gchar *name, *value; + ItemGroup *group = NULL; + + attr = xml_get_current_tag_attr(file); + while( attr ) { + name = ((XMLAttr *)attr->data)->name; + value = ((XMLAttr *)attr->data)->value; + if( ! group ) group = addritem_create_item_group(); + if( strcmp( name, AB_ATTAG_UID ) == 0 ) { + ADDRITEM_ID(group) = g_strdup( value ); + } + else if( strcmp( name, AB_ATTAG_NAME ) == 0 ) { + ADDRITEM_NAME(group) = g_strdup( value ); + } + else if( strcmp( name, AB_ATTAG_REMARKS ) == 0 ) { + group->remarks = g_strdup( value ); + } + attr = g_list_next( attr ); + } + if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ + longjmp( book->jumper, 1 ); + } + if( xml_compare_tag( file, AB_ELTAG_MEMBER_LIST ) ) { + if( group ) { + addrcache_hash_add_group( book->addressCache, group ); + } + addrbook_parse_member_list( book, file, group ); + } +} + +/* +* Parse folder item. +*/ +static void addrbook_parse_folder_item( AddressBookFile *book, XMLFile *file, ItemFolder *folder ) { + GList *attr; + gchar *name, *value; + gchar *uid = NULL; + + attr = xml_get_current_tag_attr(file); + while( attr ) { + name = ((XMLAttr *)attr->data)->name; + value = ((XMLAttr *)attr->data)->value; + if( strcmp( name, AB_ATTAG_UID ) == 0 ) { + uid = g_strdup( value ); + } + attr = g_list_next( attr ); + } + if( folder ) { + if( uid ) { + folder->listItems = g_list_append( folder->listItems, uid ); + } + } +} + +/* +* Parse folder item list. +*/ +static void addrbook_parse_folder_list( AddressBookFile *book, XMLFile *file, ItemFolder *folder ){ + GList *attr; + guint prev_level; + + for (;;) { + prev_level = file->level; + if( xml_parse_next_tag( file ) ) { + longjmp( book->jumper, 1 ); + } + if (file->level < prev_level) return; + if( xml_compare_tag( file, AB_ELTAG_ITEM ) ) { + attr = xml_get_current_tag_attr(file); + addrbook_parse_folder_item( book, file, folder ); + addrbook_parse_folder_list( book, file, folder ); + } + else { + attr = xml_get_current_tag_attr( file ); + } + } +} + +/* +* Parse folder. +*/ +static void addrbook_parse_folder( AddressBookFile *book, XMLFile *file ) { + GList *attr; + gchar *name, *value; + ItemFolder *folder = NULL; + + attr = xml_get_current_tag_attr(file); + while( attr ) { + name = ((XMLAttr *)attr->data)->name; + value = ((XMLAttr *)attr->data)->value; + if( ! folder ) { + folder = addritem_create_item_folder(); + } + if( strcmp( name, AB_ATTAG_UID ) == 0 ) { + ADDRITEM_ID(folder) = g_strdup( value ); + } + else if( strcmp( name, AB_ATTAG_NAME ) == 0 ) { + ADDRITEM_NAME(folder) = g_strdup( value ); + } + else if( strcmp( name, AB_ATTAG_REMARKS ) == 0 ) { + folder->remarks = g_strdup( value ); + } + attr = g_list_next( attr ); + } + if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ + longjmp( book->jumper, 1 ); + } + if( xml_compare_tag( file, AB_ELTAG_ITEM_LIST ) ) { + if( folder ) { + if( addrcache_hash_add_folder( book->addressCache, folder ) ) { + book->tempList = g_list_append( book->tempList, folder ); + ADDRITEM_PARENT(folder) = NULL; /* We will resolve folder later */ + } + } + addrbook_parse_folder_list( book, file, folder ); + } +} + +/* +* Parse address book. +* Return: TRUE if data read successfully, FALSE if error reading data. +*/ +static gboolean addrbook_read_tree( AddressBookFile *book, XMLFile *file ) { + gboolean retVal; + GList *attr; + gchar *name, *value; + + book->retVal = MGU_BAD_FORMAT; + if( xml_get_dtd( file ) ) { + return FALSE; + } + if( xml_parse_next_tag( file ) ) { + longjmp( book->jumper, 1 ); + } + if( ! xml_compare_tag( file, AB_ELTAG_ADDRESS_BOOK ) ) { + return FALSE; + } + + attr = xml_get_current_tag_attr(file); + while( attr ) { + name = ((XMLAttr *)attr->data)->name; + value = ((XMLAttr *)attr->data)->value; + if( strcmp( name, AB_ATTAG_NAME ) == 0 ) { + addrbook_set_name( book, value ); + } + attr = g_list_next( attr ); + } + + retVal = TRUE; + for (;;) { + if (! file->level ) break; + /* Get next item tag (person, group or folder) */ + if( xml_parse_next_tag( file ) ) { + longjmp( book->jumper, 1 ); + } + if( xml_compare_tag( file, AB_ELTAG_PERSON ) ) { + addrbook_parse_person( book, file ); + } + else if( xml_compare_tag( file, AB_ELTAG_GROUP ) ) { + addrbook_parse_group( book, file ); + } + else if( xml_compare_tag( file, AB_ELTAG_FOLDER ) ) { + addrbook_parse_folder( book, file ); + } + } + if( retVal ) book->retVal = MGU_SUCCESS; + return retVal; +} + +/* +* Resolve folder items visitor function. +*/ +static void addrbook_res_items_vis( gpointer key, gpointer value, gpointer data ) { + AddressBookFile *book = data; + AddrItemObject *obj = ( AddrItemObject * ) value; + ItemFolder *rootFolder = book->addressCache->rootFolder; + if( obj->parent == NULL ) { + if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) { + rootFolder->listPerson = g_list_append( rootFolder->listPerson, obj ); + ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder); + } + else if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) { + rootFolder->listGroup = g_list_append( rootFolder->listGroup, obj ); + ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder); + } + } +} + +/* +* Resolve folder items. Lists of UID's are replaced with pointers to data items. +*/ +static void addrbook_resolve_folder_items( AddressBookFile *book ) { + GList *nodeFolder = NULL; + GList *listRemove = NULL; + GList *node = NULL; + ItemFolder *rootFolder = book->addressCache->rootFolder; + nodeFolder = book->tempList; + while( nodeFolder ) { + ItemFolder *folder = nodeFolder->data; + listRemove = NULL; + node = folder->listItems; + while( node ) { + gchar *uid = node->data; + AddrItemObject *aio = addrcache_get_object( book->addressCache, uid ); + if( aio ) { + if( aio->type == ITEMTYPE_FOLDER ) { + ItemFolder *item = ( ItemFolder * ) aio; + folder->listFolder = g_list_append( folder->listFolder, item ); + ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder); + addrcache_hash_add_folder( book->addressCache, folder ); + } + else if( aio->type == ITEMTYPE_PERSON ) { + ItemPerson *item = ( ItemPerson * ) aio; + folder->listPerson = g_list_append( folder->listPerson, item ); + ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder); + } + else if( aio->type == ITEMTYPE_GROUP ) { + ItemGroup *item = ( ItemGroup * ) aio; + folder->listGroup = g_list_append( folder->listGroup, item ); + ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder); + } + /* Replace data with pointer to item */ + g_free( uid ); + node->data = aio; + } + else { + /* Not found, append to remove list. */ + listRemove = g_list_append( listRemove, uid ); + } + node = g_list_next( node ); + } + rootFolder->listFolder = g_list_append( rootFolder->listFolder, folder ); + + /* Process remove list */ + node = listRemove; + while( node ) { + gchar *uid = node->data; + folder->listItems = g_list_remove( folder->listItems, uid ); + g_free( uid ); + node = g_list_next( node ); + } + g_list_free( listRemove ); + nodeFolder = g_list_next( nodeFolder ); + } + + /* Remove folders with parents. */ + listRemove = NULL; + node = rootFolder->listFolder; + while( node ) { + ItemFolder *folder = ( ItemFolder * ) node->data; + if( ADDRITEM_PARENT(folder) ) { + /* Remove folders with parents */ + listRemove = g_list_append( listRemove, folder ); + } + else { + /* Add to root folder */ + ADDRITEM_PARENT(folder) = ADDRITEM_OBJECT(book->addressCache->rootFolder); + } + node = g_list_next( node ); + } + + /* Process remove list */ + node = listRemove; + while( node ) { + rootFolder->listFolder = g_list_remove( rootFolder->listFolder, node->data ); + node = g_list_next( node ); + } + g_list_free( listRemove ); + + /* Move all unparented persons and groups into root folder */ + g_hash_table_foreach( book->addressCache->itemHash, addrbook_res_items_vis, book ); + + /* Free up some more */ + nodeFolder = book->tempList; + while( nodeFolder ) { + ItemFolder *folder = nodeFolder->data; + g_list_free( folder->listItems ); + folder->listItems = NULL; + nodeFolder = g_list_next( nodeFolder ); + } + g_list_free( book->tempList ); + book->tempList = NULL; + +} + +/* +* Read address book file. +*/ +gint addrbook_read_data( AddressBookFile *book ) { + XMLFile *file = NULL; + gchar *fileSpec = NULL; + + g_return_val_if_fail( book != NULL, -1 ); + + fileSpec = g_strconcat( book->path, G_DIR_SEPARATOR_S, book->fileName, NULL ); + book->retVal = MGU_OPEN_FILE; + book->accessFlag = FALSE; + book->modifyFlag = FALSE; + file = xml_open_file( fileSpec ); + g_free( fileSpec ); + if( file ) { + book->tempList = NULL; + + /* Trap for parsing errors. */ + if( setjmp( book->jumper ) ) { + xml_close_file( file ); + return book->retVal; + } + addrbook_read_tree( book, file ); + xml_close_file( file ); + + /* Resolve folder items */ + addrbook_resolve_folder_items( book ); + book->tempList = NULL; + book->readFlag = TRUE; + book->dirtyFlag = FALSE; + } + return book->retVal; +} + +static void addrbook_write_elem_s( FILE *fp, gint lvl, gchar *name ) { + gint i; + for( i = 0; i < lvl; i++ ) fputs( " ", fp ); + fputs( "<", fp ); + fputs( name, fp ); +} + +static void addrbook_write_elem_e( FILE *fp, gint lvl, gchar *name ) { + gint i; + for( i = 0; i < lvl; i++ ) fputs( " ", fp ); + fputs( "\n", fp ); +} + +static void addrbook_write_attr( FILE *fp, gchar *name, gchar *value ) { + fputs( " ", fp ); + fputs( name, fp ); + fputs( "=\"", fp ); + xml_file_put_escape_str( fp, value ); + fputs( "\"", fp ); +} + +/* +* Write file hash table visitor function. +*/ +static void addrbook_write_item_person_vis( gpointer key, gpointer value, gpointer data ) { + AddrItemObject *obj = ( AddrItemObject * ) value; + FILE *fp = ( FILE * ) data; + GList *node; + + if( ! obj ) return; + if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) { + ItemPerson *person = ( ItemPerson * ) value; + if( person ) { + addrbook_write_elem_s( fp, 1, AB_ELTAG_PERSON ); + addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(person) ); + addrbook_write_attr( fp, AB_ATTAG_FIRST_NAME, person->firstName ); + addrbook_write_attr( fp, AB_ATTAG_LAST_NAME, person->lastName ); + addrbook_write_attr( fp, AB_ATTAG_NICK_NAME, person->nickName ); + addrbook_write_attr( fp, AB_ATTAG_COMMON_NAME, ADDRITEM_NAME(person) ); + fputs( " >\n", fp); + + /* Output email addresses */ + addrbook_write_elem_s( fp, 2, AB_ELTAG_ADDRESS_LIST ); + fputs( ">\n", fp ); + node = person->listEMail; + while ( node ) { + ItemEMail *email = node->data; + addrbook_write_elem_s( fp, 3, AB_ELTAG_ADDRESS ); + addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(email) ); + addrbook_write_attr( fp, AB_ATTAG_ALIAS, ADDRITEM_NAME(email) ); + addrbook_write_attr( fp, AB_ATTAG_EMAIL, email->address ); + addrbook_write_attr( fp, AB_ATTAG_REMARKS, email->remarks ); + fputs( " />\n", fp); + node = g_list_next( node ); + } + addrbook_write_elem_e( fp, 2, AB_ELTAG_ADDRESS_LIST ); + + /* Output user attributes */ + addrbook_write_elem_s( fp, 2, AB_ELTAG_ATTRIBUTE_LIST ); + fputs( ">\n", fp ); + node = person->listAttrib; + while ( node ) { + UserAttribute *attrib = node->data; + addrbook_write_elem_s( fp, 3, AB_ELTAG_ATTRIBUTE ); + addrbook_write_attr( fp, AB_ATTAG_UID, attrib->uid ); + addrbook_write_attr( fp, AB_ATTAG_NAME, attrib->name ); + fputs( " >", fp); + xml_file_put_escape_str( fp, attrib->value ); + addrbook_write_elem_e( fp, 0, AB_ELTAG_ATTRIBUTE ); + node = g_list_next( node ); + } + addrbook_write_elem_e( fp, 2, AB_ELTAG_ATTRIBUTE_LIST ); + addrbook_write_elem_e( fp, 1, AB_ELTAG_PERSON ); + } + } +} + +/* +* Write file hash table visitor function. +*/ +static void addrbook_write_item_group_vis( gpointer key, gpointer value, gpointer data ) { + AddrItemObject *obj = ( AddrItemObject * ) value; + FILE *fp = ( FILE * ) data; + GList *node; + + if( ! obj ) return; + if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) { + ItemGroup *group = ( ItemGroup * ) value; + if( group ) { + addrbook_write_elem_s( fp, 1, AB_ELTAG_GROUP ); + addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(group) ); + addrbook_write_attr( fp, AB_ATTAG_NAME, ADDRITEM_NAME(group) ); + addrbook_write_attr( fp, AB_ATTAG_REMARKS, group->remarks ); + fputs( " >\n", fp ); + + /* Output email address links */ + addrbook_write_elem_s( fp, 2, AB_ELTAG_MEMBER_LIST ); + fputs( ">\n", fp ); + node = group->listEMail; + while ( node ) { + ItemEMail *email = node->data; + ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(email); + addrbook_write_elem_s( fp, 3, AB_ELTAG_MEMBER ); + addrbook_write_attr( fp, AB_ATTAG_PID, ADDRITEM_ID(person) ); + addrbook_write_attr( fp, AB_ATTAG_EID, ADDRITEM_ID(email) ); + fputs( " />\n", fp ); + node = g_list_next( node ); + } + addrbook_write_elem_e( fp, 2, AB_ELTAG_MEMBER_LIST ); + addrbook_write_elem_e( fp, 1, AB_ELTAG_GROUP ); + } + } +} + +/* +* Write file hash table visitor function. +*/ +static void addrbook_write_item_folder_vis( gpointer key, gpointer value, gpointer data ) { + AddrItemObject *obj = ( AddrItemObject * ) value; + FILE *fp = ( FILE * ) data; + GList *node; + + if( ! obj ) return; + if( ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER ) { + ItemFolder *folder = ( ItemFolder * ) value; + if( folder ) { + addrbook_write_elem_s( fp, 1, AB_ELTAG_FOLDER ); + addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(folder) ); + addrbook_write_attr( fp, AB_ATTAG_NAME, ADDRITEM_NAME(folder) ); + addrbook_write_attr( fp, AB_ATTAG_REMARKS, folder->remarks ); + fputs( " >\n", fp ); + addrbook_write_elem_s( fp, 2, AB_ELTAG_ITEM_LIST ); + fputs( ">\n", fp ); + + /* Output persons */ + node = folder->listPerson; + while ( node ) { + ItemPerson *item = node->data; + addrbook_write_elem_s( fp, 3, AB_ELTAG_ITEM ); + addrbook_write_attr( fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_PERSON ); + addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(item ) ); + fputs( " />\n", fp ); + node = g_list_next( node ); + } + + /* Output groups */ + node = folder->listGroup; + while ( node ) { + ItemGroup *item = node->data; + addrbook_write_elem_s( fp, 3, AB_ELTAG_ITEM ); + addrbook_write_attr( fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_GROUP ); + addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(item ) ); + fputs( " />\n", fp ); + node = g_list_next( node ); + } + + /* Output folders */ + node = folder->listFolder; + while ( node ) { + ItemFolder *item = node->data; + addrbook_write_elem_s( fp, 3, AB_ELTAG_ITEM ); + addrbook_write_attr( fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_FOLDER ); + addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(item ) ); + fputs( " />\n", fp ); + node = g_list_next( node ); + } + addrbook_write_elem_e( fp, 2, AB_ELTAG_ITEM_LIST ); + addrbook_write_elem_e( fp, 1, AB_ELTAG_FOLDER ); + } + } +} + +/* +* Output address book data to specified file. +* return: Status code. +*/ +gint addrbook_write_to( AddressBookFile *book, gchar *newFile ) { + FILE *fp; + gchar *fileSpec; +#ifndef DEV_STANDALONE + PrefFile *pfile; +#endif + + g_return_val_if_fail( book != NULL, -1 ); + g_return_val_if_fail( newFile != NULL, -1 ); + + fileSpec = g_strconcat( book->path, G_DIR_SEPARATOR_S, newFile, NULL ); + + book->retVal = MGU_OPEN_FILE; +#ifdef DEV_STANDALONE + fp = g_fopen( fileSpec, "wb" ); + g_free( fileSpec ); + if( fp ) { + fputs( "\n", fp ); +#else + pfile = prefs_file_open( fileSpec ); + g_free( fileSpec ); + if( pfile ) { + fp = pfile->fp; + fprintf( fp, "\n", CS_INTERNAL ); +#endif + addrbook_write_elem_s( fp, 0, AB_ELTAG_ADDRESS_BOOK ); + addrbook_write_attr( fp, AB_ATTAG_NAME, book->name ); + fputs( " >\n", fp ); + + /* Output all persons */ + g_hash_table_foreach( book->addressCache->itemHash, addrbook_write_item_person_vis, fp ); + + /* Output all groups */ + g_hash_table_foreach( book->addressCache->itemHash, addrbook_write_item_group_vis, fp ); + + /* Output all folders */ + g_hash_table_foreach( book->addressCache->itemHash, addrbook_write_item_folder_vis, fp ); + + addrbook_write_elem_e( fp, 0, AB_ELTAG_ADDRESS_BOOK ); + book->retVal = MGU_SUCCESS; +#ifdef DEV_STANDALONE + fclose( fp ); +#else + if( prefs_file_close( pfile ) < 0 ) { + book->retVal = MGU_ERROR_WRITE; + } +#endif + } + + fileSpec = NULL; + return book->retVal; +} + +/* +* Output address book data to original file. +* return: Status code. +*/ +gint addrbook_save_data( AddressBookFile *book ) { + g_return_val_if_fail( book != NULL, -1 ); + + book->retVal = MGU_NO_FILE; + if( book->fileName == NULL || *book->fileName == '\0' ) return book->retVal; + if( book->path == NULL || *book->path == '\0' ) return book->retVal; + + addrbook_write_to( book, book->fileName ); + if( book->retVal == MGU_SUCCESS ) { + book->dirtyFlag = FALSE; + } + return book->retVal; +} + +/* ********************************************************************** +* Address book edit interface functions... +* *********************************************************************** +*/ + +/* +* Move person's email item. +* param: book Address book. +* person Person. +* itemMove Item to move. +* itemTarget Target item before which to move item. +*/ +ItemEMail *addrbook_move_email_before( AddressBookFile *book, ItemPerson *person, + ItemEMail *itemMove, ItemEMail *itemTarget ) +{ + ItemEMail *email = NULL; + + g_return_val_if_fail( book != NULL, NULL ); + + email = addritem_move_email_before( person, itemMove, itemTarget ); + if( email ) { + book->dirtyFlag = TRUE; + } + return email; +} + +/* +* Move person's email item. +* param: book Address book. +* person Person. +* itemMove Item to move. +* itemTarget Target item after which to move item. +*/ +ItemEMail *addrbook_move_email_after( AddressBookFile *book, ItemPerson *person, + ItemEMail *itemMove, ItemEMail *itemTarget ) +{ + ItemEMail *email = NULL; + + g_return_val_if_fail( book != NULL, NULL ); + + email = addritem_move_email_after( person, itemMove, itemTarget ); + if( email ) { + book->dirtyFlag = TRUE; + } + return email; +} + +/* +* Hash table visitor function. +*/ +static gboolean addrbook_free_simple_hash_vis( gpointer *key, gpointer *value, gpointer *data ) { + g_free( key ); + key = NULL; + value = NULL; + return TRUE; +} + +/* +* Update address book email list for specified person. +* Enter: book Address book. +* person Person to update. +* listEMail New list of email addresses. +* Note: The existing email addresses are replaced with the new addresses. Any references +* to old addresses in the groups are re-linked to the new addresses. All old addresses +* linked to the person are removed. +*/ +void addrbook_update_address_list( AddressBookFile *book, ItemPerson *person, GList *listEMail ) { + GList *node; + GList *oldData; + GList *listGroup; + + g_return_if_fail( book != NULL ); + g_return_if_fail( person != NULL ); + + /* Remember old list */ + oldData = person->listEMail; + + /* Attach new address list to person. */ + node = listEMail; + while( node ) { + ItemEMail *email = node->data; + if( ADDRITEM_ID(email) == NULL ) { + /* Allocate an ID */ + addrcache_id_email( book->addressCache, email ); + } + ADDRITEM_PARENT(email) = ADDRITEM_OBJECT(person); + node = g_list_next( node ); + } + person->listEMail = listEMail; + + /* Get groups where person's email is listed */ + listGroup = addrcache_get_group_for_person( book->addressCache, person ); + if( listGroup ) { + GHashTable *hashEMail; + GList *nodeGrp; + + /* Load hash table with new address entries */ + hashEMail = g_hash_table_new( g_str_hash, g_str_equal ); + node = listEMail; + while( node ) { + ItemEMail *email = node->data; + gchar *addr = g_strdup( email->address ); + g_strdown( addr ); + if( ! g_hash_table_lookup( hashEMail, addr ) ) { + g_hash_table_insert( hashEMail, addr, email ); + } + node = g_list_next( node ); + } + + /* Re-parent new addresses to existing groups, where email address match. */ + nodeGrp = listGroup; + while( nodeGrp ) { + ItemGroup *group = ( ItemGroup * ) nodeGrp->data; + GList *groupEMail = group->listEMail; + GList *nodeGrpEM; + GList *listRemove = NULL; + + /* Process each email item linked to group */ + nodeGrpEM = groupEMail; + while( nodeGrpEM ) { + ItemEMail *emailGrp = ( ItemEMail * ) nodeGrpEM->data; + if( ADDRITEM_PARENT(emailGrp) == ADDRITEM_OBJECT(person) ) { + /* Found an email address for this person */ + ItemEMail *emailNew = NULL; + gchar *addr = g_strdup( emailGrp->address ); + g_strdown( addr ); + emailNew = ( ItemEMail * ) g_hash_table_lookup( hashEMail, addr ); + g_free( addr ); + if( emailNew ) { + /* Point to this entry */ + nodeGrpEM->data = emailNew; + } + else { + /* Mark for removal */ + listRemove = g_list_append( listRemove, emailGrp ); + } + } + /* Move on to next email link */ + nodeGrpEM = g_list_next( nodeGrpEM ); + } + + /* Process all removed links in current group */ + nodeGrpEM = listRemove; + while( nodeGrpEM ) { + ItemEMail *emailGrp = nodeGrpEM->data; + groupEMail = g_list_remove( groupEMail, emailGrp ); + nodeGrpEM = g_list_next( nodeGrpEM ); + } + group->listEMail = groupEMail; + + /* Move on to next group */ + nodeGrp = g_list_next( nodeGrp ); + + } + + /* Clear hash table */ + g_hash_table_foreach_remove( hashEMail, ( GHRFunc ) addrbook_free_simple_hash_vis, NULL ); + g_hash_table_destroy( hashEMail ); + hashEMail = NULL; + g_list_free( listGroup ); + listGroup = NULL; + } + + /* Free up old data */ + addritem_free_list_email( oldData ); + oldData = NULL; + book->dirtyFlag = TRUE; + +} + +/* +* Add person and address data to address book. +* Enter: book Address book. +* folder Folder where to add person, or NULL for root folder. +* listEMail New list of email addresses. +* Return: Person added. +* Note: A new person is created with specified list of email addresses. All objects inserted +* into address book. +*/ +ItemPerson *addrbook_add_address_list( AddressBookFile *book, ItemFolder *folder, GList *listEMail ) { + ItemPerson *person; + ItemFolder *f = folder; + GList *node; + + g_return_val_if_fail( book != NULL, NULL ); + + if( ! f ) f = book->addressCache->rootFolder; + person = addritem_create_item_person(); + addrcache_id_person( book->addressCache, person ); + addrcache_folder_add_person( book->addressCache, f, person ); + + node = listEMail; + while( node ) { + ItemEMail *email = node->data; + if( ADDRITEM_ID(email) == NULL ) { + addrcache_id_email( book->addressCache, email ); + } + addrcache_person_add_email( book->addressCache, person, email ); + node = g_list_next( node ); + } + book->dirtyFlag = TRUE; + return person; +} + +#if 0 +/* +* Load hash table visitor function. +*/ +static void addrbook_load_hash_table_email_vis( gpointer key, gpointer value, gpointer data ) { + AddrItemObject *obj = ( AddrItemObject * ) value; + + if( ADDRITEM_TYPE(obj) == ITEMTYPE_EMAIL ) { + GHashTable *table = ( GHashTable * ) data; + gchar *newKey = g_strdup( key ); + ItemEMail *email = ( ItemEMail * ) obj; + if( ! g_hash_table_lookup( table, newKey ) ) { + g_hash_table_insert( table, newKey, email ); + } + } +} + +/* +* Load hash table with links to email addresses. +*/ +static void addrbook_load_hash_table_email( AddressBookFile *book, GHashTable *table ) { + g_return_if_fail( book != NULL ); + g_return_if_fail( table != NULL ); + g_hash_table_foreach( book->addressCache->itemHash, addrbook_load_hash_table_email_vis, table ); +} +#endif + +/* +* Build available email list visitor function. +*/ +static void addrbook_build_avail_email_vis( gpointer key, gpointer value, gpointer data ) { + AddrItemObject *obj = ( AddrItemObject * ) value; + + if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) { + AddressBookFile *book = data; + ItemPerson *person = ( ItemPerson * ) obj; + GList *node = person->listEMail; + while( node ) { + ItemEMail *email = node->data; + /* gchar *newKey = g_strdup( ADDRITEM_ID(email) ); */ + + if( ! g_hash_table_lookup( book->tempHash, ADDRITEM_ID(email) ) ) { + book->tempList = g_list_append( book->tempList, email ); + } + node = g_list_next( node ); + } + } +} + +/* +* Return link list of available email items (which have not already been linked to +* groups). Note that the list contains references to items and should be g_free() +* when done. Do *NOT* attempt to used the addrbook_free_xxx() functions... this will +* destroy the addressbook data! +* Return: List of items, or NULL if none. +*/ +GList *addrbook_get_available_email_list( AddressBookFile *book, ItemGroup *group ) { + GList *list = NULL; + GHashTable *table; + + g_return_val_if_fail( book != NULL, NULL ); + + /* Load hash table with group email entries */ + table = g_hash_table_new( g_str_hash, g_str_equal ); + if( group ) { + list = group->listEMail; + while( list ) { + ItemEMail *email = list->data; + g_hash_table_insert( table, ADDRITEM_ID(email), email ); + list = g_list_next( list ); + } + } + + /* Build list of available email addresses which exclude those already in groups */ + book->tempList = NULL; + book->tempHash = table; + g_hash_table_foreach( book->addressCache->itemHash, addrbook_build_avail_email_vis, book ); + list = book->tempList; + book->tempList = NULL; + book->tempHash = NULL; + + /* Clear hash table */ + g_hash_table_destroy( table ); + table = NULL; + + return list; +} + +/* +* Update address book email list for specified group. +* Enter: book Address book. +* group group to update. +* listEMail New list of email addresses. This should *NOT* be g_free() when done. +* Note: The existing email addresses are replaced with the new addresses. Any references +* to old addresses in the groups are re-linked to the new addresses. All old addresses +* linked to the person are removed. +*/ +void addrbook_update_group_list( AddressBookFile *book, ItemGroup *group, GList *listEMail ) { + GList *oldData; + + g_return_if_fail( book != NULL ); + g_return_if_fail( group != NULL ); + + /* Remember old list */ + oldData = group->listEMail; + group->listEMail = listEMail; + mgu_clear_list( oldData ); + g_list_free ( oldData ); + book->dirtyFlag = TRUE; +} + +/* +* Add group and email list to address book. +* Enter: book Address book. +* folder Parent folder, or NULL for root folder. +* listEMail New list of email addresses. This should *NOT* be g_free() when done. +* Return: Group object. +* Note: The existing email addresses are replaced with the new addresses. Any references +* to old addresses in the groups are re-linked to the new addresses. All old addresses +* linked to the person are removed. +*/ +ItemGroup *addrbook_add_group_list( AddressBookFile *book, ItemFolder *folder, GList *listEMail ) { + ItemGroup *group = NULL; + ItemFolder *f = folder; + + g_return_val_if_fail( book != NULL, NULL ); + + if( ! f ) f = book->addressCache->rootFolder; + group = addritem_create_item_group(); + addrcache_id_group( book->addressCache, group ); + addrcache_folder_add_group( book->addressCache, f, group ); + group->listEMail = listEMail; + book->dirtyFlag = TRUE; + return group; +} + +/* +* Add new folder to address book. +* Enter: book Address book. +* parent Parent folder. +* Return: Folder that was added. This should *NOT* be g_free() when done. +*/ +ItemFolder *addrbook_add_new_folder( AddressBookFile *book, ItemFolder *parent ) { + ItemFolder *folder = NULL; + ItemFolder *p = parent; + + g_return_val_if_fail( book != NULL, NULL ); + + if( ! p ) p = book->addressCache->rootFolder; + folder = addritem_create_item_folder(); + addrcache_id_folder( book->addressCache, folder ); + if( addrcache_hash_add_folder( book->addressCache, folder ) ) { + p->listFolder = g_list_append( p->listFolder, folder ); + ADDRITEM_PARENT(folder) = ADDRITEM_OBJECT(p); + book->dirtyFlag = TRUE; + } + else { + addritem_free_item_folder( folder ); + folder = NULL; + } + return folder; +} + +/* +* Update address book attribute list for specified person. +* Enter: book Address book. +* person Person to update. +* listAttrib New list of attributes. +* Note: The existing email addresses are replaced with the new addresses. All old attributes +* linked to the person are removed. +*/ +void addrbook_update_attrib_list( AddressBookFile *book, ItemPerson *person, GList *listAttrib ) { + GList *node; + GList *oldData; + + g_return_if_fail( book != NULL ); + g_return_if_fail( person != NULL ); + + /* Remember old list */ + oldData = person->listAttrib; + + /* Attach new address list to person. */ + node = listAttrib; + while( node ) { + UserAttribute *attrib = node->data; + if( attrib->uid == NULL ) { + /* Allocate an ID */ + addrcache_id_attribute( book->addressCache, attrib ); + } + node = g_list_next( node ); + } + person->listAttrib = listAttrib; + + /* Free up old data */ + addritem_free_list_attribute( oldData ); + oldData = NULL; + book->dirtyFlag = TRUE; + +} + +/* +* Add attribute data for person to address book. +* Enter: book Address book. +* person New person object. +* listAttrib New list of attributes. +* Note: Only attributes are inserted into address book. +*/ +void addrbook_add_attrib_list( AddressBookFile *book, ItemPerson *person, GList *listAttrib ) { + GList *node; + + g_return_if_fail( book != NULL ); + g_return_if_fail( person != NULL ); + + node = listAttrib; + while( node ) { + UserAttribute *attrib = node->data; + if( attrib->uid == NULL ) { + addrcache_id_attribute( book->addressCache, attrib ); + } + addritem_person_add_attribute( person, attrib ); + node = g_list_next( node ); + } + book->dirtyFlag = TRUE; +} + +/* +* Return address book file for specified object. +* Enter: aio Book item object. +* Return: Address book, or NULL if not found. +*/ +AddressBookFile *addrbook_item_get_bookfile( AddrItemObject *aio ) { + AddressBookFile *book = NULL; + + if( aio ) { + ItemFolder *parent = NULL; + ItemFolder *root = NULL; + if( aio->type == ITEMTYPE_EMAIL ) { + ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(aio); + if( person ) { + parent = ( ItemFolder * ) ADDRITEM_PARENT(person); + } + } + else { + parent = ( ItemFolder * ) ADDRITEM_PARENT(aio); + } + if( parent ) { + root = addrcache_find_root_folder( parent ); + } + if( root ) { + book = ( AddressBookFile * ) ADDRITEM_PARENT(root); + } + } + return book; +} + +/* +* Remove folder from address book. Children are re-parented to parent folder. +* param: folder Folder to remove. +* return: Folder, or NULL if not found. Note that object should still be freed. +*/ +ItemFolder *addrbook_remove_folder( AddressBookFile *book, ItemFolder *folder ) { + ItemFolder *f; + + g_return_val_if_fail( book != NULL, NULL ); + + f = addrcache_remove_folder( book->addressCache, folder ); + if( f ) book->dirtyFlag = TRUE; + return f; +} + +/* +* Remove folder from address book. Children are deleted. +* param: folder Folder to remove. +* return: Folder, or NULL if not found. Note that object should still be freed. +*/ +ItemFolder *addrbook_remove_folder_delete( AddressBookFile *book, ItemFolder *folder ) { + ItemFolder *f; + + g_return_val_if_fail( book != NULL, NULL ); + + f = addrcache_remove_folder_delete( book->addressCache, folder ); + if( f ) book->dirtyFlag = TRUE; + return f; +} + +#define WORK_BUFLEN 1024 +#define ADDRBOOK_DIGITS "0123456789" + +/* +* Return list of existing address book files. +* Enter: book Address book file. +* Return: File list. +*/ +GList *addrbook_get_bookfile_list( AddressBookFile *book ) { + gchar *adbookdir; + GDir *dir; + const gchar *dir_name; + GStatBuf statbuf; + gchar buf[ WORK_BUFLEN ]; + gchar numbuf[ WORK_BUFLEN ]; + gint len, lenpre, lensuf, lennum; + long int val, maxval; + GList *fileList = NULL; + + g_return_val_if_fail( book != NULL, NULL ); + + if( book->path == NULL || *book->path == '\0' ) { + book->retVal = MGU_NO_PATH; + return NULL; + } + + strcpy( buf, book->path ); + len = strlen( buf ); + if( len > 0 ) { + if( buf[ len-1 ] != G_DIR_SEPARATOR ) { + buf[ len ] = G_DIR_SEPARATOR; + buf[ ++len ] = '\0'; + } + } + + adbookdir = g_strdup( buf ); + strcat( buf, ADDRBOOK_PREFIX ); + + if( ( dir = g_dir_open( adbookdir, 0, NULL ) ) == NULL ) { + book->retVal = MGU_OPEN_DIRECTORY; + g_free( adbookdir ); + return NULL; + } + + lenpre = strlen( ADDRBOOK_PREFIX ); + lensuf = strlen( ADDRBOOK_SUFFIX ); + lennum = FILE_NUMDIGITS + lenpre; + maxval = -1; + + while( ( dir_name = g_dir_read_name( dir ) ) != NULL ) { + gchar *endptr = NULL; + gint i; + gboolean flg; + + strcpy( buf, adbookdir ); + strcat( buf, dir_name ); + g_stat( buf, &statbuf ); + if( S_IFREG & statbuf.st_mode ) { + if( strncmp( dir_name, ADDRBOOK_PREFIX, lenpre ) == 0 ) { + if( strncmp( (dir_name) + lennum, ADDRBOOK_SUFFIX, lensuf ) == 0 ) { + strncpy( numbuf, (dir_name) + lenpre, FILE_NUMDIGITS ); + numbuf[ FILE_NUMDIGITS ] = '\0'; + flg = TRUE; + for( i = 0; i < FILE_NUMDIGITS; i++ ) { + if( ! strchr( ADDRBOOK_DIGITS, numbuf[i] ) ) { + flg = FALSE; + break; + } + } + if( flg ) { + /* Get value */ + val = strtol( numbuf, &endptr, 10 ); + if( endptr && val > -1 ) { + if( val > maxval ) maxval = val; + fileList = g_list_append( fileList, g_strdup( dir_name ) ); + } + } + } + } + } + } + g_dir_close( dir ); + g_free( adbookdir ); + + book->maxValue = maxval; + book->retVal = MGU_SUCCESS; + return fileList; +} + +/* +* Return file name for specified file number. +* Enter: fileNum File number. +* Return: File name, or NULL if file number too large. Should be g_free() when done. +*/ +gchar *addrbook_gen_new_file_name( gint fileNum ) { + gchar fmt[ 30 ]; + gchar buf[ WORK_BUFLEN ]; + gint n = fileNum; + long int nmax; + + if( n < 1 ) n = 1; + nmax = -1 + (long int) pow( 10, FILE_NUMDIGITS ); + if( fileNum > nmax ) return NULL; + sprintf( fmt, "%%s%%0%dd%%s", FILE_NUMDIGITS ); + sprintf( buf, fmt, ADDRBOOK_PREFIX, n, ADDRBOOK_SUFFIX ); + return g_strdup( buf ); +} + +/* ********************************************************************** +* Address book test functions... +* *********************************************************************** +*/ + +#if 0 +static void addrbook_show_attribs( GList *attr ) { + while( attr ) { + gchar *name = ((XMLAttr *)attr->data)->name; + gchar *value = ((XMLAttr *)attr->data)->value; + printf( "\tn/v = %s : %s\n", name, value ); + attr = g_list_next( attr ); + } + printf( "\t---\n" ); +} +#endif + +/* +* Test email address list. +*/ +static void addrbook_chkparse_addr_list( AddressBookFile *book, XMLFile *file ){ + guint prev_level; + GList *attr; + + for (;;) { + prev_level = file->level; + if( xml_parse_next_tag( file ) ) { + longjmp( book->jumper, 1 ); + } + if (file->level < prev_level) return; + attr = xml_get_current_tag_attr(file); + /* addrbook_show_attribs( attr ); */ + if( xml_compare_tag( file, AB_ELTAG_ADDRESS ) ) { + addrbook_chkparse_addr_list( book, file ); + } + } +} + +/* +* Test user attributes for person. +*/ +static void addrbook_chkparse_attribute( AddressBookFile *book, XMLFile *file ) { + GList *attr; + gchar *element; + + attr = xml_get_current_tag_attr(file); + /* addrbook_show_attribs( attr ); */ + element = xml_get_element( file ); + /* printf( "\t\tattrib value : %s\n", element ); */ +} + +/* +* Test attribute list. +*/ +static void addrbook_chkparse_attr_list( AddressBookFile *book, XMLFile *file ){ + guint prev_level; + + for (;;) { + prev_level = file->level; + if( xml_parse_next_tag( file ) ) { + longjmp( book->jumper, 1 ); + } + if (file->level < prev_level) return; + if( xml_compare_tag( file, AB_ELTAG_ATTRIBUTE ) ) { + addrbook_chkparse_attribute( book, file ); + addrbook_chkparse_attr_list( book, file ); + } + } +} + +/* +* Test person. +*/ +static void addrbook_chkparse_person( AddressBookFile *book, XMLFile *file ) { + GList *attr; + + attr = xml_get_current_tag_attr(file); + /* addrbook_show_attribs( attr ); */ + if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ + longjmp( book->jumper, 1 ); + } + if( xml_compare_tag( file, AB_ELTAG_ADDRESS_LIST ) ) { + addrbook_chkparse_addr_list( book, file ); + } + if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ + longjmp( book->jumper, 1 ); + } + if( xml_compare_tag( file, AB_ELTAG_ATTRIBUTE_LIST ) ) { + addrbook_chkparse_attr_list( book, file ); + } +} + +/* +* Test group member list. +*/ +static void addrbook_chkparse_member_list( AddressBookFile *book, XMLFile *file ){ + GList *attr; + guint prev_level; + + for (;;) { + prev_level = file->level; + if( xml_parse_next_tag( file ) ) { + longjmp( book->jumper, 1 ); + } + if (file->level < prev_level) return; + if( xml_compare_tag( file, AB_ELTAG_MEMBER ) ) { + attr = xml_get_current_tag_attr(file); + /* addrbook_show_attribs( attr ); */ + addrbook_chkparse_member_list( book, file ); + } + else { + attr = xml_get_current_tag_attr( file ); + /* addrbook_show_attribs( attr ); */ + } + } +} + +/* +* Test group. +*/ +static void addrbook_chkparse_group( AddressBookFile *book, XMLFile *file ) { + GList *attr; + + attr = xml_get_current_tag_attr(file); + /* addrbook_show_attribs( attr ); */ + if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ + longjmp( book->jumper, 1 ); + } + if( xml_compare_tag( file, AB_ELTAG_MEMBER_LIST ) ) { + addrbook_chkparse_member_list( book, file ); + } +} + +/* +* Test folder item list. +*/ +static void addrbook_chkparse_folder_list( AddressBookFile *book, XMLFile *file ){ + GList *attr; + guint prev_level; + + for (;;) { + prev_level = file->level; + if( xml_parse_next_tag( file ) ) { + longjmp( book->jumper, 1 ); + } + if (file->level < prev_level) return; + if( xml_compare_tag( file, AB_ELTAG_ITEM ) ) { + attr = xml_get_current_tag_attr(file); + /* addrbook_show_attribs( attr ); */ + addrbook_chkparse_folder_list( book, file ); + } + else { + attr = xml_get_current_tag_attr( file ); + /* addrbook_show_attribs( attr ); */ + } + } +} + +/* +* Test folder. +*/ +static void addrbook_chkparse_folder( AddressBookFile *book, XMLFile *file ) { + GList *attr; + + attr = xml_get_current_tag_attr(file); + /* addrbook_show_attribs( attr ); */ + if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ + longjmp( book->jumper, 1 ); + } + if( xml_compare_tag( file, AB_ELTAG_ITEM_LIST ) ) { + addrbook_chkparse_folder_list( book, file ); + } +} + +/* +* Test address book. +*/ +static gboolean addrbook_chkread_tree( AddressBookFile *book, XMLFile *file ) { + GList *attr; + gboolean retVal; + + if( xml_get_dtd( file ) ) { + return FALSE; + } + if( xml_parse_next_tag( file ) ) { + return FALSE; + } + + if( ! xml_compare_tag( file, AB_ELTAG_ADDRESS_BOOK ) ) { + return FALSE; + } + + attr = xml_get_current_tag_attr(file); + /* addrbook_show_attribs( attr ); */ + + retVal = TRUE; + for (;;) { + if (! file->level ) break; + /* Get item tag */ + if( xml_parse_next_tag( file ) ) { + longjmp( book->jumper, 1 ); + } + /* Get next tag (person, group or folder) */ + if( xml_compare_tag( file, AB_ELTAG_PERSON ) ) { + addrbook_chkparse_person( book, file ); + } + else if( xml_compare_tag( file, AB_ELTAG_GROUP ) ) { + addrbook_chkparse_group( book, file ); + } + else if( xml_compare_tag( file, AB_ELTAG_FOLDER ) ) { + addrbook_chkparse_folder( book, file ); + } + } + return retVal; +} + +/* +* Test address book file by parsing contents. +* Enter: book Address book file to check. +* fileName File name to check. +* Return: MGU_SUCCESS if file appears to be valid format. +*/ +gint addrbook_test_read_file( AddressBookFile *book, gchar *fileName ) { + XMLFile *file = NULL; + gchar *fileSpec = NULL; + + g_return_val_if_fail( book != NULL, -1 ); + + fileSpec = g_strconcat( book->path, G_DIR_SEPARATOR_S, fileName, NULL ); + book->retVal = MGU_OPEN_FILE; + file = xml_open_file( fileSpec ); + g_free( fileSpec ); + if( file ) { + book->retVal = MGU_BAD_FORMAT; + if( setjmp( book->jumper ) ) { + /* printf( "Caught Ya!!!\n" ); */ + xml_close_file( file ); + return book->retVal; + } + if( addrbook_chkread_tree( book, file ) ) { + book->retVal = MGU_SUCCESS; + } + xml_close_file( file ); + } + return book->retVal; +} + +/* +* Return link list of all persons in address book. Note that the list contains +* references to items. Do *NOT* attempt to use the addrbook_free_xxx() functions... +* this will destroy the addressbook data! +* Return: List of items, or NULL if none. +*/ +GList *addrbook_get_all_persons( AddressBookFile *book ) { + g_return_val_if_fail( book != NULL, NULL ); + return addrcache_get_all_persons( book->addressCache ); +} + +/* +* Add person and address data to address book. +* Enter: book Address book. +* folder Folder where to add person, or NULL for root folder. +* name Common name. +* address EMail address. +* remarks Remarks. +* Return: Person added. Do not *NOT* to use the addrbook_free_xxx() functions... +* this will destroy the address book data. +*/ +ItemPerson *addrbook_add_contact( AddressBookFile *book, ItemFolder *folder, const gchar *name, + const gchar *address, const gchar *remarks ) +{ + ItemPerson *person = NULL; + + g_return_val_if_fail( book != NULL, NULL ); + + person = addrcache_add_contact( book->addressCache, folder, name, address, remarks ); + if( person ) book->dirtyFlag = TRUE; + return person; +} + +/* +* End of Source. +*/ diff --git a/src/addrbook.h b/src/addrbook.h new file mode 100644 index 0000000..2a64b9e --- /dev/null +++ b/src/addrbook.h @@ -0,0 +1,116 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Definitions necessary to access external address book files. + */ + +#ifndef __ADDRBOOK_H__ +#define __ADDRBOOK_H__ + +#include +#include +#include + +#include "addritem.h" +#include "addrcache.h" + +/* Address book file */ +typedef struct _AddressBookFile AddressBookFile; + +struct _AddressBookFile { + gchar *name; + gchar *path; + gchar *fileName; + AddressCache *addressCache; + gint retVal; + gint maxValue; + GList *tempList; + GHashTable *tempHash; + gboolean readFlag; + gboolean dirtyFlag; + gboolean modifyFlag; + gboolean accessFlag; + jmp_buf jumper; +}; + +/* Function prototypes */ + +AddressBookFile *addrbook_create_book ( void ); +void addrbook_empty_book ( AddressBookFile *book ); +void addrbook_free_book ( AddressBookFile *book ); +void addrbook_print_book ( AddressBookFile *book, FILE *stream ); +void addrbook_dump_hash ( AddressBookFile *book, FILE *stream ); +void addrbook_dump_book ( AddressBookFile *book, FILE *stream ); +void addrbook_set_name ( AddressBookFile *book, const gchar *value ); +void addrbook_set_path ( AddressBookFile *book, const gchar *value ); +void addrbook_set_file ( AddressBookFile *book, const gchar *value ); +void addrbook_set_accessed ( AddressBookFile *book, const gboolean value ); +gboolean addrbook_get_modified ( AddressBookFile *book ); +gboolean addrbook_get_accessed ( AddressBookFile *book ); +gboolean addrbook_get_read_flag ( AddressBookFile *book ); +gint addrbook_get_status ( AddressBookFile *book ); +ItemFolder *addrbook_get_root_folder ( AddressBookFile *book ); +GList *addrbook_get_list_folder ( AddressBookFile *book ); +GList *addrbook_get_list_person ( AddressBookFile *book ); +gchar *addrbook_get_name ( AddressBookFile *book ); + +ItemPerson *addrbook_remove_person ( AddressBookFile *book, ItemPerson *person ); +ItemGroup *addrbook_remove_group ( AddressBookFile *book, ItemGroup *group ); +ItemEMail *addrbook_person_remove_email ( AddressBookFile *book, ItemPerson *person, + ItemEMail *email ); +ItemEMail *addrbook_group_remove_email ( AddressBookFile *book, ItemGroup *group, + ItemEMail *email ); + +gint addrbook_read_data ( AddressBookFile *book ); +gint addrbook_save_data ( AddressBookFile *book ); + +ItemEMail *addrbook_move_email_before ( AddressBookFile *book, ItemPerson *person, + ItemEMail *itemMove, ItemEMail *itemTarget ); +ItemEMail *addrbook_move_email_after ( AddressBookFile *book, ItemPerson *person, + ItemEMail *itemMove, ItemEMail *itemTarget ); + +void addrbook_update_address_list ( AddressBookFile *book, ItemPerson *person, + GList *listEMail ); +ItemPerson *addrbook_add_address_list ( AddressBookFile *book, ItemFolder *folder, + GList *listEMail ); +GList *addrbook_get_available_email_list( AddressBookFile *book, ItemGroup *group ); +void addrbook_update_group_list ( AddressBookFile *book, ItemGroup *group, + GList *listEMail ); +ItemGroup *addrbook_add_group_list ( AddressBookFile *book, ItemFolder *folder, + GList *listEMail ); +ItemFolder *addrbook_add_new_folder ( AddressBookFile *book, ItemFolder *parent ); + +void addrbook_update_attrib_list ( AddressBookFile *book, ItemPerson *person, GList *listAttrib ); +void addrbook_add_attrib_list ( AddressBookFile *book, ItemPerson *person, GList *listAttrib ); + +ItemFolder *addrbook_remove_folder ( AddressBookFile *book, ItemFolder *folder ); +ItemFolder *addrbook_remove_folder_delete( AddressBookFile *book, ItemFolder *folder ); + +GList *addrbook_get_bookfile_list ( AddressBookFile *book ); +gchar *addrbook_gen_new_file_name ( gint fileNum ); +gint addrbook_test_read_file ( AddressBookFile *book, gchar *fileName ); + +GList *addrbook_get_all_persons ( AddressBookFile *book ); + +ItemPerson *addrbook_add_contact ( AddressBookFile *book, ItemFolder *folder, + const gchar *name, const gchar *address, + const gchar *remarks ); + +#endif /* __ADDRBOOK_H__ */ diff --git a/src/addrcache.c b/src/addrcache.c new file mode 100644 index 0000000..55c7212 --- /dev/null +++ b/src/addrcache.c @@ -0,0 +1,1237 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Functions to maintain address cache. + */ + +#include +#include +#include +#include + +/* #include "mgutils.h" */ +#include "addritem.h" +#include "addrcache.h" +#include "utils.h" + +#define ID_TIME_OFFSET 998000000 +#define ADDRCACHE_MAX_SEARCH_COUNT 1000 + +/* +* Create new address cache. +*/ +AddressCache *addrcache_create() { + AddressCache *cache; + gint t; + + cache = g_new0( AddressCache, 1 ); + cache->itemHash = g_hash_table_new( g_str_hash, g_str_equal ); + + cache->dataRead = FALSE; + cache->modified = FALSE; + cache->modifyTime = 0; + + /* Generate the next ID using system time */ + cache->nextID = 1; + t = time( NULL ); + if( t > 0 ) { + cache->nextID = t - ID_TIME_OFFSET; + } + + cache->tempList = NULL; + cache->rootFolder = addritem_create_item_folder(); + cache->rootFolder->isRoot = TRUE; + ADDRITEM_PARENT(cache->rootFolder) = NULL; + return cache; +} + +/* +* Properties. +*/ +ItemFolder *addrcache_get_root_folder( AddressCache *cache ) { + g_return_val_if_fail( cache != NULL, NULL ); + return cache->rootFolder; +} +GList *addrcache_get_list_folder( AddressCache *cache ) { + g_return_val_if_fail( cache != NULL, NULL ); + return cache->rootFolder->listFolder; +} +GList *addrcache_get_list_person( AddressCache *cache ) { + g_return_val_if_fail( cache != NULL, NULL ); + return cache->rootFolder->listPerson; +} + +/* +* Generate next ID. +*/ +void addrcache_next_id( AddressCache *cache ) { + g_return_if_fail( cache != NULL ); + cache->nextID++; +} + +/* +* Refresh internal variables. This can be used force a reload. +*/ +void addrcache_refresh( AddressCache *cache ) { + cache->dataRead = FALSE; + cache->modified = TRUE; + cache->modifyTime = 0; +} + +/* +* Free hash table visitor function. +*/ +static gint addrcache_free_item_vis( gpointer key, gpointer value, gpointer data ) { + AddrItemObject *obj = ( AddrItemObject * ) value; + if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) { + addritem_free_item_person( ( ItemPerson * ) obj ); + } + else if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) { + addritem_free_item_group( ( ItemGroup * ) obj ); + } + else if( ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER ) { + addritem_free_item_folder( ( ItemFolder * ) obj ); + } + key = NULL; + value = NULL; + return 0; +} + +/* +* Free hash table of address cache items. +*/ +static void addrcache_free_item_hash( GHashTable *table ) { + g_return_if_fail( table != NULL ); + g_hash_table_freeze( table ); + g_hash_table_foreach_remove( table, addrcache_free_item_vis, NULL ); + g_hash_table_thaw( table ); + g_hash_table_destroy( table ); +} + +/* +* Free up folders and groups. +*/ +static void addrcache_free_all_folders( ItemFolder *parent ) { + GList *node = parent->listFolder; + while( node ) { + ItemFolder *folder = node->data; + addrcache_free_all_folders( folder ); + node = g_list_next( node ); + } + g_list_free( parent->listPerson ); + g_list_free( parent->listGroup ); + g_list_free( parent->listFolder ); + parent->listPerson = NULL; + parent->listGroup = NULL; + parent->listFolder = NULL; +} + +/* +* Clear the address cache. +*/ +void addrcache_clear( AddressCache *cache ) { + g_return_if_fail( cache != NULL ); + + /* Free up folders and hash table */ + addrcache_free_all_folders( cache->rootFolder ); + addrcache_free_item_hash( cache->itemHash ); + cache->itemHash = NULL; + ADDRITEM_PARENT(cache->rootFolder) = NULL; + addritem_free_item_folder( cache->rootFolder ); + cache->rootFolder = NULL; + g_list_free( cache->tempList ); + cache->tempList = NULL; + + /* Reset to initial state */ + cache->itemHash = g_hash_table_new( g_str_hash, g_str_equal ); + cache->rootFolder = addritem_create_item_folder(); + cache->rootFolder->isRoot = TRUE; + ADDRITEM_PARENT(cache->rootFolder) = NULL; + + addrcache_refresh( cache ); + +} + +/* +* Free address cache. +*/ +void addrcache_free( AddressCache *cache ) { + g_return_if_fail( cache != NULL ); + + addrcache_free_all_folders( cache->rootFolder ); + addrcache_free_item_hash( cache->itemHash ); + cache->itemHash = NULL; + ADDRITEM_PARENT(cache->rootFolder) = NULL; + addritem_free_item_folder( cache->rootFolder ); + cache->rootFolder = NULL; + g_list_free( cache->tempList ); + cache->tempList = NULL; + g_free( cache ); +} + +/* +* Check whether file has changed by comparing with cache. +* return: TRUE if file has changed. +*/ +gboolean addrcache_check_file( AddressCache *cache, gchar *path ) { + gboolean retVal; + GStatBuf filestat; + retVal = TRUE; + if( path ) { + if( 0 == g_stat( path, &filestat ) ) { + if( filestat.st_mtime == cache->modifyTime ) retVal = FALSE; + } + } + return retVal; +} + +/* +* Save file time to cache. +* return: TRUE if time marked. +*/ +gboolean addrcache_mark_file( AddressCache *cache, gchar *path ) { + gboolean retVal = FALSE; + GStatBuf filestat; + if( path ) { + if( 0 == g_stat( path, &filestat ) ) { + cache->modifyTime = filestat.st_mtime; + retVal = TRUE; + } + } + return retVal; +} + +/* +* Print list of items. +*/ +void addrcache_print_item_list( GList *list, FILE *stream ) { + GList *node = list; + while( node ) { + AddrItemObject *obj = node->data; + if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) { + addritem_print_item_person( ( ItemPerson * ) obj, stream ); + } + else if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) { + addritem_print_item_group( ( ItemGroup * ) obj, stream ); + } + else if( ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER ) { + addritem_print_item_folder( ( ItemFolder * ) obj, stream ); + } + node = g_list_next( node ); + } + fprintf( stream, "\t---\n" ); +} + +/* +* Print item hash table visitor function. +*/ +static void addrcache_print_item_vis( gpointer key, gpointer value, gpointer data ) { + AddrItemObject *obj = ( AddrItemObject * ) value; + FILE *stream = ( FILE * ) data; + if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) { + addritem_print_item_person( ( ItemPerson * ) obj, stream ); + } + else if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) { + addritem_print_item_group( ( ItemGroup * ) obj, stream ); + } + else if( ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER ) { + addritem_print_item_folder( ( ItemFolder * ) obj, stream ); + } +} + +/* +* Dump entire address cache hash table contents. +*/ +void addrcache_print( AddressCache *cache, FILE *stream ) { + g_return_if_fail( cache != NULL ); + fprintf( stream, "AddressCache:\n" ); + fprintf( stream, "next id : %d\n", cache->nextID ); + fprintf( stream, "mod time : %ld\n", cache->modifyTime ); + fprintf( stream, "modified : %s\n", cache->modified ? "yes" : "no" ); + fprintf( stream, "data read: %s\n", cache->dataRead ? "yes" : "no" ); +} + +/* +* Dump entire address cache hash table contents. +*/ +void addrcache_dump_hash( AddressCache *cache, FILE *stream ) { + g_return_if_fail( cache != NULL ); + addrcache_print( cache, stream ); + g_hash_table_foreach( cache->itemHash, addrcache_print_item_vis, stream ); +} + +/* + * Allocate ID for person. + */ +void addrcache_id_person( AddressCache *cache, ItemPerson *person ) { + g_return_if_fail( cache != NULL ); + g_return_if_fail( person != NULL ); + if( ADDRITEM_ID(person) ) return; + addrcache_next_id( cache ); + ADDRITEM_ID(person) = g_strdup_printf( "%d", cache->nextID ); +} + +/* + * Allocate ID for group. + */ +void addrcache_id_group( AddressCache *cache, ItemGroup *group ) { + g_return_if_fail( cache != NULL ); + g_return_if_fail( group != NULL ); + if( ADDRITEM_ID(group) ) return; + addrcache_next_id( cache ); + ADDRITEM_ID(group) = g_strdup_printf( "%d", cache->nextID ); +} + +/* + * Allocate ID for folder. + */ +void addrcache_id_folder( AddressCache *cache, ItemFolder *folder ) { + g_return_if_fail( cache != NULL ); + g_return_if_fail( folder != NULL ); + if( ADDRITEM_ID(folder) ) return; + addrcache_next_id( cache ); + ADDRITEM_ID(folder) = g_strdup_printf( "%d", cache->nextID ); +} + +/* + * Allocate ID for email address. + */ +void addrcache_id_email( AddressCache *cache, ItemEMail *email ) { + g_return_if_fail( cache != NULL ); + g_return_if_fail( email != NULL ); + if( ADDRITEM_ID(email) ) return; + addrcache_next_id( cache ); + ADDRITEM_ID(email) = g_strdup_printf( "%d", cache->nextID ); +} + +/* + * Allocate ID for user attribute. + */ +void addrcache_id_attribute( AddressCache *cache, UserAttribute *attrib ) { + g_return_if_fail( cache != NULL ); + g_return_if_fail( attrib != NULL ); + if( attrib->uid ) return; + addrcache_next_id( cache ); + attrib->uid = g_strdup_printf( "%d", cache->nextID ); +} + +/* +* Add person to hash table. +* return: TRUE if item added. +*/ +gboolean addrcache_hash_add_person( AddressCache *cache, ItemPerson *person ) { + if( g_hash_table_lookup( cache->itemHash, ADDRITEM_ID(person) ) ) { + return FALSE; + } + g_hash_table_insert( cache->itemHash, ADDRITEM_ID(person), person ); + return TRUE; +} + +/* +* Add group to hash table. +* return: TRUE if item added. +*/ +gboolean addrcache_hash_add_group( AddressCache *cache, ItemGroup *group ) { + g_return_val_if_fail( cache != NULL, FALSE ); + g_return_val_if_fail( group != NULL, FALSE ); + + if( g_hash_table_lookup( cache->itemHash, ADDRITEM_ID(group) ) ) { + return FALSE; + } + g_hash_table_insert( cache->itemHash, ADDRITEM_ID(group), group ); + return TRUE; +} + +/* +* Add folder to hash table. +* return: TRUE if item added. +*/ +gboolean addrcache_hash_add_folder( AddressCache *cache, ItemFolder *folder ) { + g_return_val_if_fail( cache != NULL, FALSE ); + g_return_val_if_fail( folder != NULL, FALSE ); + + if( g_hash_table_lookup( cache->itemHash, ADDRITEM_ID(folder) ) ) { + return FALSE; + } + g_hash_table_insert( cache->itemHash, ADDRITEM_ID(folder), folder ); + return TRUE; +} + +/* +* Add person to specified folder in cache. +*/ +gboolean addrcache_folder_add_person( AddressCache *cache, ItemFolder *folder, ItemPerson *item ) { + gboolean retVal = FALSE; + + g_return_val_if_fail( cache != NULL, FALSE ); + g_return_val_if_fail( folder != NULL, FALSE ); + g_return_val_if_fail( item != NULL, FALSE ); + + retVal = addrcache_hash_add_person( cache, item ); + if( retVal ) { + addritem_folder_add_person( folder, item ); + } + return retVal; +} + +/* +* Add folder to specified folder in cache. +*/ +gboolean addrcache_folder_add_folder( AddressCache *cache, ItemFolder *folder, ItemFolder *item ) { + gboolean retVal = FALSE; + + g_return_val_if_fail( cache != NULL, FALSE ); + g_return_val_if_fail( folder != NULL, FALSE ); + g_return_val_if_fail( item != NULL, FALSE ); + + retVal = addrcache_hash_add_folder( cache, item ); + if( retVal ) { + addritem_folder_add_folder( folder, item ); + } + return TRUE; +} + +/* +* Add folder to specified folder in cache. +*/ +gboolean addrcache_folder_add_group( AddressCache *cache, ItemFolder *folder, ItemGroup *item ) { + gboolean retVal = FALSE; + + g_return_val_if_fail( cache != NULL, FALSE ); + g_return_val_if_fail( folder != NULL, FALSE ); + g_return_val_if_fail( item != NULL, FALSE ); + + retVal = addrcache_hash_add_group( cache, item ); + if( retVal ) { + addritem_folder_add_group( folder, item ); + } + return retVal; +} + +/* +* Add person to address cache. +* return: TRUE if item added. +*/ +gboolean addrcache_add_person( AddressCache *cache, ItemPerson *person ) { + gboolean retVal = FALSE; + + g_return_val_if_fail( cache != NULL, FALSE ); + g_return_val_if_fail( person != NULL, FALSE ); + + retVal = addrcache_hash_add_person( cache, person ); + if( retVal ) { + addritem_folder_add_person( cache->rootFolder, person ); + } + return retVal; +} + +/* +* Add EMail address to person. +* return: TRUE if item added. +*/ +gboolean addrcache_person_add_email( AddressCache *cache, ItemPerson *person, ItemEMail *email ) { + g_return_val_if_fail( cache != NULL, FALSE ); + g_return_val_if_fail( person != NULL, FALSE ); + g_return_val_if_fail( email != NULL, FALSE ); + + addritem_person_add_email( person, email ); + return TRUE; +} + +/* +* Add group to address cache. +* return: TRUE if item added. +*/ +gboolean addrcache_add_group( AddressCache *cache, ItemGroup *group ) { + gboolean retVal = FALSE; + + g_return_val_if_fail( cache != NULL, FALSE ); + g_return_val_if_fail( group != NULL, FALSE ); + + retVal = addrcache_hash_add_group( cache, group ); + if( retVal ) { + addritem_folder_add_group( cache->rootFolder, group ); + } + return retVal; +} + +/* +* Add EMail address to person. +* return: TRUE if item added. +*/ +gboolean addrcache_group_add_email( AddressCache *cache, ItemGroup *group, ItemEMail *email ) { + g_return_val_if_fail( cache != NULL, FALSE ); + g_return_val_if_fail( group != NULL, FALSE ); + g_return_val_if_fail( email != NULL, FALSE ); + + addritem_group_add_email( group, email ); + return TRUE; +} + +/* +* Add folder to address cache. +* return: TRUE if item added. +*/ +gboolean addrcache_add_folder( AddressCache *cache, ItemFolder *folder ) { + gboolean retVal = FALSE; + + g_return_val_if_fail( cache != NULL, FALSE ); + g_return_val_if_fail( folder != NULL, FALSE ); + + retVal = addrcache_hash_add_folder( cache, folder ); + if( retVal ) { + addritem_folder_add_folder( cache->rootFolder, folder ); + } + return retVal; +} + +/* +* Return pointer to object (either person or group) for specified ID. +* param: uid Object ID. +* return: Object, or NULL if not found. +*/ +AddrItemObject *addrcache_get_object( AddressCache *cache, const gchar *uid ) { + AddrItemObject *obj = NULL; + gchar *uidH; + + g_return_val_if_fail( cache != NULL, NULL ); + + if( uid == NULL || *uid == '\0' ) return NULL; + obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid ); + if( obj ) { + /* Check for matching UID */ + uidH = ADDRITEM_ID(obj); + if( uidH ) { + if( strcmp( uidH, uid ) == 0 ) return obj; + } + } + return NULL; +} + +/* +* Return pointer for specified object ID. +* param: uid Object ID. +* return: Person object, or NULL if not found. +*/ +ItemPerson *addrcache_get_person( AddressCache *cache, const gchar *uid ) { + ItemPerson *person = NULL; + AddrItemObject *obj = addrcache_get_object( cache, uid ); + + if( obj ) { + if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) { + person = ( ItemPerson * ) obj; + } + } + return person; +} + +/* +* Return pointer for specified object ID. +* param: uid group ID. +* return: Group object, or NULL if not found. +*/ +ItemGroup *addrcache_get_group( AddressCache *cache, const gchar *uid ) { + ItemGroup *group = NULL; + AddrItemObject *obj = addrcache_get_object( cache, uid ); + + if( obj ) { + if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) { + group = ( ItemGroup * ) obj; + } + } + return group; +} + +/* +* Find email address in address cache. +* param: uid Object ID for person. +* eid EMail ID. +* return: email object for specified object ID and email ID, or NULL if not found. +*/ +ItemEMail *addrcache_get_email( AddressCache *cache, const gchar *uid, const gchar *eid ) { + AddrItemObject *objP; + + if( eid == NULL || *eid == '\0' ) return NULL; + + objP = addrcache_get_object( cache, uid ); + if( objP ) { + if( ADDRITEM_TYPE(objP) == ITEMTYPE_PERSON ) { + /* Sequential search through email addresses */ + ItemPerson *person = ( ItemPerson * ) objP; + GList *nodeMail = person->listEMail; + while( nodeMail ) { + AddrItemObject *objE = nodeMail->data; + gchar *ide = ADDRITEM_ID(objE); + if( ide ) { + if( strcmp( ide, eid ) == 0 ) { + return ( ItemEMail * ) objE; + } + } + nodeMail = g_list_next( nodeMail ); + } + } + } + return NULL; +} + +/* +* Remove attribute from person. +* param: uid Object ID for person. +* aid Attribute ID. +* return: UserAttribute object, or NULL if not found. Note that object should still be freed. +*/ +UserAttribute *addrcache_person_remove_attrib_id( AddressCache *cache, const gchar *uid, const gchar *aid ) { + UserAttribute *attrib = NULL; + ItemPerson *person; + + if( aid == NULL || *aid == '\0' ) return NULL; + + person = addrcache_get_person( cache, uid ); + if( person ) { + attrib = addritem_person_remove_attrib_id( person, aid ); + } + return attrib; +} + +/* +* Remove attribute from person. +* param: person Person. +* attrib Attribute to remove. +* return: UserAttribute object. Note that object should still be freed. +*/ +UserAttribute *addrcache_person_remove_attribute( AddressCache *cache, ItemPerson *person, UserAttribute *attrib ) { + UserAttribute *found = NULL; + + g_return_val_if_fail( cache != NULL, NULL ); + + if( person && attrib ) { + found = addritem_person_remove_attribute( person, attrib ); + } + return found; +} + +/* +* Remove group from address cache for specified ID. +* param: uid Object ID. +* return: Group, or NULL if not found. Note that object should still be freed. +*/ +ItemGroup *addrcache_remove_group_id( AddressCache *cache, const gchar *uid ) { + AddrItemObject *obj = NULL; + + g_return_val_if_fail( cache != NULL, NULL ); + + if( uid == NULL || *uid == '\0' ) return NULL; + obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid ); + if( obj ) { + if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) { + ItemGroup *group = ( ItemGroup * ) obj; + ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(group); + if( ! parent ) parent = cache->rootFolder; + /* Remove group from parent's list and hash table */ + parent->listGroup = g_list_remove( parent->listGroup, group ); + g_hash_table_remove( cache->itemHash, uid ); + return ( ItemGroup * ) obj; + } + } + return NULL; +} + +/* +* Remove group from address cache. +* param: group Group to remove. +* return: Group, or NULL if not found. Note that object should still be freed. +*/ +ItemGroup *addrcache_remove_group( AddressCache *cache, ItemGroup *group ) { + AddrItemObject *obj = NULL; + + g_return_val_if_fail( cache != NULL, NULL ); + + if( group ) { + gchar *uid = ADDRITEM_ID(group); + if( uid == NULL || *uid == '\0' ) return NULL; + obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid ); + if( obj ) { + ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(group); + if( ! parent ) parent = cache->rootFolder; + + /* Remove group from parent's list and hash table */ + parent->listGroup = g_list_remove( parent->listGroup, obj ); + g_hash_table_remove( cache->itemHash, uid ); + return group; + } + } + return NULL; +} + +/* +* Remove person's email addresses from all groups. +*/ +static void addrcache_allgrp_rem_person_vis( gpointer key, gpointer value, gpointer data ) { + AddrItemObject *obj = ( AddrItemObject * ) value; + ItemPerson *person = ( ItemPerson * ) data; + + if ( !person ) return; + + if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) { + ItemGroup *group = ( ItemGroup * ) obj; + if( group ) { + /* Remove each email address that belongs to the person from the list */ + GList *node = person->listEMail; + debug_print("removing email in person %p (%s) from group %p (%s)\n", person, ADDRITEM_NAME(person), group, ADDRITEM_NAME(group)); + while( node ) { + group->listEMail = g_list_remove( group->listEMail, node->data ); + node = g_list_next( node ); + } + } + } +} + +/* +* Remove email from group item hash table visitor function. +*/ +static void addrcache_allgrp_rem_email_vis( gpointer key, gpointer value, gpointer data ) { + AddrItemObject *obj = ( AddrItemObject * ) value; + ItemEMail *email = ( ItemEMail * ) data; + + if( !email ) return; + if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) { + ItemGroup *group = ( ItemGroup * ) value; + if( group ) { + debug_print("removing email %p (%s) from group %p (%s)\n", email, email->address, group, ADDRITEM_NAME(group)); + /* Remove each email address that belongs to the person from the list */ + group->listEMail = g_list_remove( group->listEMail, email ); + } + } +} + +/* +* Remove person from address cache for specified ID. Note that person still retains +* their EMail addresses. Also, links to these email addresses will be severed from +* the group. +* param: uid Object ID. +* return: Person, or NULL if not found. Note that object should still be freed. +*/ +ItemPerson *addrcache_remove_person_id( AddressCache *cache, const gchar *uid ) { + AddrItemObject *obj = NULL; + + g_return_val_if_fail( cache != NULL, NULL ); + + if( uid == NULL || *uid == '\0' ) return NULL; + obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid ); + if( obj ) { + if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) { + /* Remove person's email addresses from all groups where */ + /* referenced and from hash table. */ + ItemPerson *person = ( ItemPerson * ) obj; + ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(person); + if( ! parent ) parent = cache->rootFolder; + /* Remove emails from groups, remove from parent's list */ + /* and hash table */ + g_hash_table_foreach( cache->itemHash, addrcache_allgrp_rem_person_vis, person ); + parent->listPerson = g_list_remove( parent->listPerson, person ); + g_hash_table_remove( cache->itemHash, uid ); + return person; + } + } + return NULL; +} + +/* +* Remove specified person from address cache. +* param: person Person to remove. +* return: Person, or NULL if not found. Note that object should still be freed. +*/ +ItemPerson *addrcache_remove_person( AddressCache *cache, ItemPerson *person ) { + AddrItemObject *obj = NULL; + + g_return_val_if_fail( cache != NULL, NULL ); + + if( person ) { + gchar *uid = ADDRITEM_ID(person); + if( uid == NULL || *uid == '\0' ) return NULL; + obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid ); + if( obj ) { + if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) { + /* Remove person's email addresses from all groups where */ + /* referenced and from hash table. */ + ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(person); + if( ! parent ) parent = cache->rootFolder; + g_hash_table_foreach( cache->itemHash, addrcache_allgrp_rem_person_vis, person ); + parent->listPerson = g_list_remove( parent->listPerson, person ); + g_hash_table_remove( cache->itemHash, uid ); + return person; + } + } + } + return NULL; +} + +/* +* Remove email address in address cache for specified ID. +* param: uid Object ID for person. +* eid EMail ID. +* return: EMail object, or NULL if not found. Note that object should still be freed. +*/ +ItemEMail *addrcache_person_remove_email_id( AddressCache *cache, const gchar *uid, const gchar *eid ) { + ItemEMail *email = NULL; + ItemPerson *person; + + if( eid == NULL || *eid == '\0' ) return NULL; + + person = addrcache_get_person( cache, uid ); + if( person ) { + email = addritem_person_remove_email_id( person, eid ); + if( email ) { + /* Remove email from all groups. */ + g_hash_table_foreach( cache->itemHash, addrcache_allgrp_rem_email_vis, email ); + + /* Remove email from person's address list */ + if( person->listEMail ) { + person->listEMail = g_list_remove( person->listEMail, email ); + } + /* Unlink reference to person. */ + ADDRITEM_PARENT(email) = NULL; + } + } + return email; +} + +/* +* Remove email address in address cache for specified person. +* param: person Person. +* email EMail to remove. +* return: EMail object, or NULL if not found. Note that object should still be freed. +*/ +ItemEMail *addrcache_person_remove_email( AddressCache *cache, ItemPerson *person, ItemEMail *email ) { + ItemEMail *found = NULL; + + g_return_val_if_fail( cache != NULL, NULL ); + + if( person && email ) { + found = addritem_person_remove_email( person, email ); + if( found ) { + /* Remove email from all groups. */ + g_hash_table_foreach( cache->itemHash, addrcache_allgrp_rem_email_vis, email ); + + /* Remove email from person's address list */ + if( person->listEMail ) { + person->listEMail = g_list_remove( person->listEMail, email ); + } + /* Unlink reference to person. */ + ADDRITEM_PARENT(email) = NULL; + } + } + return found; +} + +/* +* Return link list of address items for root level folder. Note that the list contains +* references to items and should be g_free() when done. Do *NOT* attempt to use the +* addrcache_free_xxx() functions... this will destroy the address cache data! +* Return: List of items, or NULL if none. +*/ +GList *addrcache_folder_get_address_list( AddressCache *cache, ItemFolder *folder ) { + GList *list = NULL; + GList *node = NULL; + ItemFolder *f = folder; + + g_return_val_if_fail( cache != NULL, NULL ); + + if( ! f ) f = cache->rootFolder; + node = f->listPerson; + while( node ) { + list = g_list_append( list, node->data ); + node = g_list_next( node ); + } + node = f->listGroup; + while( node ) { + list = g_list_append( list, node->data ); + node = g_list_next( node ); + } + return list; +} + +/* +* Return link list of persons for specified folder. Note that the list contains +* references to items and should be g_free() when done. Do *NOT* attempt to use the +* addrcache_free_xxx() functions... this will destroy the address cache data! +* Return: List of items, or NULL if none. +*/ +GList *addrcache_folder_get_person_list( AddressCache *cache, ItemFolder *folder ) { + ItemFolder *f = folder; + + g_return_val_if_fail( cache != NULL, NULL ); + + if( ! f ) f = cache->rootFolder; + return addritem_folder_get_person_list( f ); +} + +/* +* Return link list of group items for specified folder. Note that the list contains +* references to items and should be g_free() when done. Do *NOT* attempt to use the +* addrcache_free_xxx() functions... this will destroy the address cache data! +* Return: List of items, or NULL if none. +*/ +GList *addrcache_folder_get_group_list( AddressCache *cache, ItemFolder *folder ) { + ItemFolder *f = folder; + + g_return_val_if_fail( cache != NULL, NULL ); + + if( ! f ) f = cache->rootFolder; + return addritem_folder_get_group_list( f ); +} + +/* +* Return link list of folder items for specified folder. Note that the list contains +* references to items and should be g_free() when done. Do *NOT* attempt to used the +* addrcache_free_xxx() functions... this will destroy the address cache data! +* Return: List of items, or NULL if none. +*/ +GList *addrcache_folder_get_folder_list( AddressCache *cache, ItemFolder *folder ) { + GList *node = NULL; + GList *list = NULL; + ItemFolder *f = folder; + + g_return_val_if_fail( cache != NULL, NULL ); + + if( ! f ) f = cache->rootFolder; + node = f->listFolder; + while( node ) { + list = g_list_append( list, node->data ); + node = g_list_next( node ); + } + return list; +} + +/* +* Return link list of address items for root level folder. Note that the list contains +* references to items and should be g_free() when done. Do *NOT* attempt to used the +* addrcache_free_xxx() functions... this will destroy the address cache data! +* Return: List of items, or NULL if none. +*/ +GList *addrcache_get_address_list( AddressCache *cache ) { + g_return_val_if_fail( cache != NULL, NULL ); + return addrcache_folder_get_address_list( cache, cache->rootFolder ); +} + +/* +* Return link list of persons for root level folder. Note that the list contains +* references to items and should be g_free() when done. Do *NOT* attempt to used the +* addrcache_free_xxx() functions... this will destroy the address cache data! +* Return: List of items, or NULL if none. +*/ +GList *addrcache_get_person_list( AddressCache *cache ) { + g_return_val_if_fail( cache != NULL, NULL ); + return addritem_folder_get_person_list( cache->rootFolder ); +} + +/* +* Return link list of group items in root level folder. Note that the list contains +* references to items and should be g_free() when done. Do *NOT* attempt to used the +* addrcache_free_xxx() functions... this will destroy the address cache data! +* Return: List of items, or NULL if none. +*/ +GList *addrcache_get_group_list( AddressCache *cache ) { + g_return_val_if_fail( cache != NULL, NULL ); + return cache->rootFolder->listGroup; +} + +/* +* Return link list of folder items in root level folder. Note that the list contains +* references to items and should be g_free() when done. Do *NOT* attempt to used the +* addrcache_free_xxx() functions... this will destroy the address cache data! +* Return: List of items, or NULL if none. +*/ +GList *addrcache_get_folder_list( AddressCache *cache ) { + g_return_val_if_fail( cache != NULL, NULL ); + return cache->rootFolder->listFolder; +} + +/* +* Group visitor function. +*/ +static void addrcache_get_grp_person_vis( gpointer key, gpointer value, gpointer data ) { + AddrItemObject *obj = ( AddrItemObject * ) value; + + if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) { + AddressCache *cache = data; + ItemGroup *group = ( ItemGroup * ) obj; + ItemPerson *person = ( ItemPerson * ) cache->tempList->data; + GList *node = group->listEMail; + while( node ) { + ItemEMail *email = ( ItemEMail * ) node->data; + if( ADDRITEM_PARENT(email) == ADDRITEM_OBJECT(person) ) { + if( ! g_list_find( cache->tempList, group ) ) { + cache->tempList = g_list_append( cache->tempList, group ); + } + } + node = g_list_next( node ); + } + } +} + +/* +* Return link list of groups which contain a reference to specified person's email +* address. +*/ +GList *addrcache_get_group_for_person( AddressCache *cache, ItemPerson *person ) { + GList *list = NULL; + + g_return_val_if_fail( cache != NULL, NULL ); + + cache->tempList = NULL; + cache->tempList = g_list_append( cache->tempList, person ); + g_hash_table_foreach( cache->itemHash, addrcache_get_grp_person_vis, cache ); + cache->tempList = g_list_remove( cache->tempList, person ); + list = cache->tempList; + cache->tempList = NULL; + return list; +} + +/* +* Find root folder for specified folder. +* Enter: folder Folder to search. +* Return: root folder, or NULL if not found. +*/ +ItemFolder *addrcache_find_root_folder( ItemFolder *folder ) { + ItemFolder *item = folder; + gint count = 0; + + while( item ) { + if( item->isRoot ) break; + if( ++count > ADDRCACHE_MAX_SEARCH_COUNT ) { + item = NULL; + break; + } + item = ( ItemFolder * ) ADDRITEM_PARENT(folder); + } + return item; +} + +/* +* Get all person visitor function. +*/ +static void addrcache_get_all_persons_vis( gpointer key, gpointer value, gpointer data ) { + AddrItemObject *obj = ( AddrItemObject * ) value; + + if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) { + AddressCache *cache = data; + cache->tempList = g_list_append( cache->tempList, obj ); + } +} + +/* +* Return link list of all persons in address cache. Note that the list contains +* references to items. Do *NOT* attempt to use the addrcache_free_xxx() functions... +* this will destroy the address cache data! +* Return: List of items, or NULL if none. +*/ +GList *addrcache_get_all_persons( AddressCache *cache ) { + GList *list = NULL; + + g_return_val_if_fail( cache != NULL, NULL ); + + cache->tempList = NULL; + g_hash_table_foreach( cache->itemHash, addrcache_get_all_persons_vis, cache ); + list = cache->tempList; + cache->tempList = NULL; + return list; +} + +/* +* Get all groups visitor function. +*/ +static void addrcache_get_all_groups_vis( gpointer key, gpointer value, gpointer data ) { + AddrItemObject *obj = ( AddrItemObject * ) value; + + if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) { + AddressCache *cache = data; + cache->tempList = g_list_append( cache->tempList, obj ); + } +} + +/* +* Return link list of all groups in address cache. Note that the list contains +* references to items. Do *NOT* attempt to use the addrcache_free_xxx() functions... +* this will destroy the address cache data! +* Return: List of items, or NULL if none. +*/ +GList *addrcache_get_all_groups( AddressCache *cache ) { + GList *list = NULL; + + g_return_val_if_fail( cache != NULL, NULL ); + + cache->tempList = NULL; + g_hash_table_foreach( cache->itemHash, addrcache_get_all_groups_vis, cache ); + list = cache->tempList; + cache->tempList = NULL; + return list; +} + +/* +* Remove folder from cache. Children are re-parented to parent folder. +* param: folder Folder to remove. +* return: Folder, or NULL if not found. Note that object should still be freed. +*/ +ItemFolder *addrcache_remove_folder( AddressCache *cache, ItemFolder *folder ) { + AddrItemObject *obj = NULL; + + g_return_val_if_fail( cache != NULL, NULL ); + + if( folder ) { + gchar *uid = ADDRITEM_ID(folder); + if( uid == NULL || *uid == '\0' ) return NULL; + obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid ); + if( obj ) { + ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(folder); + GList *node; + AddrItemObject *aio; + if( ! parent ) parent = cache->rootFolder; + + /* Re-parent children in folder */ + node = folder->listFolder; + while( node ) { + aio = ( AddrItemObject * ) node->data; + parent->listFolder = g_list_append( parent->listFolder, aio ); + aio->parent = ADDRITEM_OBJECT(parent); + node = g_list_next( node ); + } + node = folder->listPerson; + while( node ) { + aio = ( AddrItemObject * ) node->data; + parent->listPerson = g_list_append( parent->listPerson, aio ); + aio->parent = ADDRITEM_OBJECT(parent); + node = g_list_next( node ); + } + node = folder->listGroup; + while( node ) { + aio = ( AddrItemObject * ) node->data; + parent->listGroup = g_list_append( parent->listGroup, aio ); + aio->parent = ADDRITEM_OBJECT(parent); + node = g_list_next( node ); + } + + /* Remove folder from parent's list and hash table */ + parent->listFolder = g_list_remove( parent->listFolder, folder ); + ADDRITEM_PARENT(folder) = NULL; + g_hash_table_remove( cache->itemHash, uid ); + return folder; + } + } + return NULL; +} + +/* +* Remove folder from cache. Children are deleted. +* param: folder Folder to remove. +* return: Folder, or NULL if not found. Note that object should still be freed. +*/ +ItemFolder *addrcache_remove_folder_delete( AddressCache *cache, ItemFolder *folder ) { + AddrItemObject *obj = NULL; + + g_return_val_if_fail( cache != NULL, NULL ); + + if( folder ) { + gchar *uid = ADDRITEM_ID(folder); + if( uid == NULL || *uid == '\0' ) return NULL; + obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid ); + if( obj ) { + ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(folder); + if( ! parent ) parent = cache->rootFolder; + + /* Remove groups */ + while( folder->listGroup ) { + ItemGroup *item = ( ItemGroup * ) folder->listGroup->data; + item = addrcache_remove_group( cache, item ); + if( item ) { + addritem_free_item_group( item ); + item = NULL; + } + } + + while( folder->listPerson ) { + ItemPerson *item = ( ItemPerson * ) folder->listPerson->data; + item = addrcache_remove_person( cache, item ); + if( item ) { + addritem_free_item_person( item ); + item = NULL; + } + } + + /* Recursive deletion of folder */ + while( folder->listFolder ) { + ItemFolder *item = ( ItemFolder * ) folder->listFolder->data; + item = addrcache_remove_folder_delete( cache, item ); + if( item ) { + addritem_free_item_folder( item ); + item = NULL; + } + } + + /* Remove folder from parent's list and hash table */ + parent->listFolder = g_list_remove( parent->listFolder, folder ); + ADDRITEM_PARENT(folder) = NULL; + g_hash_table_remove( cache->itemHash, uid ); + return folder; + } + } + return NULL; +} + +/* +* Add person and address data to cache. +* Enter: cache Cache. +* folder Folder where to add person, or NULL for root folder. +* name Common name. +* address EMail address. +* remarks Remarks. +* Return: Person added. Do not *NOT* to use the addrbook_free_xxx() functions... +* this will destroy the address book data. +*/ +ItemPerson *addrcache_add_contact( AddressCache *cache, ItemFolder *folder, const gchar *name, + const gchar *address, const gchar *remarks ) +{ + ItemPerson *person = NULL; + ItemEMail *email = NULL; + ItemFolder *f = folder; + + g_return_val_if_fail( cache != NULL, NULL ); + + if( ! f ) f = cache->rootFolder; + + /* Create person object */ + person = addritem_create_item_person(); + addritem_person_set_common_name( person, name ); + addrcache_id_person( cache, person ); + addrcache_folder_add_person( cache, f, person ); + + /* Create email object */ + email = addritem_create_item_email(); + addritem_email_set_address( email, address ); + addritem_email_set_remarks( email, remarks ); + addrcache_id_email( cache, email ); + addritem_person_add_email( person, email ); + + return person; +} + +/* +* End of Source. +*/ diff --git a/src/addrcache.h b/src/addrcache.h new file mode 100644 index 0000000..2c57486 --- /dev/null +++ b/src/addrcache.h @@ -0,0 +1,124 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Definitions for address cache. + */ + +#ifndef __ADDRCACHE_H__ +#define __ADDRCACHE_H__ + +#include +#include +#include +#include "addritem.h" +#include "utils.h" + +/* Address cache */ +typedef struct _AddressCache AddressCache; + +struct _AddressCache { + gint nextID; + gboolean dataRead; + gboolean modified; + stime_t modifyTime; + GHashTable *itemHash; + GList *tempList; + ItemFolder *rootFolder; +}; + +/* Function prototypes */ +AddressCache *addrcache_create(); +ItemFolder *addrcache_get_root_folder ( AddressCache *cache ); +GList *addrcache_get_list_folder ( AddressCache *cache ); +GList *addrcache_get_list_person ( AddressCache *cache ); + +void addrcache_refresh ( AddressCache *cache ); +/* void addrcache_empty ( AddressCache *cache ); */ +void addrcache_clear ( AddressCache *cache ); +void addrcache_free ( AddressCache *cache ); +gboolean addrcache_check_file ( AddressCache *cache, gchar *path ); +gboolean addrcache_mark_file ( AddressCache *cache, gchar *path ); + +void addrcache_print_item_list ( GList *list, FILE *stream ); +void addrcache_print ( AddressCache *cache, FILE *stream ); +void addrcache_dump_hash ( AddressCache *cache, FILE *stream ); + +void addrcache_id_person ( AddressCache *cache, ItemPerson *person ); +void addrcache_id_group ( AddressCache *cache, ItemGroup *group ); +void addrcache_id_folder ( AddressCache *cache, ItemFolder *folder ); +void addrcache_id_email ( AddressCache *cache, ItemEMail *email ); +void addrcache_id_attribute ( AddressCache *cache, UserAttribute *attrib ); + +gboolean addrcache_hash_add_person ( AddressCache *cache, ItemPerson *person ); +gboolean addrcache_hash_add_group ( AddressCache *cache, ItemGroup *group ); +gboolean addrcache_hash_add_folder ( AddressCache *cache, ItemFolder *folder ); + +gboolean addrcache_folder_add_person ( AddressCache *cache, ItemFolder *folder, ItemPerson *item ); +gboolean addrcache_folder_add_folder ( AddressCache *cache, ItemFolder *folder, ItemFolder *item ); +gboolean addrcache_folder_add_group ( AddressCache *cache, ItemFolder *folder, ItemGroup *item ); + +gboolean addrcache_add_person ( AddressCache *cache, ItemPerson *person ); +gboolean addrcache_add_group ( AddressCache *cache, ItemGroup *group ); +gboolean addrcache_person_add_email ( AddressCache *cache, ItemPerson *person, ItemEMail *email ); +gboolean addrcache_group_add_email ( AddressCache *cache, ItemGroup *group, ItemEMail *email ); +gboolean addrcache_add_folder ( AddressCache *cache, ItemFolder *folder ); + +AddrItemObject *addrcache_get_object ( AddressCache *cache, const gchar *uid ); +ItemPerson *addrcache_get_person ( AddressCache *cache, const gchar *uid ); +ItemGroup *addrcache_get_group ( AddressCache *cache, const gchar *uid ); +ItemEMail *addrcache_get_email ( AddressCache *cache, const gchar *uid, const gchar *eid ); + +UserAttribute *addrcache_person_remove_attrib_id ( AddressCache *cache, const gchar *uid, + const gchar *aid ); +UserAttribute *addrcache_person_remove_attribute ( AddressCache *cache, ItemPerson *person, + UserAttribute *attrib ); + +ItemGroup *addrcache_remove_group_id ( AddressCache *cache, const gchar *uid ); +ItemGroup *addrcache_remove_group ( AddressCache *cache, ItemGroup *group ); + +ItemPerson *addrcache_remove_person_id ( AddressCache *cache, const gchar *uid ); +ItemPerson *addrcache_remove_person ( AddressCache *cache, ItemPerson *person ); +ItemEMail *addrcache_person_remove_email_id ( AddressCache *cache, const gchar *uid, const gchar *eid ); +ItemEMail *addrcache_person_remove_email ( AddressCache *cache, ItemPerson *person, ItemEMail *email ); + +GList *addrcache_folder_get_address_list ( AddressCache *cache, ItemFolder *folder ); +GList *addrcache_folder_get_person_list ( AddressCache *cache, ItemFolder *folder ); +GList *addrcache_folder_get_group_list ( AddressCache *cache, ItemFolder *folder ); +GList *addrcache_folder_get_folder_list ( AddressCache *cache, ItemFolder *folder ); + +GList *addrcache_get_address_list ( AddressCache *cache ); +GList *addrcache_get_person_list ( AddressCache *cache ); +GList *addrcache_get_group_list ( AddressCache *cache ); +GList *addrcache_get_folder_list ( AddressCache *cache ); + +GList *addrcache_get_group_for_person ( AddressCache *cache, ItemPerson *person ); + +ItemFolder *addrcache_find_root_folder ( ItemFolder *folder ); +GList *addrcache_get_all_persons ( AddressCache *cache ); +GList *addrcache_get_all_groups ( AddressCache *cache ); + +ItemFolder *addrcache_remove_folder ( AddressCache *cache, ItemFolder *folder ); +ItemFolder *addrcache_remove_folder_delete ( AddressCache *cache, ItemFolder *folder ); + +ItemPerson *addrcache_add_contact ( AddressCache *cache, ItemFolder *folder, + const gchar *name, const gchar *address, + const gchar *remarks ); + +#endif /* __ADDRCACHE_H__ */ diff --git a/src/addressadd.c b/src/addressadd.c new file mode 100644 index 0000000..fde7930 --- /dev/null +++ b/src/addressadd.c @@ -0,0 +1,447 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * Copyright (C) 2001-2015 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Add address to address book dialog. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtkutils.h" +#include "stock_pixmap.h" +#include "prefs_common.h" +#include "addressadd.h" +#include "addritem.h" +#include "addrbook.h" +#include "addrindex.h" +#include "editaddress.h" +#include "manage_window.h" +#include "utils.h" + +typedef struct { + AddressBookFile *book; + ItemFolder *folder; +} FolderInfo; + +static struct _AddressAdd_dlg { + GtkWidget *window; + GtkWidget *label_name; + GtkWidget *label_address; + GtkWidget *label_remarks; + GtkWidget *tree_folder; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + FolderInfo *fiSelected; +} addressadd_dlg; + +static GdkPixmap *folderXpm; +static GdkBitmap *folderXpmMask; +static GdkPixmap *bookXpm; +static GdkBitmap *bookXpmMask; + +static gboolean addressadd_cancelled; + +static FolderInfo *addressadd_create_folderinfo( AddressBookFile *abf, ItemFolder *folder ) +{ + FolderInfo *fi = g_new0( FolderInfo, 1 ); + fi->book = abf; + fi->folder = folder; + return fi; +} + +static void addressadd_free_folderinfo( FolderInfo *fi ) { + fi->book = NULL; + fi->folder = NULL; + g_free( fi ); +} + +static gint addressadd_delete_event( GtkWidget *widget, GdkEventAny *event, gboolean *cancelled ) { + addressadd_cancelled = TRUE; + gtk_main_quit(); + return TRUE; +} + +static gboolean addressadd_key_pressed( GtkWidget *widget, GdkEventKey *event, gboolean *cancelled ) { + if (event && event->keyval == GDK_Escape) { + addressadd_cancelled = TRUE; + gtk_main_quit(); + } + return FALSE; +} + +static void addressadd_ok( GtkWidget *widget, gboolean *cancelled ) { + addressadd_cancelled = FALSE; + gtk_main_quit(); +} + +static void addressadd_cancel( GtkWidget *widget, gboolean *cancelled ) { + addressadd_cancelled = TRUE; + gtk_main_quit(); +} + +static void addressadd_folder_select( GtkCTree *ctree, gint row, gint column, + GdkEvent *event, gpointer data ) +{ + addressadd_dlg.fiSelected = gtk_clist_get_row_data( GTK_CLIST(ctree), row ); +} + +static gboolean addressadd_tree_button( GtkCTree *ctree, GdkEventButton *event, gpointer data ) { + if( ! event ) return FALSE; + if( event->button == 1 ) { + /* Handle double click */ + if( event->type == GDK_2BUTTON_PRESS ) { + addressadd_cancelled = FALSE; + gtk_main_quit(); + } + } + + return FALSE; +} + +static void addressadd_create( void ) { + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *label; + GtkWidget *label_name; + GtkWidget *label_addr; + GtkWidget *label_rems; + GtkWidget *tree_folder; + GtkWidget *vlbox; + GtkWidget *tree_win; + GtkWidget *hbbox; + GtkWidget *hsep; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + gint top; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request(window, + 300 * gtkut_get_dpi_multiplier(), + 360 * gtkut_get_dpi_multiplier()); + gtk_container_set_border_width(GTK_CONTAINER(window), 0); + gtk_window_set_title(GTK_WINDOW(window), _("Add Address to Book")); + gtk_window_set_position(GTK_WINDOW(window), + GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_widget_realize(window); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(addressadd_delete_event), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(addressadd_key_pressed), NULL); + + vbox = gtk_vbox_new(FALSE, 4); + gtk_container_add(GTK_CONTAINER(window), vbox); + gtk_container_set_border_width( GTK_CONTAINER(vbox), 0 ); + + table = gtk_table_new(3, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 8 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 4); + gtk_table_set_col_spacings(GTK_TABLE(table), 4); + + /* First row */ + top = 0; + label = gtk_label_new(_("Name")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + label_name = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), label_name, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label_name), 0, 0.5); + + /* Second row */ + top = 1; + label = gtk_label_new(_("Address")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + + label_addr = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), label_addr, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label_addr), 0, 0.5); + + /* Third row */ + top = 2; + label = gtk_label_new(_("Remarks")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + label_rems = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), label_rems, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label_rems), 0, 0.5); + + /* Address book/folder tree */ + vlbox = gtk_vbox_new(FALSE, 4); + gtk_box_pack_start(GTK_BOX(vbox), vlbox, TRUE, TRUE, 0); + gtk_container_set_border_width( GTK_CONTAINER(vlbox), 4 ); + + tree_win = gtk_scrolled_window_new( NULL, NULL ); + gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(tree_win), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC ); + gtk_box_pack_start( GTK_BOX(vlbox), tree_win, TRUE, TRUE, 0 ); + + tree_folder = gtk_ctree_new( 1, 0 ); + gtk_container_add( GTK_CONTAINER(tree_win), tree_folder ); + gtk_clist_column_titles_show( GTK_CLIST(tree_folder) ); + gtk_clist_set_column_title( GTK_CLIST(tree_folder), 0, _( "Select Address Book Folder" ) ); + gtk_ctree_set_line_style( GTK_CTREE(tree_folder), GTK_CTREE_LINES_DOTTED ); + gtk_clist_set_selection_mode( GTK_CLIST(tree_folder), GTK_SELECTION_BROWSE ); + gtk_ctree_set_expander_style( GTK_CTREE(tree_folder), GTK_CTREE_EXPANDER_SQUARE ); + gtk_ctree_set_indent( GTK_CTREE(tree_folder), CTREE_INDENT ); + gtk_clist_set_auto_sort( GTK_CLIST(tree_folder), TRUE ); + gtkut_clist_set_redraw( GTK_CLIST(tree_folder) ); + + /* Button panel */ + gtkut_stock_button_set_create(&hbbox, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(hbbox), 4 ); + gtk_widget_grab_default(ok_btn); + + hsep = gtk_hseparator_new(); + gtk_box_pack_end(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(addressadd_ok), NULL); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(addressadd_cancel), NULL); + g_signal_connect(G_OBJECT(tree_folder), "select_row", + G_CALLBACK(addressadd_folder_select), NULL); + g_signal_connect(G_OBJECT(tree_folder), "button_press_event", + G_CALLBACK(addressadd_tree_button), NULL); + + addressadd_dlg.window = window; + addressadd_dlg.label_name = label_name; + addressadd_dlg.label_address = label_addr; + addressadd_dlg.label_remarks = label_rems; + addressadd_dlg.tree_folder = tree_folder; + addressadd_dlg.ok_btn = ok_btn; + addressadd_dlg.cancel_btn = cancel_btn; + + gtk_widget_show_all(vbox); + +#if 0 + stock_pixmap_gdk(window, STOCK_PIXMAP_BOOK, &bookXpm, &bookXpmMask); + stock_pixmap_gdk(window, STOCK_PIXMAP_FOLDER_OPEN, + &folderXpm, &folderXpmMask); +#endif +} + +static void addressadd_load_folder( GtkCTreeNode *parentNode, ItemFolder *parentFolder, + FolderInfo *fiParent ) +{ + GtkCTree *tree = GTK_CTREE( addressadd_dlg.tree_folder ); + GList *list; + ItemFolder *folder; + gchar *fName; + gchar **name; + GtkCTreeNode *node; + FolderInfo *fi; + + list = parentFolder->listFolder; + while( list ) { + folder = list->data; + fName = g_strdup( ADDRITEM_NAME(folder) ); + name = &fName; + node = gtk_ctree_insert_node( tree, parentNode, NULL, name, FOLDER_SPACING, + folderXpm, folderXpmMask, folderXpm, folderXpmMask, + FALSE, TRUE ); + g_free( fName ); + fi = addressadd_create_folderinfo( fiParent->book, folder ); + gtk_ctree_node_set_row_data_full( tree, node, fi, + ( GtkDestroyNotify ) addressadd_free_folderinfo ); + addressadd_load_folder( node, folder, fi ); + list = g_list_next( list ); + } +} + +static void addressadd_load_data( AddressIndex *addrIndex ) { + AddressDataSource *ds; + GList *list, *nodeDS; + gchar **name; + gchar *dsName; + ItemFolder *rootFolder; + AddressBookFile *abf; + FolderInfo *fi; + GtkCTree *tree = GTK_CTREE( addressadd_dlg.tree_folder ); + GtkCTreeNode *node; + + gtk_clist_clear( GTK_CLIST( tree ) ); + list = addrindex_get_interface_list( addrIndex ); + while( list ) { + AddressInterface *iface = list->data; + if( iface->type == ADDR_IF_BOOK ) { + nodeDS = iface->listSource; + while( nodeDS ) { + ds = nodeDS->data; + if ( !strcmp( addrindex_ds_get_name( ds ), ADDR_DS_AUTOREG ) ) + dsName = g_strdup( _("Auto-registered address") ); + else + dsName = g_strdup( addrindex_ds_get_name( ds ) ); + + /* Read address book */ + if( ! addrindex_ds_get_read_flag( ds ) ) { + addrindex_ds_read_data( ds ); + } + + /* Add node for address book */ + abf = ds->rawDataSource; + name = &dsName; + node = gtk_ctree_insert_node( tree, NULL, NULL, + name, FOLDER_SPACING, bookXpm, + bookXpmMask, bookXpm, bookXpmMask, + FALSE, TRUE ); + g_free( dsName ); + + fi = addressadd_create_folderinfo( abf, NULL ); + gtk_ctree_node_set_row_data_full( tree, node, fi, + ( GtkDestroyNotify ) addressadd_free_folderinfo ); + + rootFolder = addrindex_ds_get_root_folder( ds ); + addressadd_load_folder( node, rootFolder, fi ); + + nodeDS = g_list_next( nodeDS ); + } + } + list = g_list_next( list ); + } +} + +gboolean addressadd_selection( AddressIndex *addrIndex, const gchar *name, const gchar *address, const gchar *remarks ) { + gboolean retVal = FALSE; + ItemPerson *person = NULL; + + addressadd_cancelled = FALSE; + if( ! addressadd_dlg.window ) addressadd_create(); + gtk_widget_grab_focus(addressadd_dlg.ok_btn); + manage_window_set_transient(GTK_WINDOW(addressadd_dlg.window)); + gtk_widget_show(addressadd_dlg.window); + + addressadd_dlg.fiSelected = NULL; + addressadd_load_data( addrIndex ); + gtk_clist_select_row( GTK_CLIST( addressadd_dlg.tree_folder ), 0, 0 ); + gtk_widget_show(addressadd_dlg.window); + + gtk_label_set_text( GTK_LABEL(addressadd_dlg.label_name ), "" ); + gtk_label_set_text( GTK_LABEL(addressadd_dlg.label_address ), "" ); + gtk_label_set_text( GTK_LABEL(addressadd_dlg.label_remarks ), "" ); + if( name ) + gtk_label_set_text( GTK_LABEL(addressadd_dlg.label_name ), name ); + if( address ) + gtk_label_set_text( GTK_LABEL(addressadd_dlg.label_address ), address ); + if( remarks ) + gtk_label_set_text( GTK_LABEL(addressadd_dlg.label_remarks ), remarks ); + + if( ! name ) + name = ""; + + gtk_main(); + gtk_widget_hide( addressadd_dlg.window ); + + if( ! addressadd_cancelled ) { + if( addressadd_dlg.fiSelected ) { + FolderInfo *fi = addressadd_dlg.fiSelected; + person = addrbook_add_contact( fi->book, fi->folder, name, address, remarks ); + if( person ) { + if( addressbook_edit_person( fi->book, NULL, person, FALSE ) == NULL ) + addrbook_remove_person( fi->book, person ); + else + retVal = TRUE; + } + } + } + + gtk_clist_clear( GTK_CLIST( addressadd_dlg.tree_folder ) ); + + return retVal; +} + +gboolean addressadd_autoreg(AddressIndex *addrIndex, const gchar *name, + const gchar *address, const gchar *remarks) +{ + ItemPerson *person = NULL; + AddressInterface *iface = NULL; + AddressDataSource *ds = NULL; + AddressBookFile *abf = NULL; + GList *node_ds; + const gchar *ds_name; + + g_return_val_if_fail(address != NULL, FALSE); + + if (!name) + name = ""; + + iface = addrindex_get_interface(addrIndex, ADDR_IF_BOOK); + if (!iface) + return FALSE; + + for (node_ds = iface->listSource; node_ds != NULL; + node_ds = node_ds->next) { + ds = node_ds->data; + ds_name = addrindex_ds_get_name(ds); + if (!ds_name) + continue; + if (strcmp(ds_name, ADDR_DS_AUTOREG) != 0) + continue; + debug_print("addressadd_autoreg: AddressDataSource: %s found\n", ds_name); + + if (!addrindex_ds_get_read_flag(ds)) + addrindex_ds_read_data(ds); + abf = ds->rawDataSource; + } + + if (!abf) + return FALSE; + + person = addrbook_add_contact(abf, NULL, name, address, remarks); + if (person) { + debug_print("addressadd_autoreg: person added: %s <%s>\n", + name, address); + return TRUE; + } + + return FALSE; +} + +/* +* End of Source. +*/ + diff --git a/src/addressadd.h b/src/addressadd.h new file mode 100644 index 0000000..748d1ef --- /dev/null +++ b/src/addressadd.h @@ -0,0 +1,33 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Add address to address book dialog. + */ + +#ifndef __ADDRESS_ADD_H__ +#define __ADDRESS_ADD_H__ + +#include "addrindex.h" + +gboolean addressadd_selection( AddressIndex *addrIndex, const gchar *name, const gchar *address, const gchar *remarks ); + +gboolean addressadd_autoreg( AddressIndex *addrIndex, const gchar *name, const gchar *address, const gchar *remarks ); + +#endif /* __ADDRESS_ADD_H__ */ diff --git a/src/addressbook.c b/src/addressbook.c new file mode 100644 index 0000000..b013507 --- /dev/null +++ b/src/addressbook.c @@ -0,0 +1,4919 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2017 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "addressbook.h" +#include "manage_window.h" +#include "prefs_common.h" +#include "alertpanel.h" +#include "inputdialog.h" +#include "menu.h" +#include "stock_pixmap.h" +#include "xml.h" +#include "prefs.h" +#include "procmime.h" +#include "utils.h" +#include "gtkutils.h" +#include "codeconv.h" +#include "about.h" +#include "addr_compl.h" + +#include "mgutils.h" +#include "addressitem.h" +#include "addritem.h" +#include "addrcache.h" +#include "addrbook.h" +#include "addrindex.h" +#include "addressadd.h" +#include "vcard.h" +#include "editvcard.h" +#include "editgroup.h" +#include "editaddress.h" +#include "editbook.h" +#include "ldif.h" +#include "importldif.h" +#include "importcsv.h" +#include "exportcsv.h" + +#ifdef USE_JPILOT +#include "jpilot.h" +#include "editjpilot.h" +#endif + +#ifdef USE_LDAP +#include +#include "syldap.h" +#include "editldap.h" + +#define ADDRESSBOOK_LDAP_BUSYMSG "Busy" +#endif + +typedef enum +{ + COL_FOLDER_NAME, + COL_OBJ, + COL_PIXBUF, + COL_PIXBUF_OPEN, + + N_TREE_COLS +} AddressBookTreeColumnPos; + +typedef enum +{ + COL_NAME, + COL_ADDRESS, + COL_NICKNAME, + COL_REMARKS, + COL_L_OBJ, + COL_L_PIXBUF, + + N_LIST_COLS +} AddressBookListColumnPos; + +enum +{ + DRAG_TYPE_OBJ, + + N_DRAG_TYPES +}; + +#define COL_NAME_WIDTH 164 +#define COL_ADDRESS_WIDTH 156 + +#define COL_FOLDER_WIDTH 170 + +#define ADDRESSBOOK_MSGBUF_SIZE 2048 + +static GdkPixbuf *folderpix; +static GdkPixbuf *folderopenpix; +static GdkPixbuf *grouppix; +static GdkPixbuf *interfacepix; +static GdkPixbuf *bookpix; +static GdkPixbuf *personpix; +static GdkPixbuf *addresspix; +static GdkPixbuf *vcardpix; +static GdkPixbuf *jpilotpix; +static GdkPixbuf *categorypix; +static GdkPixbuf *ldappix; + +/* Address list selection */ +static GList *_addressListSelection_ = NULL; + +static gboolean can_toggle_list_selection = TRUE; +static gboolean list_on_drag = FALSE; + +/* Address index file and interfaces */ +static AddressIndex *_addressIndex_ = NULL; +static GList *_addressInterfaceList_ = NULL; +static GList *_addressIFaceSelection_ = NULL; +#define ADDRESSBOOK_IFACE_SELECTION "1/y,3/y,4/y,2/n" + +/* Address clipboard */ +static GList *_clipObjectList_ = NULL; + +static AddressBook_win addrbook; + +static GHashTable *_addressBookTypeHash_ = NULL; +static GList *_addressBookTypeList_ = NULL; + +static void addressbook_refresh (void); +static void addressbook_reopen (void); + +static void addressbook_create (void); +static gint addressbook_close (void); + +static void addressbook_menuitem_set_sensitive (void); + +/* callback functions */ +static void addressbook_del_clicked (GtkButton *button, + gpointer data); +static void addressbook_reg_clicked (GtkButton *button, + gpointer data); +static void addressbook_to_clicked (GtkButton *button, + gpointer data); +static void addressbook_lup_clicked (GtkButton *button, + gpointer data); +static void addressbook_close_clicked (GtkButton *button, + gpointer data); + +static void addressbook_tree_selection_changed (GtkTreeSelection *selection, + gpointer data); +static void addressbook_list_selection_changed (GtkTreeSelection *selection, + gpointer data); +static void addressbook_person_expand_node (GtkTreeView *treeview, + GtkTreeIter *iter, + GtkTreePath *path, + gpointer *data); +static void addressbook_person_collapse_node (GtkTreeView *treeview, + GtkTreeIter *iter, + GtkTreePath *path, + gpointer *data); + +static void addressbook_drag_begin (GtkWidget *widget, + GdkDragContext *drag_context, + gpointer data); +static void addressbook_drag_end (GtkWidget *widget, + GdkDragContext *drag_context, + gpointer data); +static void addressbook_drag_data_get (GtkWidget *widget, + GdkDragContext *drag_context, + GtkSelectionData *selection_data, + guint info, + guint time, + gpointer data); + +#if 0 +static void addressbook_entry_changed (GtkWidget *widget); +#endif +static void addressbook_entry_activated (GtkWidget *widget, + gpointer data); + +static gboolean addressbook_list_button_pressed (GtkWidget *widget, + GdkEventButton *event, + gpointer data); +static gboolean addressbook_list_button_released(GtkWidget *widget, + GdkEventButton *event, + gpointer data); +static gboolean addressbook_tree_button_pressed (GtkWidget *ctree, + GdkEventButton *event, + gpointer data); +static gboolean addressbook_tree_button_released(GtkWidget *ctree, + GdkEventButton *event, + gpointer data); + +static gboolean addressbook_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time, + gpointer data); +static void addressbook_drag_leave (GtkWidget *widget, + GdkDragContext *context, + guint time, + gpointer data); +static void addressbook_drag_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + gpointer user_data); + +static void addressbook_folder_resized (GtkWidget *widget, + GtkAllocation *allocation, + gpointer data); +static void addressbook_col_resized (GtkWidget *widget, + GtkAllocation *allocation, + gpointer data); + +static void addressbook_popup_close (GtkMenuShell *menu_shell, + gpointer data); + +static void addressbook_new_folder_cb (gpointer data, + guint action, + GtkWidget *widget); +static void addressbook_new_group_cb (gpointer data, + guint action, + GtkWidget *widget); +static void addressbook_treenode_edit_cb (gpointer data, + guint action, + GtkWidget *widget); +static void addressbook_treenode_delete_cb (gpointer data, + guint action, + GtkWidget *widget); + +static void addressbook_change_node_name (GtkTreeIter *iter, + const gchar *name); + +static void addressbook_new_address_cb (gpointer data, + guint action, + GtkWidget *widget); +static void addressbook_compose_to_cb (gpointer data, + guint action, + GtkWidget *widget); +static void addressbook_edit_address_cb (gpointer data, + guint action, + GtkWidget *widget); +static void addressbook_delete_address_cb (gpointer data, + guint action, + GtkWidget *widget); +static void addressbook_copy_address_cb (gpointer data, + guint action, + GtkWidget *widget); +static void addressbook_paste_address_cb (gpointer data, + guint action, + GtkWidget *widget); + +static void close_cb (gpointer data, + guint action, + GtkWidget *widget); +static void addressbook_file_save_cb (gpointer data, + guint action, + GtkWidget *widget); + +/* Data source edit stuff */ +static void addressbook_new_book_cb (gpointer data, + guint action, + GtkWidget *widget); +static void addressbook_new_vcard_cb (gpointer data, + guint action, + GtkWidget *widget); + +#ifdef USE_JPILOT +static void addressbook_new_jpilot_cb (gpointer data, + guint action, + GtkWidget *widget); +#endif + +#ifdef USE_LDAP +static void addressbook_new_ldap_cb (gpointer data, + guint action, + GtkWidget *widget); +#endif + +static void addressbook_set_list (AddressObject *obj); + +static void addressbook_load_tree (void); +void addressbook_read_file (void); + +static gboolean addressbook_add_object (GtkTreeIter *iter, + GtkTreeIter *new_iter, + AddressObject *obj); +static AddressDataSource *addressbook_find_datasource + (GtkTreeIter *iter); + +static AddressBookFile *addressbook_get_book_file(); + +static gboolean addressbook_node_add_folder (GtkTreeIter *iter, + AddressDataSource *ds, + ItemFolder *itemFolder, + AddressObjectType otype, + GtkTreeIter *new_iter); +static gboolean addressbook_node_add_group (GtkTreeIter *iter, + AddressDataSource *ds, + ItemGroup *itemGroup, + GtkTreeIter *new_iter); +static void addressbook_tree_remove_children (GtkTreeModel *model, + GtkTreeIter *parent); +static void addressbook_move_nodes_up (GtkTreeIter *iter); +static gboolean addressbook_find_group_node (GtkTreeIter *parent, + GtkTreeIter *iter, + ItemGroup *group); + +/* static void addressbook_delete_object (AddressObject *obj); */ + +static gboolean key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); +static void size_allocated (GtkWidget *widget, + GtkAllocation *allocation, + gpointer data); + +static gint addressbook_tree_compare (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint addressbook_list_name_compare (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint addressbook_list_address_compare (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint addressbook_list_nickname_compare (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint addressbook_list_remarks_compare (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); + +static gboolean addressbook_list_select_func (GtkTreeSelection *selection, + GtkTreeModel *model, + GtkTreePath *path, + gboolean cur_selected, + gpointer data); + +/* static void addressbook_book_show_message (AddressBookFile *book); */ +/* static void addressbook_vcard_show_message (VCardFile *vcf); */ +#ifdef USE_JPILOT +/* static void addressbook_jpilot_show_message (JPilotFile *jpf); */ +#endif +#ifdef USE_LDAP +static void addressbook_ldap_show_message (SyldapServer *server); +#endif + +/* LUT's and IF stuff */ +static void addressbook_free_adapter (GtkTreeIter *iter); +static void addressbook_free_child_adapters (GtkTreeIter *iter); +AddressTypeControlItem *addrbookctl_lookup (gint ot); +AddressTypeControlItem *addrbookctl_lookup_iface(AddressIfType ifType); + +void addrbookctl_build_map (GtkWidget *window); +void addrbookctl_build_iflist (void); +AdapterInterface *addrbookctl_find_interface (AddressIfType ifType); +void addrbookctl_build_ifselect (void); + +static void addrbookctl_free_interface (AdapterInterface *adapter); +static void addrbookctl_free_datasource (AdapterDSource *adapter); +static void addrbookctl_free_folder (AdapterFolder *adapter); +static void addrbookctl_free_group (AdapterGroup *adapter); + +static void addressbook_list_select_clear (void); +static void addressbook_list_select_remove (AddressObject *obj); +static void addressbook_list_select_set (GList *row_list); + +static void addressbook_import_ldif_cb (void); +static void addressbook_import_csv_cb (void); + +static void addressbook_export_csv_cb (void); + +static void addressbook_modified (void); + + +static GtkTargetEntry addressbook_drag_types[] = +{ + {"application/octet-stream", GTK_TARGET_SAME_APP, DRAG_TYPE_OBJ} +}; + +static GtkItemFactoryEntry addressbook_entries[] = +{ + {N_("/_File"), NULL, NULL, 0, ""}, + {N_("/_File/New _Book"), NULL, addressbook_new_book_cb, 0, NULL}, + {N_("/_File/New _vCard"), NULL, addressbook_new_vcard_cb, 0, NULL}, +#ifdef USE_JPILOT + {N_("/_File/New _JPilot"), NULL, addressbook_new_jpilot_cb, 0, NULL}, +#endif +#ifdef USE_LDAP + {N_("/_File/New _LDAP Server"), NULL, addressbook_new_ldap_cb, 0, NULL}, +#endif + {N_("/_File/---"), NULL, NULL, 0, ""}, + {N_("/_File/_Edit"), NULL, addressbook_treenode_edit_cb, 0, NULL}, + {N_("/_File/_Delete"), NULL, addressbook_treenode_delete_cb, 0, NULL}, + {N_("/_File/---"), NULL, NULL, 0, ""}, + {N_("/_File/_Save"), "S", addressbook_file_save_cb, 0, NULL}, + {N_("/_File/_Close"), "W", close_cb, 0, NULL}, + + {N_("/_Edit"), NULL, NULL, 0, ""}, + {N_("/_Edit/_Copy"), "C", addressbook_copy_address_cb, 0, NULL}, + {N_("/_Edit/_Paste"), "V", addressbook_paste_address_cb, 0, NULL}, + + {N_("/_Address"), NULL, NULL, 0, ""}, + {N_("/_Address/New _Address"), "N", addressbook_new_address_cb, 0, NULL}, + {N_("/_Address/New _Group"), "G", addressbook_new_group_cb, 0, NULL}, + {N_("/_Address/New _Folder"), "F", addressbook_new_folder_cb, 0, NULL}, + {N_("/_Address/---"), NULL, NULL, 0, ""}, + {N_("/_Address/Add _to recipient"), + "M", addressbook_compose_to_cb, COMPOSE_ENTRY_TO, NULL}, + {N_("/_Address/Add to _Cc"), + NULL, addressbook_compose_to_cb, COMPOSE_ENTRY_CC, NULL}, + {N_("/_Address/Add to _Bcc"), + NULL, addressbook_compose_to_cb, COMPOSE_ENTRY_BCC, NULL}, + {N_("/_Address/---"), NULL, NULL, 0, ""}, + {N_("/_Address/_Edit"), "Return", addressbook_edit_address_cb, 0, NULL}, + {N_("/_Address/_Delete"), "Delete", addressbook_delete_address_cb, 0, NULL}, + + {N_("/_Tools"), NULL, NULL, 0, ""}, + {N_("/_Tools/Import _LDIF file"), NULL, addressbook_import_ldif_cb, 0, NULL}, + {N_("/_Tools/Import _CSV file"), NULL, addressbook_import_csv_cb, 0, NULL}, + {N_("/_Tools/---"), NULL, NULL, 0, ""}, + {N_("/_Tools/Export to C_SV file"), NULL, addressbook_export_csv_cb, 0, NULL}, + {N_("/_Help"), NULL, NULL, 0, ""}, + {N_("/_Help/_About"), NULL, about_show, 0, NULL} +}; + +/* New options to be added. */ +/* + {N_("/_Edit"), NULL, NULL, 0, ""}, + {N_("/_Edit/C_ut"), "X", NULL, 0, NULL}, + {N_("/_Tools"), NULL, NULL, 0, ""}, + {N_("/_Tools/Import _Mozilla"), NULL, NULL, 0, NULL}, + {N_("/_Tools/Import _vCard"), NULL, NULL, 0, NULL}, + {N_("/_Tools/---"), NULL, NULL, 0, ""}, + {N_("/_Tools/Export _LDIF file"), NULL, NULL, 0, NULL}, + {N_("/_Tools/Export v_Card"), NULL, NULL, 0, NULL}, +*/ + +static GtkItemFactoryEntry addressbook_tree_popup_entries[] = +{ + {N_("/New _Address"), NULL, addressbook_new_address_cb, 0, NULL}, + {N_("/New _Group"), NULL, addressbook_new_group_cb, 0, NULL}, + {N_("/New _Folder"), NULL, addressbook_new_folder_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Edit"), NULL, addressbook_treenode_edit_cb, 0, NULL}, + {N_("/_Delete"), NULL, addressbook_treenode_delete_cb, 0, NULL} +}; + +static GtkItemFactoryEntry addressbook_list_popup_entries[] = +{ + {N_("/New _Address"), NULL, addressbook_new_address_cb, 0, NULL}, + {N_("/New _Group"), NULL, addressbook_new_group_cb, 0, NULL}, + {N_("/New _Folder"), NULL, addressbook_new_folder_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/Add _to recipient"), + NULL, addressbook_compose_to_cb, COMPOSE_ENTRY_TO, NULL}, + {N_("/Add t_o Cc"), + NULL, addressbook_compose_to_cb, COMPOSE_ENTRY_CC, NULL}, + {N_("/Add to _Bcc"), + NULL, addressbook_compose_to_cb, COMPOSE_ENTRY_BCC, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Edit"), NULL, addressbook_edit_address_cb, 0, NULL}, + {N_("/_Delete"), NULL, addressbook_delete_address_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Copy"), NULL, addressbook_copy_address_cb, 0, NULL}, + {N_("/_Paste"), NULL, addressbook_paste_address_cb, 0, NULL} +}; + + +void addressbook_open(Compose *target) +{ + if (!addrbook.window) { + GtkTreeView *treeview; + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path; + + addressbook_read_file(); + addressbook_create(); + addressbook_load_tree(); + treeview = GTK_TREE_VIEW(addrbook.treeview); + model = gtk_tree_view_get_model(treeview); + if (gtk_tree_model_get_iter_first(model, &iter)) { + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_view_set_cursor(treeview, path, NULL, FALSE); + gtk_tree_path_free(path); + } + addressbook_menuitem_set_sensitive(); + gtk_widget_show(addrbook.window); + } + + gtk_window_present(GTK_WINDOW(addrbook.window)); + + addressbook_set_target_compose(target); +} + +void addressbook_set_target_compose(Compose *target) +{ + addrbook.target_compose = target; +} + +Compose *addressbook_get_target_compose(void) +{ + return addrbook.target_compose; +} + +static void addressbook_refresh(void) +{ + if (addrbook.window) { + if (addrbook.tree_opened) { + addressbook_reopen(); + } + } + addressbook_export_to_file(); +} + +static void addressbook_reopen(void) +{ + GtkTreePath *path; + + if (addrbook.tree_selected) { + gtk_tree_row_reference_free(addrbook.tree_selected); + addrbook.tree_selected = NULL; + } + if (addrbook.tree_opened) { + path = gtk_tree_row_reference_get_path(addrbook.tree_opened); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(addrbook.treeview), path, NULL, FALSE); + gtk_tree_path_free(path); + } +} + +/* +* Create the address book widgets. The address book contains two CTree widgets: the +* address index tree on the left and the address list on the right. +* +* The address index tree displays a hierarchy of interfaces and groups. Each node in +* this tree is linked to an address Adapter. Adapters have been created for interfaces, +* data sources and folder objects. +* +* The address list displays group, person and email objects. These items are linked +* directly to ItemGroup, ItemPerson and ItemEMail objects inside the address book data +* sources. +* +* In the tradition of MVC architecture, the data stores have been separated from the +* GUI components. The addrindex.c file provides the interface to all data stores. +*/ +static void addressbook_create(void) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *menubar; + GtkWidget *spc_hbox; + GtkWidget *vbox2; + GtkWidget *tree_swin; + GtkWidget *treeview; + GtkTreeStore *tree_store; + GtkTreeSelection *selection; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkWidget *list_vbox; + GtkWidget *list_swin; + GtkWidget *listview; + GtkTreeStore *list_store; + GtkWidget *paned; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *entry; + GtkWidget *statusbar; + GtkWidget *hbbox; + GtkWidget *hbbox1; + GtkWidget *hbbox2; + GtkWidget *hsbox; + GtkWidget *to_btn; + GtkWidget *cc_btn; + GtkWidget *bcc_btn; + GtkWidget *del_btn; + GtkWidget *reg_btn; + GtkWidget *lup_btn; + GtkWidget *close_btn; + GtkWidget *tree_popup; + GtkWidget *list_popup; + GtkItemFactory *tree_factory; + GtkItemFactory *list_factory; + GtkItemFactory *menu_factory; + gint n_entries; + GList *nodeIf; + + debug_print("Creating addressbook window...\n"); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), _("Address book")); + gtk_window_set_wmclass(GTK_WINDOW(window), "addressbook", "Sylpheed"); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, TRUE); + gtk_widget_set_size_request(window, 620 * gtkut_get_dpi_multiplier(), 360 * gtkut_get_dpi_multiplier()); + gtk_window_set_default_size(GTK_WINDOW(window), + prefs_common.addressbook_width, + prefs_common.addressbook_height); + gtkut_window_move(GTK_WINDOW(window), prefs_common.addressbook_x, + prefs_common.addressbook_y); + gtk_widget_realize(window); + + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(addressbook_close), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(key_pressed), NULL); + g_signal_connect(G_OBJECT(window), "size_allocate", + G_CALLBACK(size_allocated), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(window), vbox); + + n_entries = sizeof(addressbook_entries) / + sizeof(addressbook_entries[0]); + menubar = menubar_create(window, addressbook_entries, n_entries, + "", NULL); + gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0); + menu_factory = gtk_item_factory_from_widget(menubar); + + spc_hbox = gtk_hbox_new(FALSE, 0); + gtk_widget_set_size_request(spc_hbox, -1, BORDER_WIDTH); + gtk_box_pack_start(GTK_BOX(vbox), spc_hbox, FALSE, FALSE, 0); + + vbox2 = gtk_vbox_new(FALSE, 4); + gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0); + + tree_swin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(tree_swin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tree_swin), + GTK_SHADOW_IN); + gtk_widget_set_size_request(tree_swin, prefs_common.addressbook_folder_width, -1); + + /* Address index */ + tree_store = gtk_tree_store_new(N_TREE_COLS, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_PIXBUF, + GDK_TYPE_PIXBUF); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(tree_store), + COL_FOLDER_NAME, + addressbook_tree_compare, + NULL, NULL); + + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tree_store)); + g_object_unref(G_OBJECT(tree_store)); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), FALSE); + gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), + COL_FOLDER_NAME); + gtk_tree_view_set_reorderable(GTK_TREE_VIEW(treeview), FALSE); + + gtk_container_add(GTK_CONTAINER(tree_swin), treeview); + + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_spacing(column, 1); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_resizable(column, TRUE); + + renderer = gtk_cell_renderer_pixbuf_new(); + g_object_set(renderer, "ypad", 0, NULL); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_set_title(column, _("Folder")); + gtk_tree_view_column_set_attributes + (column, renderer, "pixbuf", COL_PIXBUF, + "pixbuf-expander-open", COL_PIXBUF_OPEN, + "pixbuf-expander-closed", COL_PIXBUF, NULL); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, +#if GTK_CHECK_VERSION(2, 6, 0) + "ellipsize", PANGO_ELLIPSIZE_END, +#endif + "ypad", 0, NULL); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_set_attributes(column, renderer, + "text", COL_FOLDER_NAME, NULL); + + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + gtk_tree_view_set_expander_column(GTK_TREE_VIEW(treeview), column); + + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(tree_store), + COL_FOLDER_NAME, + GTK_SORT_ASCENDING); + + g_signal_connect(G_OBJECT(tree_swin), "size-allocate", + G_CALLBACK(addressbook_folder_resized), NULL); + + g_signal_connect(G_OBJECT(selection), "changed", + G_CALLBACK(addressbook_tree_selection_changed), NULL); + g_signal_connect(G_OBJECT(treeview), "button_press_event", + G_CALLBACK(addressbook_tree_button_pressed), + NULL); + g_signal_connect(G_OBJECT(treeview), "button_release_event", + G_CALLBACK(addressbook_tree_button_released), + NULL); + + gtk_drag_dest_set(treeview, GTK_DEST_DEFAULT_ALL, + addressbook_drag_types, N_DRAG_TYPES, + GDK_ACTION_MOVE | GDK_ACTION_COPY); + g_signal_connect(G_OBJECT(treeview), "drag-motion", + G_CALLBACK(addressbook_drag_motion), NULL); + g_signal_connect(G_OBJECT(treeview), "drag-leave", + G_CALLBACK(addressbook_drag_leave), NULL); + g_signal_connect(G_OBJECT(treeview), "drag-data-received", + G_CALLBACK(addressbook_drag_received), NULL); + + list_vbox = gtk_vbox_new(FALSE, 4); + + list_swin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(list_swin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(list_swin), + GTK_SHADOW_IN); + gtk_box_pack_start(GTK_BOX(list_vbox), list_swin, TRUE, TRUE, 0); + + /* Address list */ + list_store = gtk_tree_store_new(N_LIST_COLS, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_POINTER, + GDK_TYPE_PIXBUF); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(list_store), + COL_NAME, + addressbook_list_name_compare, + NULL, NULL); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(list_store), + COL_ADDRESS, + addressbook_list_address_compare, + NULL, NULL); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(list_store), + COL_NICKNAME, + addressbook_list_nickname_compare, + NULL, NULL); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(list_store), + COL_REMARKS, + addressbook_list_remarks_compare, + NULL, NULL); + + listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store)); + g_object_unref(G_OBJECT(list_store)); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(listview)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + gtk_tree_selection_set_select_function + (selection, addressbook_list_select_func, NULL, NULL); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(listview), TRUE); + gtk_tree_view_set_search_column(GTK_TREE_VIEW(listview), COL_NAME); + gtk_tree_view_set_reorderable(GTK_TREE_VIEW(listview), FALSE); + gtk_container_add(GTK_CONTAINER(list_swin), listview); + + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_spacing(column, 1); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width + (column, prefs_common.addressbook_col_name); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_title(column, _("Name")); +#if GTK_CHECK_VERSION(2, 14, 0) + /* gtk_tree_view_column_set_expand(column, TRUE); */ +#endif + + renderer = gtk_cell_renderer_pixbuf_new(); + g_object_set(renderer, "ypad", 0, NULL); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_set_attributes(column, renderer, + "pixbuf", COL_L_PIXBUF, NULL); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, +#if GTK_CHECK_VERSION(2, 6, 0) + "ellipsize", PANGO_ELLIPSIZE_END, +#endif + "ypad", 0, NULL); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_set_attributes(column, renderer, + "text", COL_NAME, NULL); + + gtk_tree_view_column_set_sort_column_id(column, COL_NAME); + gtk_tree_view_append_column(GTK_TREE_VIEW(listview), column); + gtk_tree_view_set_expander_column(GTK_TREE_VIEW(listview), column); + g_signal_connect(G_OBJECT(column->button), "size-allocate", + G_CALLBACK(addressbook_col_resized), + GINT_TO_POINTER(COL_NAME)); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, +#if GTK_CHECK_VERSION(2, 6, 0) + "ellipsize", PANGO_ELLIPSIZE_END, +#endif + "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes + (_("E-Mail address"), renderer, "text", COL_ADDRESS, NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width + (column, prefs_common.addressbook_col_addr); + gtk_tree_view_column_set_resizable(column, TRUE); +#if GTK_CHECK_VERSION(2, 14, 0) + /* gtk_tree_view_column_set_expand(column, TRUE); */ +#endif + gtk_tree_view_column_set_sort_column_id(column, COL_ADDRESS); + gtk_tree_view_append_column(GTK_TREE_VIEW(listview), column); + g_signal_connect(G_OBJECT(column->button), "size-allocate", + G_CALLBACK(addressbook_col_resized), + GINT_TO_POINTER(COL_ADDRESS)); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, +#if GTK_CHECK_VERSION(2, 6, 0) + "ellipsize", PANGO_ELLIPSIZE_END, +#endif + "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes + (_("Nick Name"), renderer, "text", COL_NICKNAME, NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width + (column, prefs_common.addressbook_col_nickname); + gtk_tree_view_column_set_resizable(column, TRUE); +#if GTK_CHECK_VERSION(2, 14, 0) + /* gtk_tree_view_column_set_expand(column, TRUE); */ +#endif + gtk_tree_view_column_set_sort_column_id(column, COL_NICKNAME); + gtk_tree_view_append_column(GTK_TREE_VIEW(listview), column); + g_signal_connect(G_OBJECT(column->button), "size-allocate", + G_CALLBACK(addressbook_col_resized), + GINT_TO_POINTER(COL_NICKNAME)); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, +#if GTK_CHECK_VERSION(2, 6, 0) + "ellipsize", PANGO_ELLIPSIZE_END, +#endif + "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes + (_("Remarks"), renderer, "text", COL_REMARKS, NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_resizable(column, TRUE); +#if GTK_CHECK_VERSION(2, 14, 0) + /* gtk_tree_view_column_set_expand(column, TRUE); */ +#endif + gtk_tree_view_column_set_sort_column_id(column, COL_REMARKS); + gtk_tree_view_append_column(GTK_TREE_VIEW(listview), column); + g_signal_connect(G_OBJECT(column->button), "size-allocate", + G_CALLBACK(addressbook_col_resized), + GINT_TO_POINTER(COL_REMARKS)); + + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(list_store), + COL_NAME, GTK_SORT_ASCENDING); + + g_signal_connect(G_OBJECT(selection), "changed", + G_CALLBACK(addressbook_list_selection_changed), NULL); + g_signal_connect(G_OBJECT(listview), "button_press_event", + G_CALLBACK(addressbook_list_button_pressed), + NULL); + g_signal_connect(G_OBJECT(listview), "button_release_event", + G_CALLBACK(addressbook_list_button_released), + NULL); + g_signal_connect(G_OBJECT(listview), "row_expanded", + G_CALLBACK(addressbook_person_expand_node), NULL); + g_signal_connect(G_OBJECT(listview), "row_collapsed", + G_CALLBACK(addressbook_person_collapse_node), NULL); + + gtk_tree_view_enable_model_drag_source + (GTK_TREE_VIEW(listview), GDK_BUTTON1_MASK, + addressbook_drag_types, N_DRAG_TYPES, + GDK_ACTION_MOVE | GDK_ACTION_COPY); + + g_signal_connect_after(G_OBJECT(listview), "drag-begin", + G_CALLBACK(addressbook_drag_begin), NULL); + g_signal_connect_after(G_OBJECT(listview), "drag-end", + G_CALLBACK(addressbook_drag_end), NULL); + g_signal_connect(G_OBJECT(listview), "drag-data-get", + G_CALLBACK(addressbook_drag_data_get), NULL); + + hbox = gtk_hbox_new(FALSE, 4); + gtk_box_pack_start(GTK_BOX(list_vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new(_("Search:")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); + + address_completion_register_entry(GTK_ENTRY(entry)); + +#if 0 + g_signal_connect(G_OBJECT(entry), "changed", + G_CALLBACK(addressbook_entry_changed), NULL); +#endif + g_signal_connect(G_OBJECT(entry), "activate", + G_CALLBACK(addressbook_entry_activated), NULL); + + paned = gtk_hpaned_new(); + gtk_box_pack_start(GTK_BOX(vbox2), paned, TRUE, TRUE, 0); + gtk_paned_add1(GTK_PANED(paned), tree_swin); + gtk_paned_add2(GTK_PANED(paned), list_vbox); + + /* Status bar */ + hsbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, 0); + statusbar = gtk_statusbar_new(); + gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, 0); + + hbbox = gtk_hbox_new(FALSE, 4); + gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); + + /* Button panel */ + hbbox1 = gtk_hbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox1), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(hbbox1), 4); + gtk_container_set_border_width(GTK_CONTAINER(hbbox1), 4); + gtk_button_box_set_child_size(GTK_BUTTON_BOX(hbbox1), 64, -1); + gtk_box_pack_end(GTK_BOX(hbbox), hbbox1, FALSE, FALSE, 0); + + to_btn = gtk_button_new_with_label + (prefs_common.trans_hdr ? _("To:") : "To:"); + GTK_WIDGET_SET_FLAGS(to_btn, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(hbbox1), to_btn, TRUE, TRUE, 0); + cc_btn = gtk_button_new_with_label + (prefs_common.trans_hdr ? _("Cc:") : "Cc:"); + GTK_WIDGET_SET_FLAGS(cc_btn, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(hbbox1), cc_btn, TRUE, TRUE, 0); + bcc_btn = gtk_button_new_with_label + (prefs_common.trans_hdr ? _("Bcc:") : "Bcc:"); + GTK_WIDGET_SET_FLAGS(bcc_btn, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(hbbox1), bcc_btn, TRUE, TRUE, 0); + + g_signal_connect(G_OBJECT(to_btn), "clicked", + G_CALLBACK(addressbook_to_clicked), + GINT_TO_POINTER(COMPOSE_ENTRY_TO)); + g_signal_connect(G_OBJECT(cc_btn), "clicked", + G_CALLBACK(addressbook_to_clicked), + GINT_TO_POINTER(COMPOSE_ENTRY_CC)); + g_signal_connect(G_OBJECT(bcc_btn), "clicked", + G_CALLBACK(addressbook_to_clicked), + GINT_TO_POINTER(COMPOSE_ENTRY_BCC)); + + hbbox2 = gtk_hbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox2), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(hbbox2), 4); + gtk_container_set_border_width(GTK_CONTAINER(hbbox2), 4); + gtk_button_box_set_child_size(GTK_BUTTON_BOX(hbbox2), 64, -1); + gtk_box_pack_end(GTK_BOX(hbbox), hbbox2, TRUE, TRUE, 0); + + del_btn = gtk_button_new_with_label(_("Delete")); + GTK_WIDGET_SET_FLAGS(del_btn, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(hbbox2), del_btn, TRUE, TRUE, 0); + reg_btn = gtk_button_new_with_label(_("Add")); + GTK_WIDGET_SET_FLAGS(reg_btn, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(hbbox2), reg_btn, TRUE, TRUE, 0); + lup_btn = gtk_button_new_with_label(_("Search")); + GTK_WIDGET_SET_FLAGS(lup_btn, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(hbbox2), lup_btn, TRUE, TRUE, 0); + close_btn = gtk_button_new_with_mnemonic(_("_Close")); + GTK_WIDGET_SET_FLAGS(close_btn, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(hbbox2), close_btn, TRUE, TRUE, 0); + gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(hbbox2), close_btn, + TRUE); + + g_signal_connect(G_OBJECT(del_btn), "clicked", + G_CALLBACK(addressbook_del_clicked), NULL); + g_signal_connect(G_OBJECT(reg_btn), "clicked", + G_CALLBACK(addressbook_reg_clicked), NULL); + g_signal_connect(G_OBJECT(lup_btn), "clicked", + G_CALLBACK(addressbook_lup_clicked), NULL); + g_signal_connect(G_OBJECT(close_btn), "clicked", + G_CALLBACK(addressbook_close_clicked), NULL); + + /* Build icons for interface */ + stock_pixbuf_gdk(window, STOCK_PIXMAP_INTERFACE, &interfacepix); + /* Build control tables */ + addrbookctl_build_map(window); + addrbookctl_build_iflist(); + addrbookctl_build_ifselect(); + + /* Add each interface into the tree as a root level folder */ + nodeIf = _addressInterfaceList_; + while( nodeIf ) { + AdapterInterface *adapter = nodeIf->data; + AddressInterface *iface = adapter->iface; + + nodeIf = g_list_next(nodeIf); + + if(iface->useInterface) { + AddressTypeControlItem *atci = adapter->atci; + gchar *text = atci->displayName; + GtkTreeIter iter; + GtkTreePath *path; + + debug_print("addressbook_create: adapter: %s\n", text); + gtk_tree_store_append(tree_store, &iter, NULL); + gtk_tree_store_set(tree_store, &iter, + COL_FOLDER_NAME, text, + COL_OBJ, adapter, + COL_PIXBUF, interfacepix, + COL_PIXBUF_OPEN, interfacepix, -1); + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree_store), &iter); + adapter->tree_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(tree_store), path); + gtk_tree_path_free(path); + } + } + + /* Popup menu */ + n_entries = sizeof(addressbook_tree_popup_entries) / + sizeof(addressbook_tree_popup_entries[0]); + tree_popup = menu_create_items(addressbook_tree_popup_entries, + n_entries, + "", &tree_factory, + NULL); + g_signal_connect(G_OBJECT(tree_popup), "selection_done", + G_CALLBACK(addressbook_popup_close), NULL); + n_entries = sizeof(addressbook_list_popup_entries) / + sizeof(addressbook_list_popup_entries[0]); + list_popup = menu_create_items(addressbook_list_popup_entries, + n_entries, + "", &list_factory, + NULL); + + addrbook.window = window; + addrbook.menubar = menubar; + addrbook.treeview = treeview; + addrbook.listview = listview; + addrbook.entry = entry; + addrbook.statusbar = statusbar; + addrbook.status_cid = gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar), "Address Book Window"); + + addrbook.to_btn = to_btn; + addrbook.cc_btn = cc_btn; + addrbook.bcc_btn = bcc_btn; + addrbook.del_btn = del_btn; + addrbook.reg_btn = reg_btn; + addrbook.lup_btn = lup_btn; + addrbook.close_btn = close_btn; + + addrbook.tree_popup = tree_popup; + addrbook.list_popup = list_popup; + addrbook.tree_factory = tree_factory; + addrbook.list_factory = list_factory; + addrbook.menu_factory = menu_factory; + + addrbook.tree_selected = NULL; + addrbook.list_selected = NULL; + address_completion_start(window); + gtk_widget_show_all(window); +} + +static gint addressbook_close(void) +{ + gtkut_widget_get_uposition(addrbook.window, + &prefs_common.addressbook_x, + &prefs_common.addressbook_y); + gtk_widget_hide(addrbook.window); + addressbook_export_to_file(); + return TRUE; +} + +static void addressbook_status_show(const gchar *msg) +{ + if (addrbook.statusbar != NULL) { + gtk_statusbar_pop(GTK_STATUSBAR(addrbook.statusbar), addrbook.status_cid); + if (msg) { + gtk_statusbar_push(GTK_STATUSBAR(addrbook.statusbar), addrbook.status_cid, msg); + } + } +} + +static void addressbook_ds_show_message(AddressDataSource *ds) +{ + gint retVal; + gchar *name; + gchar msgbuf[ADDRESSBOOK_MSGBUF_SIZE] = ""; + + if (ds) { + name = addrindex_ds_get_name(ds); + if (ds->type == ADDR_IF_BOOK && name && + !strcmp(name, ADDR_DS_AUTOREG)) + name = _("Auto-registered address"); + retVal = addrindex_ds_get_status_code(ds); + if (retVal == MGU_SUCCESS) { + g_snprintf(msgbuf, sizeof(msgbuf), "%s", name); + } else { + g_snprintf(msgbuf, sizeof(msgbuf), "%s: %s", + name, mgu_error2string(retVal)); + } + } + + addressbook_status_show(msgbuf); +} + +/* +* Delete one or more objects from address list. +*/ +static void addressbook_del_clicked(GtkButton *button, gpointer data) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreeView *listview = GTK_TREE_VIEW(addrbook.listview); + GtkTreeModel *lmodel; + GtkTreeIter list_sel; + AddressObject *pobj = NULL; + AddressObject *obj = NULL; + AdapterDSource *ads = NULL; + gboolean procFlag; + AlertValue aval; + AddressBookFile *abf = NULL; + AddressDataSource *ds = NULL; + + if (!addrbook.tree_opened) + return; + + model = gtk_tree_view_get_model(treeview); + lmodel = gtk_tree_view_get_model(listview); + selection = gtk_tree_view_get_selection(treeview); + + gtkut_tree_row_reference_get_iter(model, addrbook.tree_opened, &iter); + gtk_tree_model_get(model, &iter, COL_OBJ, &pobj, -1); + g_return_if_fail(pobj != NULL); + + gtkut_tree_row_reference_get_iter(lmodel, addrbook.list_selected, + &list_sel); + gtk_tree_model_get(lmodel, &list_sel, COL_L_OBJ, &obj, -1); + if (obj == NULL) + return; + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) + return; + ds = addressbook_find_datasource(&iter); + if (ds == NULL) + return; + + procFlag = FALSE; + if (pobj->type == ADDR_DATASOURCE) { + ads = ADAPTER_DSOURCE(pobj); + if(ads->subType == ADDR_BOOK) + procFlag = TRUE; + } + else if (pobj->type == ADDR_ITEM_FOLDER) { + procFlag = TRUE; + } else if (pobj->type == ADDR_ITEM_GROUP) { + procFlag = TRUE; + } + if (!procFlag) + return; + abf = ds->rawDataSource; + if (abf == NULL) + return; + + /* Confirm deletion */ + aval = alertpanel(_("Delete address(es)"), + _("Really delete the address(es)?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if (aval != G_ALERTDEFAULT) + return; + + /* Process deletions */ + if (pobj->type == ADDR_DATASOURCE || pobj->type == ADDR_ITEM_FOLDER) { + /* Items inside folders */ + GList *node; + + node = _addressListSelection_; + while (node) { + AddrItemObject *aio = node->data; + node = g_list_next(node); + if (aio->type == ADDR_ITEM_GROUP) { + ItemGroup *item = (ItemGroup *)aio; + GtkTreeIter giter; + + addressbook_find_group_node(&iter, &giter, item); + item = addrbook_remove_group(abf, item); + if (item) { + addritem_free_item_group(item); + item = NULL; + } + /* Remove group from parent node */ + gtk_tree_store_remove(GTK_TREE_STORE(model), &giter); + } else if (aio->type == ADDR_ITEM_PERSON) { + ItemPerson *item = (ItemPerson *)aio; + + if (_clipObjectList_) { + _clipObjectList_ = g_list_remove(_clipObjectList_, item); + } + item = addrbook_remove_person(abf, item); + if (item) { + addritem_free_item_person(item); + item = NULL; + } + } else if (aio->type == ADDR_ITEM_EMAIL) { + ItemEMail *item = (ItemEMail *)aio; + ItemPerson *person = (ItemPerson *)ADDRITEM_PARENT(item); + + item = addrbook_person_remove_email(abf, person, item); + if (item) { + addritem_free_item_email(item); + item = NULL; + } + } + } + addressbook_list_select_clear(); + addressbook_reopen(); + addressbook_modified(); + return; + } else if (pobj->type == ADDR_ITEM_GROUP) { + /* Items inside groups */ + GList *node; + ItemGroup *group = ADAPTER_GROUP(pobj)->itemGroup; + + node = _addressListSelection_; + while (node) { + AddrItemObject *aio = node->data; + node = g_list_next(node); + if (aio->type == ADDR_ITEM_EMAIL) { + ItemEMail *item = (ItemEMail *)aio; + item = addrbook_group_remove_email(abf, group, item); + } + } + addressbook_list_select_clear(); + addressbook_reopen(); + return; + } + + gtk_tree_store_remove(GTK_TREE_STORE(lmodel), &list_sel); + addressbook_list_select_remove(obj); +} + +static void addressbook_reg_clicked(GtkButton *button, gpointer data) +{ + addressbook_new_address_cb(NULL, 0, NULL); +} + +gchar *addressbook_format_address(AddressObject *obj) +{ + gchar *buf = NULL; + gchar *name = NULL; + gchar *address = NULL; + + if( obj->type == ADDR_ITEM_EMAIL ) { + ItemPerson *person = NULL; + ItemEMail *email = ( ItemEMail * ) obj; + + person = ( ItemPerson * ) ADDRITEM_PARENT(email); + if( email->address ) { + if( ADDRITEM_NAME(email) ) { + name = ADDRITEM_NAME(email); + if( *name == '\0' ) { + name = ADDRITEM_NAME(person); + } + } + else if( person && ADDRITEM_NAME(person) ) { + name = ADDRITEM_NAME(person); + } + else { + buf = g_strdup( email->address ); + } + address = email->address; + } + } + else if( obj->type == ADDR_ITEM_PERSON ) { + ItemPerson *person = ( ItemPerson * ) obj; + GList *node = person->listEMail; + + name = ADDRITEM_NAME(person); + if( node ) { + ItemEMail *email = ( ItemEMail * ) node->data; + address = email->address; + } + } + if( address ) { + if( !prefs_common.always_add_address_only && + name && name[0] != '\0' ) { + if( name[0] != '"' && strpbrk( name, ",.[]<>" ) != NULL ) + buf = g_strdup_printf( "\"%s\" <%s>", name, address ); + else + buf = g_strdup_printf( "%s <%s>", name, address ); + } + else { + buf = g_strdup( address ); + } + } + + return buf; +} + +static void addressbook_to_clicked(GtkButton *button, gpointer data) +{ + GList *node = _addressListSelection_; + gboolean new_compose = FALSE; + + if (!addrbook.target_compose) { + new_compose = TRUE; + addrbook.target_compose = compose_new(NULL, NULL, NULL, NULL); + if (!addrbook.target_compose) + return; + } + + if (new_compose) + compose_block_modified(addrbook.target_compose); + + while( node ) { + AddressObject *obj = node->data; + Compose *compose = addrbook.target_compose; + + node = g_list_next( node ); + if( obj->type == ADDR_ITEM_PERSON || obj->type == ADDR_ITEM_EMAIL ) { + gchar *addr = addressbook_format_address( obj ); + compose_entry_append( compose, addr, (ComposeEntryType) data ); + g_free( addr ); + addr = NULL; + } + else if( obj->type == ADDR_ITEM_GROUP ) { + ItemGroup *group = ( ItemGroup * ) obj; + GList *nodeMail = group->listEMail; + while( nodeMail ) { + ItemEMail *email = nodeMail->data; + gchar *addr = addressbook_format_address( ( AddressObject * ) email ); + compose_entry_append( compose, addr, (ComposeEntryType) data ); + g_free( addr ); + nodeMail = g_list_next( nodeMail ); + } + } + } + + if (new_compose) + compose_unblock_modified(addrbook.target_compose); +} + +static void addressbook_menuitem_set_sensitive(void) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeView *listview = GTK_TREE_VIEW(addrbook.listview); + GtkTreeSelection *selection; + GtkTreeSelection *lselection; + GtkTreeModel *model; + GtkTreeModel *lmodel; + GtkTreeIter iter; + GtkTreeIter liter; + gboolean canAdd = FALSE; + gboolean canEditTree = TRUE; + gboolean canEditAddress = FALSE; + gboolean canDelete = FALSE; + gboolean canLookup = FALSE; + gboolean canCopy = FALSE; + gboolean canPaste = FALSE; + gboolean hasListSelection = _addressListSelection_ != NULL; + AddressTypeControlItem *atci = NULL; + AddressDataSource *ds = NULL; + AddressInterface *iface = NULL; + AddressObject *pobj = NULL; + AddressObject *obj = NULL; + + menu_set_sensitive(addrbook.menu_factory, "/File/New Book", FALSE); + menu_set_sensitive(addrbook.menu_factory, "/File/New vCard", FALSE); +#ifdef USE_JPILOT + menu_set_sensitive(addrbook.menu_factory, "/File/New JPilot", FALSE); +#endif +#ifdef USE_LDAP + menu_set_sensitive(addrbook.menu_factory, "/File/New LDAP Server", FALSE); +#endif + menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.tree_popup)); + menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.list_popup)); + + selection = gtk_tree_view_get_selection(treeview); + if (!gtk_tree_selection_get_selected(selection, &model, &iter)) + return; + + gtk_tree_model_get(model, &iter, COL_OBJ, &pobj, -1); + if (pobj == NULL) + return; + + lmodel = gtk_tree_view_get_model(listview); + lselection = gtk_tree_view_get_selection(listview); + + if (addrbook.list_selected) { + if (gtkut_tree_row_reference_get_iter(lmodel, addrbook.list_selected, &liter)) + gtk_tree_model_get(lmodel, &liter, COL_L_OBJ, &obj, -1); + } + + if (pobj->type == ADDR_INTERFACE) { + AdapterInterface *adapter = ADAPTER_INTERFACE(pobj); + + iface = adapter->iface; + if (iface && iface->haveLibrary) { + /* Enable appropriate File / New command */ + atci = adapter->atci; + menu_set_sensitive(addrbook.menu_factory, atci->menuCommand, TRUE); + } + canEditTree = FALSE; + } else if (pobj->type == ADDR_DATASOURCE) { + AdapterDSource *ads = ADAPTER_DSOURCE(pobj); + + ds = ads->dataSource; + iface = ds->iface; + if (!iface->readOnly) { + canAdd = canEditAddress = TRUE; + } + if (!iface->haveLibrary) { + canAdd = canEditAddress = FALSE; + } +#ifdef USE_LDAP + if (ads->subType == ADDR_LDAP) { + if (iface->haveLibrary && ds->rawDataSource) + canLookup = TRUE; + } else +#endif + canLookup = TRUE; + if (ads->subType == ADDR_BOOK && pobj->name && + !strcmp(pobj->name, ADDR_DS_AUTOREG)) + canEditTree = FALSE; + } else if (pobj->type == ADDR_ITEM_FOLDER) { + ds = addressbook_find_datasource(&iter); + if (ds) { + iface = ds->iface; + if (!iface->readOnly) { + canAdd = canEditAddress = TRUE; + } + canLookup = TRUE; + } + } else if (pobj->type == ADDR_ITEM_GROUP) { + ds = addressbook_find_datasource(&iter); + if (ds) { + iface = ds->iface; + if (!iface->readOnly) { + canEditAddress = TRUE; + } + canLookup = TRUE; + } + } + + if (obj == NULL) + canEditAddress = FALSE; + canDelete = canEditAddress; + if (gtk_tree_selection_count_selected_rows(lselection) > 1) + canEditAddress = FALSE; + if (_addressListSelection_) { + GList *cur; + AddressObject *item; + + for (cur = _addressListSelection_; cur != NULL; cur = cur->next) { + item = cur->data; + if (item->type == ADDR_ITEM_PERSON) { + canCopy = TRUE; + break; + } + } + } + if (_clipObjectList_ && canAdd) + canPaste = TRUE; + + /* Enable add */ + menu_set_sensitive(addrbook.menu_factory, "/Address/New Address", canAdd); + menu_set_sensitive(addrbook.menu_factory, "/Address/New Folder", canAdd); + menu_set_sensitive(addrbook.menu_factory, "/Address/New Group", canAdd); + + menu_set_sensitive(addrbook.menu_factory, "/Address/Add to recipient", hasListSelection); + menu_set_sensitive(addrbook.menu_factory, "/Address/Add to Cc", hasListSelection); + menu_set_sensitive(addrbook.menu_factory, "/Address/Add to Bcc", hasListSelection); + + /* Enable edit */ + menu_set_sensitive(addrbook.menu_factory, "/Address/Edit", canEditAddress); + menu_set_sensitive(addrbook.menu_factory, "/Address/Delete", canDelete); + + menu_set_sensitive(addrbook.menu_factory, "/File/Edit", canEditTree); + menu_set_sensitive(addrbook.menu_factory, "/File/Delete", canEditTree); + + menu_set_sensitive(addrbook.menu_factory, "/Edit/Copy", canCopy); + menu_set_sensitive(addrbook.menu_factory, "/Edit/Paste", canPaste); + + menu_set_sensitive(addrbook.menu_factory, "/Tools/Export to CSV file", canEditTree); + + /* Popup menu */ + menu_set_sensitive(addrbook.tree_factory, "/New Address", canAdd); + menu_set_sensitive(addrbook.tree_factory, "/New Folder", canAdd); + menu_set_sensitive(addrbook.tree_factory, "/New Group", canAdd); + menu_set_sensitive(addrbook.tree_factory, "/Edit", canEditTree); + menu_set_sensitive(addrbook.tree_factory, "/Delete", canEditTree); + + menu_set_sensitive(addrbook.list_factory, "/New Address", canAdd); + menu_set_sensitive(addrbook.list_factory, "/New Folder", canAdd); + menu_set_sensitive(addrbook.list_factory, "/New Group", canAdd); + menu_set_sensitive(addrbook.list_factory, "/Add to recipient", hasListSelection); + menu_set_sensitive(addrbook.list_factory, "/Add to Cc", hasListSelection); + menu_set_sensitive(addrbook.list_factory, "/Add to Bcc", hasListSelection); + menu_set_sensitive(addrbook.list_factory, "/Edit", canEditAddress); + menu_set_sensitive(addrbook.list_factory, "/Delete", canDelete); + menu_set_sensitive(addrbook.list_factory, "/Copy", canCopy); + menu_set_sensitive(addrbook.list_factory, "/Paste", canPaste); + + /* Buttons */ + gtk_widget_set_sensitive(addrbook.to_btn, hasListSelection); + gtk_widget_set_sensitive(addrbook.cc_btn, hasListSelection); + gtk_widget_set_sensitive(addrbook.bcc_btn, hasListSelection); + gtk_widget_set_sensitive(addrbook.reg_btn, canAdd); + gtk_widget_set_sensitive(addrbook.del_btn, canDelete); + gtk_widget_set_sensitive(addrbook.lup_btn, canLookup); +} + +static void addressbook_tree_selection_changed(GtkTreeSelection *selection, + gpointer data) +{ + GtkTreeModel *model; + GtkTreeModel *lmodel; + GtkTreeIter iter; + GtkTreePath *path; + AddressObject *obj = NULL; + AdapterDSource *ads = NULL; + AddressDataSource *ds = NULL; + ItemFolder *rootFolder = NULL; + + debug_print("addressbook_tree_selection_changed\n"); + + if (!gtk_tree_selection_get_selected(selection, &model, &iter)) { + debug_print("addressbook_tree_selection_changed: no selection\n"); + gtk_tree_row_reference_free(addrbook.tree_selected); + addrbook.tree_selected = NULL; + gtk_tree_row_reference_free(addrbook.tree_opened); + addrbook.tree_opened = NULL; + addressbook_status_show(""); + if (addrbook.entry) + gtk_entry_set_text(GTK_ENTRY(addrbook.entry), ""); + if (addrbook.listview) { + lmodel = gtk_tree_view_get_model(GTK_TREE_VIEW(addrbook.listview)); + gtk_tree_store_clear(GTK_TREE_STORE(lmodel)); + } + return; + } + + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_row_reference_free(addrbook.tree_selected); + addrbook.tree_selected = gtk_tree_row_reference_new(model, path); + gtk_tree_row_reference_free(addrbook.list_selected); + addrbook.list_selected = NULL; + addressbook_status_show(""); + if (addrbook.entry) + gtk_entry_set_text(GTK_ENTRY(addrbook.entry), ""); + if (addrbook.listview) { + lmodel = gtk_tree_view_get_model(GTK_TREE_VIEW(addrbook.listview)); + gtk_tree_store_clear(GTK_TREE_STORE(lmodel)); + } + gtk_tree_model_get(model, &iter, COL_OBJ, &obj, -1); + if (obj == NULL) { + gtk_tree_path_free(path); + return; + } + + gtk_tree_row_reference_free(addrbook.tree_opened); + addrbook.tree_opened = gtk_tree_row_reference_new(model, path); + + if (obj->type == ADDR_DATASOURCE) { + /* Read from file */ + static gboolean tVal = TRUE; + + ads = ADAPTER_DSOURCE(obj); + if (ads == NULL) return; + ds = ads->dataSource; + if (ds == NULL) return; + + if (addrindex_ds_get_modify_flag(ds)) { + addrindex_ds_read_data(ds); + } + + if (!addrindex_ds_get_read_flag(ds)) { + addrindex_ds_read_data(ds); + } + addressbook_ds_show_message(ds); + + if (!addrindex_ds_get_access_flag(ds)) { + /* Remove existing folders and groups */ + addressbook_tree_remove_children(model, &iter); + + /* Load folders into the tree */ + rootFolder = addrindex_ds_get_root_folder(ds); + if (ds->type == ADDR_IF_JPILOT) { + addressbook_node_add_folder(&iter, ds, rootFolder, ADDR_CATEGORY, NULL); + } else { + addressbook_node_add_folder(&iter, ds, rootFolder, ADDR_ITEM_FOLDER, NULL); + } + addrindex_ds_set_access_flag(ds, &tVal); + gtk_tree_view_expand_row(GTK_TREE_VIEW(addrbook.treeview), path, TRUE); + } + } + + gtk_tree_path_free(path); + + /* Update address list */ + addressbook_set_list(obj); + addressbook_menuitem_set_sensitive(); + addressbook_list_select_clear(); +} + +static void addressbook_list_selection_changed(GtkTreeSelection *selection, + gpointer data) +{ + GtkTreeModel *lmodel; + GtkTreePath *path; + GList *selected; + + debug_print("addressbook_list_selection_changed\n"); + + selected = gtk_tree_selection_get_selected_rows(selection, &lmodel); + addressbook_list_select_set(selected); + gtk_tree_row_reference_free(addrbook.list_selected); + if (selected) { + path = (GtkTreePath *)selected->data; + addrbook.list_selected = gtk_tree_row_reference_new(lmodel, path); + g_list_foreach(selected, (GFunc)gtk_tree_path_free, NULL); + g_list_free(selected); + } else + addrbook.list_selected = NULL; + + addressbook_menuitem_set_sensitive(); +} + +static void addressbook_list_select_clear(void) +{ + if (_addressListSelection_) { + g_list_free(_addressListSelection_); + } + _addressListSelection_ = NULL; +} + +static void addressbook_list_select_remove(AddressObject *obj) +{ + if (!obj) + return; + if (_addressListSelection_) { + _addressListSelection_ = g_list_remove(_addressListSelection_, obj); + } +} + +static void addressbook_list_select_set(GList *row_list) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GList *cur; + AddressObject *obj; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(addrbook.listview)); + + addressbook_list_select_clear(); + for (cur = row_list; cur != NULL; cur = cur->next) { + obj = NULL; + gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)cur->data); + gtk_tree_model_get(model, &iter, COL_L_OBJ, &obj, -1); + if (obj && (obj->type == ADDR_ITEM_PERSON || + obj->type == ADDR_ITEM_EMAIL || + obj->type == ADDR_ITEM_GROUP)) { + _addressListSelection_ = g_list_prepend(_addressListSelection_, obj); + } + } + _addressListSelection_ = g_list_reverse(_addressListSelection_); +} + +static void addressbook_entry_activated(GtkWidget *widget, gpointer data) +{ + addressbook_lup_clicked(NULL, NULL); +} + +static gboolean addressbook_list_button_pressed(GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + GtkTreePath *path = NULL; + GtkTreeSelection *selection; + gboolean is_selected = FALSE; + gboolean mod_pressed = FALSE; + + if (!event) + return FALSE; + + gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), event->x, event->y, + &path, NULL, NULL, NULL); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + if (path) + is_selected = gtk_tree_selection_path_is_selected + (selection, path); + mod_pressed = ((event->state & + (GDK_SHIFT_MASK|GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0); + + if (event->button == 1) { + if (is_selected && !mod_pressed) { + can_toggle_list_selection = FALSE; + } + + if (is_selected && event->type == GDK_2BUTTON_PRESS) { + debug_print("addressbook_list_button_pressed: GDK_2BUTTON_PRESS\n"); + /* Handle double click */ + if (prefs_common.add_address_by_click && + addrbook.target_compose) + addressbook_to_clicked + (NULL, GINT_TO_POINTER(COMPOSE_ENTRY_TO)); + else + addressbook_edit_address_cb(NULL, 0, NULL); + return TRUE; + } + } else if (event->button == 3) { + gtk_menu_popup(GTK_MENU(addrbook.list_popup), NULL, NULL, + NULL, NULL, event->button, event->time); + gtk_tree_path_free(path); + if (is_selected) + return TRUE; + } + + return FALSE; +} + +static gboolean addressbook_list_button_released(GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + if (!can_toggle_list_selection && !list_on_drag) { + GtkTreePath *path; + + if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), event->x, event->y, &path, NULL, NULL, NULL)) { + can_toggle_list_selection = TRUE; + gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), path, NULL, FALSE); + gtk_tree_path_free(path); + } + } + can_toggle_list_selection = TRUE; + list_on_drag = FALSE; + return FALSE; +} + +static gboolean addressbook_tree_button_pressed(GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(widget); + GtkTreeSelection *selection; + GtkTreePath *path; + + if (!event) + return FALSE; + + if (!gtk_tree_view_get_path_at_pos(treeview, event->x, event->y, + &path, NULL, NULL, NULL)) + return TRUE; + selection = gtk_tree_view_get_selection(treeview); + + addressbook_menuitem_set_sensitive(); + + if (event->button == 3) { + gtk_tree_selection_select_path(selection, path); + gtk_menu_popup(GTK_MENU(addrbook.tree_popup), + NULL, NULL, NULL, NULL, + event->button, event->time); + } else if (event->type == GDK_2BUTTON_PRESS) { + gtk_tree_view_expand_row(treeview, path, FALSE); + } + + gtk_tree_path_free(path); + return FALSE; +} + +static gboolean addressbook_tree_button_released(GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + return FALSE; +} + +static gboolean addressbook_obj_is_droppable(GtkTreeModel *model, + AddressObject *obj) +{ + gboolean acceptable = FALSE; + + if (!model || !obj) + return FALSE; + if (!_addressListSelection_) + return FALSE; + + if (obj->type == ADDR_DATASOURCE) { + AdapterDSource *ads; + AddressDataSource *ds; + AddressInterface *iface; + + ads = ADAPTER_DSOURCE(obj); + ds = ads ? ads->dataSource : NULL; + iface = ds ? ds->iface : NULL; + if (ads && ads->subType == ADDR_BOOK && + iface && !iface->readOnly) + acceptable = TRUE; + } else if (obj->type == ADDR_ITEM_FOLDER) { + acceptable = TRUE; + } + + if (acceptable && addrbook.tree_opened) { + AddressObject *open_obj; + GtkTreeIter opened; + + gtkut_tree_row_reference_get_iter(model, addrbook.tree_opened, + &opened); + gtk_tree_model_get(model, &opened, COL_OBJ, &open_obj, -1); + if (open_obj == obj) + acceptable = FALSE; + } + + if (acceptable) { + GList *node; + for (node = _addressListSelection_; node != NULL; node = node->next) { + AddressObject *obj = node->data; + if (obj && obj->type == ADDR_ITEM_PERSON) + break; + } + if (!node) + acceptable = FALSE; + } + + return acceptable; +} + +static gboolean addressbook_drag_motion(GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, + guint time, gpointer data) +{ + GtkTreeModel *model; + GtkTreePath *path = NULL; + GtkTreeIter iter; + AddressObject *obj, *src_obj; + AddressDataSource *src_ds; + AddressBookFile *src_abf; + gboolean acceptable; + + if (!gtk_tree_view_get_dest_row_at_pos + (GTK_TREE_VIEW(widget), x, y, &path, NULL)) { + gtk_tree_view_set_drag_dest_row + (GTK_TREE_VIEW(widget), NULL, + GTK_TREE_VIEW_DROP_INTO_OR_AFTER); + gdk_drag_status(context, 0, time); + return FALSE; + } + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget)); + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, COL_OBJ, &obj, -1); + g_return_val_if_fail(obj != NULL, FALSE); + + /* source */ + gtkut_tree_row_reference_get_iter(model, addrbook.tree_opened, &iter); + gtk_tree_model_get(model, &iter, COL_OBJ, &src_obj, -1); + g_return_val_if_fail(src_obj != NULL, FALSE); + src_ds = addressbook_find_datasource(&iter); + g_return_val_if_fail(src_ds != NULL, FALSE); + src_abf = src_ds->rawDataSource; + g_return_val_if_fail(src_abf != NULL, FALSE); + +#ifdef G_OS_WIN32 + { + GdkModifierType state = 0; + + gdk_window_get_pointer(widget->window, NULL, NULL, &state); + if ((state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) == 0) + context->actions = GDK_ACTION_MOVE | GDK_ACTION_COPY; + } +#endif + + if (!src_ds->iface || + (src_ds->iface->readOnly || !src_ds->iface->haveLibrary)) + context->actions &= ~GDK_ACTION_MOVE; + + acceptable = addressbook_obj_is_droppable(model, obj); + if ((context->actions & (GDK_ACTION_MOVE | GDK_ACTION_COPY)) == 0) + acceptable = FALSE; + + if (!acceptable) { + gtk_tree_view_set_drag_dest_row + (GTK_TREE_VIEW(widget), NULL, + GTK_TREE_VIEW_DROP_INTO_OR_AFTER); + gdk_drag_status(context, 0, time); + } else { + GdkDragAction action = 0; + + if ((context->actions & GDK_ACTION_MOVE) != 0) + action = GDK_ACTION_MOVE; + else if ((context->actions & GDK_ACTION_COPY) != 0) + action = GDK_ACTION_COPY; + gtk_tree_view_set_drag_dest_row + (GTK_TREE_VIEW(widget), path, + GTK_TREE_VIEW_DROP_INTO_OR_AFTER); + gdk_drag_status(context, action, time); + } + + gtk_tree_path_free(path); + + return TRUE; +} + +static void addressbook_drag_leave(GtkWidget *widget, GdkDragContext *context, + guint time, gpointer data) +{ + gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(widget), NULL, + GTK_TREE_VIEW_DROP_INTO_OR_AFTER); +} + +static void addressbook_drag_received(GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, + GtkSelectionData *data, + guint info, guint time, + gpointer user_data) +{ + GtkTreeModel *model; + GtkTreePath *path = NULL; + GtkTreeIter iter; + AddressObject *obj, *pobj, *src_obj; + AddressDataSource *ds, *src_ds; + AddressBookFile *abf, *src_abf; + gboolean acceptable; + gboolean is_move; + GList *node; + ItemFolder *folder = NULL; + ItemPerson *person, *new_person; + + if (!gtk_tree_view_get_dest_row_at_pos + (GTK_TREE_VIEW(widget), x, y, &path, NULL)) + return; + + debug_print("addressbook_drag_received_cb: received data\n"); + + /* dest */ + model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget)); + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, COL_OBJ, &pobj, -1); + g_return_if_fail(pobj != NULL); + ds = addressbook_find_datasource(&iter); + g_return_if_fail(ds != NULL); + + /* source */ + gtkut_tree_row_reference_get_iter(model, addrbook.tree_opened, &iter); + gtk_tree_model_get(model, &iter, COL_OBJ, &src_obj, -1); + g_return_if_fail(src_obj != NULL); + src_ds = addressbook_find_datasource(&iter); + g_return_if_fail(src_ds != NULL); + src_abf = src_ds->rawDataSource; + g_return_if_fail(src_abf != NULL); + + acceptable = addressbook_obj_is_droppable(model, pobj); + abf = ds->rawDataSource; + if (!abf) + acceptable = FALSE; + + if ((context->actions & (GDK_ACTION_MOVE | GDK_ACTION_COPY)) == 0) + acceptable = FALSE; + is_move = (context->actions & GDK_ACTION_MOVE) != 0; + + if (!src_ds->iface || + (src_ds->iface->readOnly || !src_ds->iface->haveLibrary)) + is_move = FALSE; + + if (!_addressListSelection_ || !acceptable) { + context->action = 0; + gtk_drag_finish(context, FALSE, FALSE, time); + gtk_tree_path_free(path); + return; + } + + if (pobj->type == ADDR_ITEM_FOLDER) + folder = ADAPTER_FOLDER(pobj)->itemFolder; + + node = _addressListSelection_; + while (node) { + GList *cur; + + obj = node->data; + node = g_list_next(node); + if (obj->type != ADDR_ITEM_PERSON) + continue; + + person = (ItemPerson *)obj; + new_person = addrbook_add_address_list(abf, folder, NULL); + ADDRITEM_NAME(new_person) = g_strdup(ADDRITEM_NAME(person)); + new_person->firstName = g_strdup(person->firstName); + new_person->lastName = g_strdup(person->lastName); + new_person->nickName = g_strdup(person->nickName); + for (cur = person->listEMail; cur != NULL; cur = cur->next) { + ItemEMail *email = cur->data; + addritem_person_add_email(new_person, addritem_copy_item_email(email)); + } + for (cur = person->listAttrib; cur != NULL; cur = cur->next) { + UserAttribute *attr = cur->data; + addritem_person_add_attribute(new_person, addritem_copy_attribute(attr)); + } + + if (is_move) { + if (_clipObjectList_) + _clipObjectList_ = g_list_remove(_clipObjectList_, person); + person = addrbook_remove_person(src_abf, person); + if (person) + addritem_free_item_person(person); + } + } + + if (is_move) { + addressbook_list_select_clear(); + addressbook_reopen(); + } + + context->action = 0; + gtk_drag_finish(context, TRUE, FALSE, time); + + gtk_tree_path_free(path); + + addressbook_modified(); +} + +static void addressbook_folder_resized(GtkWidget *widget, + GtkAllocation *allocation, + gpointer data) +{ + gint width = allocation->width; + + if (width < 8) + return; + + prefs_common.addressbook_folder_width = width; +} + +static void addressbook_col_resized(GtkWidget *widget, + GtkAllocation *allocation, gpointer data) +{ + AddressBookListColumnPos type = (gint)data; + gint width = allocation->width; + + if (width < 8) + return; + + switch (type) { + case COL_NAME: + prefs_common.addressbook_col_name = width; + break; + case COL_ADDRESS: + prefs_common.addressbook_col_addr = width; + break; + case COL_NICKNAME: + prefs_common.addressbook_col_nickname = width; + break; + case COL_REMARKS: + prefs_common.addressbook_col_rem = width; + break; + default: + break; + } +} + +static void addressbook_popup_close(GtkMenuShell *menu_shell, gpointer data) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeSelection *selection; + GtkTreePath *path; + + if (!addrbook.tree_opened) + return; + + selection = gtk_tree_view_get_selection(treeview); + path = gtk_tree_row_reference_get_path(addrbook.tree_opened); + if (path) { + gtk_tree_selection_select_path(selection, path); + gtk_tree_path_free(path); + } +} + +static void addressbook_new_folder_cb(gpointer data, guint action, + GtkWidget *widget) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + AddressObject *obj = NULL; + AddressDataSource *ds = NULL; + AddressBookFile *abf = NULL; + ItemFolder *parentFolder = NULL; + ItemFolder *folder = NULL; + + selection = gtk_tree_view_get_selection(treeview); + if (!gtk_tree_selection_get_selected(selection, &model, &iter)) + return; + + gtk_tree_model_get(model, &iter, COL_OBJ, &obj, -1); + if (obj == NULL) + return; + ds = addressbook_find_datasource(&iter); + if (ds == NULL) + return; + + if (obj->type == ADDR_DATASOURCE) { + if (ADAPTER_DSOURCE(obj)->subType != ADDR_BOOK) return; + } else if (obj->type == ADDR_ITEM_FOLDER) { + parentFolder = ADAPTER_FOLDER(obj)->itemFolder; + } else { + return; + } + + abf = ds->rawDataSource; + if (abf == NULL) return; + folder = addressbook_edit_folder(abf, parentFolder, NULL); + if (folder) { + GtkTreeIter new_iter; + GtkTreePath *path; + addressbook_node_add_folder(&iter, ds, folder, ADDR_ITEM_FOLDER, &new_iter); + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_view_expand_row(treeview, path, TRUE); + gtk_tree_path_free(path); + if (gtkut_tree_row_reference_equal(addrbook.tree_selected, addrbook.tree_opened)) + addressbook_set_list(obj); + } + +} + +static void addressbook_new_group_cb(gpointer data, guint action, + GtkWidget *widget) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + AddressObject *obj = NULL; + AddressDataSource *ds = NULL; + AddressBookFile *abf = NULL; + ItemFolder *parentFolder = NULL; + ItemGroup *group = NULL; + + selection = gtk_tree_view_get_selection(treeview); + if (!gtk_tree_selection_get_selected(selection, &model, &iter)) + return; + + gtk_tree_model_get(model, &iter, COL_OBJ, &obj, -1); + if (obj == NULL) + return; + ds = addressbook_find_datasource(&iter); + if (ds == NULL) + return; + + if (obj->type == ADDR_DATASOURCE) { + if (ADAPTER_DSOURCE(obj)->subType != ADDR_BOOK) return; + } else if (obj->type == ADDR_ITEM_FOLDER) { + parentFolder = ADAPTER_FOLDER(obj)->itemFolder; + } else { + return; + } + + abf = ds->rawDataSource; + if (abf == NULL) return; + group = addressbook_edit_group(abf, parentFolder, NULL); + if (group) { + GtkTreeIter new_iter; + GtkTreePath *path; + addressbook_node_add_group(&iter, ds, group, &new_iter); + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_view_expand_row(treeview, path, TRUE); + gtk_tree_path_free(path); + if (gtkut_tree_row_reference_equal(addrbook.tree_selected, addrbook.tree_opened)) + addressbook_set_list(obj); + } + +} + +static void addressbook_change_node_name(GtkTreeIter *iter, const gchar *name) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeStore *store; + + store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview)); + gtk_tree_store_set(store, iter, COL_FOLDER_NAME, name, -1); +} + +/* +* Edit data source. +* Enter: obj Address object to edit. +* node Node in tree. +* Return: New name of data source. +*/ +static gchar *addressbook_edit_datasource(AddressObject *obj, GtkTreeIter *iter) +{ + gchar *newName = NULL; + AddressDataSource *ds = NULL; + AddressInterface *iface = NULL; + AdapterDSource *ads = NULL; + + ds = addressbook_find_datasource(iter); + if (ds == NULL) + return NULL; + iface = ds->iface; + if (!iface->haveLibrary) + return NULL; + + /* Read data from data source */ + if (!addrindex_ds_get_read_flag(ds)) { + addrindex_ds_read_data(ds); + } + + /* Handle edit */ + ads = ADAPTER_DSOURCE(obj); + if (ads->subType == ADDR_BOOK) { + if (addressbook_edit_book(_addressIndex_, ads) == NULL) + return NULL; + } else if (ads->subType == ADDR_VCARD) { + if (addressbook_edit_vcard(_addressIndex_, ads) == NULL) + return NULL; +#ifdef USE_JPILOT + } else if (ads->subType == ADDR_JPILOT) { + if (addressbook_edit_jpilot(_addressIndex_, ads) == NULL) + return NULL; +#endif +#ifdef USE_LDAP + } else if (ads->subType == ADDR_LDAP) { + if (addressbook_edit_ldap(_addressIndex_, ads) == NULL) + return NULL; +#endif + } else { + return NULL; + } + newName = obj->name; + return newName; +} + +/* +* Edit an object that is in the address tree area. +*/ +static void addressbook_treenode_edit_cb(gpointer data, guint action, + GtkWidget *widget) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter, parent; + AddressObject *obj = NULL; + AddressDataSource *ds = NULL; + AddressBookFile *abf = NULL; + gchar *name = NULL; + + selection = gtk_tree_view_get_selection(treeview); + if (!gtk_tree_selection_get_selected(selection, &model, &iter)) + return; + + if (gtk_tree_store_iter_depth(GTK_TREE_STORE(model), &iter) == 0) + return; + gtk_tree_model_get(model, &iter, COL_OBJ, &obj, -1); + if (obj == NULL) + return; + if (!gtk_tree_model_iter_parent(model, &parent, &iter)) + return; + + ds = addressbook_find_datasource(&iter); + if (ds == NULL) + return; + + if (obj->type == ADDR_DATASOURCE) { + name = addressbook_edit_datasource(obj, &iter); + if (name == NULL) + return; + } else { + abf = ds->rawDataSource; + if (abf == NULL) + return; + if (obj->type == ADDR_ITEM_FOLDER) { + AdapterFolder *adapter = ADAPTER_FOLDER(obj); + ItemFolder *item = adapter->itemFolder; + ItemFolder *parentFolder = NULL; + parentFolder = (ItemFolder *)ADDRITEM_PARENT(item); + if (addressbook_edit_folder(abf, parentFolder, item) == NULL) + return; + name = ADDRITEM_NAME(item); + } else if (obj->type == ADDR_ITEM_GROUP) { + AdapterGroup *adapter = ADAPTER_GROUP(obj); + ItemGroup *item = adapter->itemGroup; + ItemFolder *parentFolder = NULL; + parentFolder = (ItemFolder *)ADDRITEM_PARENT(item); + if (addressbook_edit_group(abf, parentFolder, item) == NULL) + return; + name = ADDRITEM_NAME(item); + } + } + + if (name) { + GtkTreePath *path; + + /* Update node in tree view */ + addressbook_change_node_name(&iter, name); + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_view_expand_row(treeview, path, TRUE); + gtk_tree_view_set_cursor(treeview, path, NULL, FALSE); + gtk_tree_path_free(path); + } +} + +/* +* Delete an item from the tree widget. +*/ +static void addressbook_treenode_delete_cb(gpointer data, guint action, + GtkWidget *widget) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter, parent; + AddressObject *obj; + gchar *message; + AlertValue aval; + AddressBookFile *abf = NULL; + AdapterDSource *ads = NULL; + AddressInterface *iface = NULL; + AddressDataSource *ds = NULL; + gboolean remFlag = FALSE; + + selection = gtk_tree_view_get_selection(treeview); + if (!gtk_tree_selection_get_selected(selection, &model, &iter)) + return; + + if (gtk_tree_store_iter_depth(GTK_TREE_STORE(model), &iter) == 0) + return; + gtk_tree_model_get(model, &iter, COL_OBJ, &obj, -1); + g_return_if_fail(obj != NULL); + + if (obj->type == ADDR_DATASOURCE) { + ads = ADAPTER_DSOURCE(obj); + if (ads == NULL) + return; + ds = ads->dataSource; + if (ds == NULL) + return; + } else { + /* Must be folder or something else */ + ds = addressbook_find_datasource(&iter); + if (ds == NULL) + return; + + /* Only allow deletion from non-readOnly data sources */ + iface = ds->iface; + if (iface->readOnly) + return; + } + + /* Confirm deletion */ + if (obj->type == ADDR_ITEM_FOLDER) { + message = g_strdup_printf + (_("Do you want to delete the folder AND all addresses in `%s' ?\n" + "If deleting the folder only, addresses will be moved into parent folder."), + obj->name); + aval = alertpanel(_("Delete folder"), message, _("_Folder only"), _("Folder and _addresses"), GTK_STOCK_CANCEL); + g_free(message); + if (aval != G_ALERTDEFAULT && aval != G_ALERTALTERNATE) + return; + } else { + message = g_strdup_printf(_("Really delete `%s' ?"), obj->name); + aval = alertpanel(_("Delete"), message, GTK_STOCK_YES, GTK_STOCK_NO, NULL); + g_free(message); + if (aval != G_ALERTDEFAULT) + return; + } + + /* Proceed with deletion */ + if (obj->type == ADDR_DATASOURCE) { + /* Remove data source. */ + if (addrindex_index_remove_datasource(_addressIndex_, ds)) { + addressbook_free_child_adapters(&iter); + abf = addressbook_get_book_file(); + if (abf) { + gchar *bookFile; + bookFile = g_strconcat(abf->path, G_DIR_SEPARATOR_S, abf->fileName, NULL); + debug_print("removing %s\n", bookFile); + g_unlink(bookFile); + g_free(bookFile); + } + remFlag = TRUE; + } + } else { + abf = addressbook_get_book_file(); + if (abf == NULL) + return; + } + + if (obj->type == ADDR_ITEM_FOLDER) { + AdapterFolder *adapter = ADAPTER_FOLDER(obj); + ItemFolder *item = adapter->itemFolder; + + if (aval == G_ALERTDEFAULT) { + /* Remove folder only */ + item = addrbook_remove_folder(abf, item); + if (item) { + addritem_free_item_folder(item); + addressbook_move_nodes_up(&iter); + remFlag = TRUE; + } + } else if (aval == G_ALERTALTERNATE) { + /* Remove folder and addresses */ + item = addrbook_remove_folder_delete(abf, item); + if (item) { + addritem_free_item_folder(item); + addressbook_free_child_adapters(&iter); + remFlag = TRUE; + } + } + } else if (obj->type == ADDR_ITEM_GROUP) { + AdapterGroup *adapter = ADAPTER_GROUP(obj); + ItemGroup *item = adapter->itemGroup; + + item = addrbook_remove_group(abf, item); + if (item) { + addritem_free_item_group(item); + remFlag = TRUE; + } + } + + if (remFlag) { + /* Free up adapter and remove node. */ + addressbook_free_adapter(&iter); + gtk_tree_model_iter_parent(model, &parent, &iter); + gtk_tree_store_remove(GTK_TREE_STORE(model), &iter); + gtk_tree_selection_select_iter(selection, &parent); + } +} + +static void addressbook_new_address_cb(gpointer data, guint action, GtkWidget *widget) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + AddressObject *pobj = NULL; + AddressDataSource *ds = NULL; + AddressBookFile *abf = NULL; + + selection = gtk_tree_view_get_selection(treeview); + if (!gtk_tree_selection_get_selected(selection, &model, &iter)) + return; + + gtk_tree_model_get(model, &iter, COL_OBJ, &pobj, -1); + if (pobj == NULL) + return; + ds = addressbook_find_datasource(&iter); + if (ds == NULL) + return; + + abf = ds->rawDataSource; + if (abf == NULL) + return; + + if (pobj->type == ADDR_DATASOURCE) { + if (ADAPTER_DSOURCE(pobj)->subType == ADDR_BOOK) { + /* New address */ + ItemPerson *person = addressbook_edit_person(abf, NULL, NULL, FALSE); + if (person) { + if (gtkut_tree_row_reference_equal(addrbook.tree_selected, addrbook.tree_opened)) { + addressbook_reopen(); + } + addressbook_modified(); + } + } + } + else if( pobj->type == ADDR_ITEM_FOLDER ) { + /* New address */ + ItemFolder *folder = ADAPTER_FOLDER(pobj)->itemFolder; + ItemPerson *person = addressbook_edit_person(abf, folder, NULL, FALSE); + if (person) { + if (gtkut_tree_row_reference_equal(addrbook.tree_selected, addrbook.tree_opened)) { + addressbook_reopen(); + } + addressbook_modified(); + } + } + else if( pobj->type == ADDR_ITEM_GROUP ) { + /* New address in group */ + ItemGroup *group = ADAPTER_GROUP(pobj)->itemGroup; + + if (addressbook_edit_group(abf, NULL, group) == NULL) + return; + if (gtkut_tree_row_reference_equal + (addrbook.tree_selected, addrbook.tree_opened)) { + /* Change node name in tree. */ + addressbook_change_node_name(&iter, ADDRITEM_NAME(group)); + addressbook_reopen(); + } + } +} + +static void addressbook_compose_to_cb(gpointer data, guint action, GtkWidget *widget) +{ + addressbook_to_clicked(NULL, GINT_TO_POINTER(action)); +} + +/* +* Search for specified group in address index tree. +*/ +static gboolean addressbook_find_group_node(GtkTreeIter *parent, GtkTreeIter *iter, ItemGroup *group) +{ + GtkTreeModel *model; + GtkTreeIter iter_; + gboolean valid = TRUE; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(addrbook.treeview)); + valid = gtk_tree_model_iter_children(model, &iter_, parent); + while (valid) { + AddressObject *obj = NULL; + + gtk_tree_model_get(model, &iter_, COL_OBJ, &obj, -1); + if (obj && obj->type == ADDR_ITEM_GROUP) { + ItemGroup *g = ADAPTER_GROUP(obj)->itemGroup; + if (g == group) { + *iter = iter_; + return TRUE; + } + } + valid = gtk_tree_model_iter_next(model, &iter_); + } + + return FALSE; +} + +static AddressBookFile *addressbook_get_book_file(void) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeSelection *selection; + GtkTreeIter iter; + AddressBookFile *abf = NULL; + AddressDataSource *ds = NULL; + + selection = gtk_tree_view_get_selection(treeview); + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) + return NULL; + ds = addressbook_find_datasource(&iter); + if (ds == NULL) + return NULL; + if (ds->type == ADDR_IF_BOOK) + abf = ds->rawDataSource; + return abf; +} + +static void addressbook_tree_remove_children(GtkTreeModel *model, + GtkTreeIter *parent) +{ + GtkTreeIter iter; + + /* Remove existing folders and groups */ + while (gtk_tree_model_iter_children(model, &iter, parent)) { + gtk_tree_store_remove(GTK_TREE_STORE(model), &iter); + } +} + +static void addressbook_move_nodes_recursive(GtkTreeIter *iter, + GtkTreeIter *parent) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeModel *model; + GtkTreeIter child, new_iter; + gboolean valid; + + model = gtk_tree_view_get_model(treeview); + + valid = gtk_tree_model_iter_children(model, &child, iter); + while (valid) { + gchar *name = NULL; + AddressObject *obj = NULL; + GdkPixbuf *pixbuf = NULL; + GdkPixbuf *pixbuf_open = NULL; + + gtk_tree_model_get(model, &child, COL_FOLDER_NAME, &name, + COL_OBJ, &obj, COL_PIXBUF, &pixbuf, + COL_PIXBUF_OPEN, &pixbuf_open, -1); + gtk_tree_store_append(GTK_TREE_STORE(model), &new_iter, parent); + gtk_tree_store_set(GTK_TREE_STORE(model), &new_iter, + COL_FOLDER_NAME, name, + COL_OBJ, obj, + COL_PIXBUF, pixbuf, + COL_PIXBUF_OPEN, pixbuf_open, -1); + g_free(name); + addressbook_move_nodes_recursive(&child, &new_iter); + valid = gtk_tree_model_iter_next(model, &child); + } +} + +static void addressbook_move_nodes_up(GtkTreeIter *iter) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeModel *model; + GtkTreeIter parent; + + model = gtk_tree_view_get_model(treeview); + if (!gtk_tree_model_iter_parent(model, &parent, iter)) + return; + + addressbook_move_nodes_recursive(iter, &parent); +} + +static void addressbook_edit_address_cb(gpointer data, guint action, GtkWidget *widget) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter liter, iter, node, parent; + gboolean node_found; + gboolean parent_found = FALSE; + GtkTreeView *listview = GTK_TREE_VIEW(addrbook.listview); + GtkTreeModel *lmodel; + AddressObject *obj = NULL, *pobj = NULL; + AddressDataSource *ds = NULL; + gchar *name = NULL; + AddressBookFile *abf = NULL; + + if (addrbook.list_selected == NULL) + return; + + lmodel = gtk_tree_view_get_model(listview); + if (!gtkut_tree_row_reference_get_iter(lmodel, addrbook.list_selected, + &liter)) + return; + gtk_tree_model_get(lmodel, &liter, COL_L_OBJ, &obj, -1); + g_return_if_fail(obj != NULL); + + selection = gtk_tree_view_get_selection(treeview); + if (!gtk_tree_selection_get_selected(selection, &model, &iter)) + return; + gtk_tree_model_get(model, &iter, COL_OBJ, &pobj, -1); + node_found = gtkut_tree_model_find_by_column_data + (model, &node, &iter, COL_OBJ, obj); + + ds = addressbook_find_datasource(&iter); + if (ds == NULL) + return; + + abf = addressbook_get_book_file(); + if (abf == NULL) + return; + + if (obj->type == ADDR_ITEM_EMAIL) { + ItemEMail *email = (ItemEMail *)obj; + ItemPerson *person; + + if (email == NULL) + return; + if (pobj && pobj->type == ADDR_ITEM_GROUP) { + /* Edit parent group */ + AdapterGroup *adapter = ADAPTER_GROUP(pobj); + ItemGroup *itemGrp = adapter->itemGroup; + + if (addressbook_edit_group(abf, NULL, itemGrp) == NULL) + return; + name = ADDRITEM_NAME(itemGrp); + node = iter; + node_found = TRUE; + parent_found = gtk_tree_model_iter_parent(model, &parent, &node); + } else { + /* Edit person - email page */ + person = (ItemPerson *)ADDRITEM_PARENT(email); + if (addressbook_edit_person(abf, NULL, person, TRUE) == NULL) + return; + addressbook_reopen(); + addressbook_modified(); + return; + } + } else if (obj->type == ADDR_ITEM_PERSON) { + /* Edit person - basic page */ + ItemPerson *person = (ItemPerson *)obj; + + if (addressbook_edit_person(abf, NULL, person, FALSE) == NULL) + return; + addressbook_reopen(); + addressbook_modified(); + return; + } else if (obj->type == ADDR_ITEM_GROUP) { + ItemGroup *itemGrp = (ItemGroup *)obj; + + if (addressbook_edit_group(abf, NULL, itemGrp) == NULL) + return; + parent = iter; + parent_found = TRUE; + node_found = addressbook_find_group_node(&parent, &node, itemGrp); + name = ADDRITEM_NAME(itemGrp); + } else { + return; + } + + /* Update tree node with node name */ + if (node_found) { + addressbook_change_node_name(&node, name); + addressbook_reopen(); + } +} + +static void addressbook_delete_address_cb(gpointer data, guint action, + GtkWidget *widget) +{ + addressbook_del_clicked(NULL, NULL); +} + +static void addressbook_copy_address_cb(gpointer data, guint action, + GtkWidget *widget) +{ + AddressObject *obj; + GList *node; + + if (_addressListSelection_ == NULL) + return; + + g_list_free(_clipObjectList_); + _clipObjectList_ = NULL; + + node = _addressListSelection_; + while (node) { + obj = node->data; + if (obj->type == ADDR_ITEM_PERSON) { + _clipObjectList_ = g_list_append(_clipObjectList_, obj); + } + node = g_list_next(node); + } + + addressbook_menuitem_set_sensitive(); +} + +static void addressbook_paste_address_cb(gpointer data, guint action, + GtkWidget *widget) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + AddressObject *obj = NULL, *pobj = NULL; + AddressDataSource *ds = NULL; + AddressBookFile *abf = NULL; + ItemFolder *folder = NULL; + ItemPerson *clipPerson, *person; + GList *cur; + GList *node; + + if (!_clipObjectList_) + return; + + selection = gtk_tree_view_get_selection(treeview); + if (!gtk_tree_selection_get_selected(selection, &model, &iter)) + return; + gtk_tree_model_get(model, &iter, COL_OBJ, &pobj, -1); + if (pobj->type == ADDR_ITEM_FOLDER) + folder = ADAPTER_FOLDER(pobj)->itemFolder; + else if (pobj->type == ADDR_DATASOURCE) + folder = NULL; + else + return; + + ds = addressbook_find_datasource(&iter); + if (ds == NULL) + return; + + abf = ds->rawDataSource; + if (abf == NULL) + return; + + node = _clipObjectList_; + while (node) { + obj = node->data; + node = g_list_next(node); + if (obj->type != ADDR_ITEM_PERSON) + continue; + clipPerson = (ItemPerson *)obj; + + person = addrbook_add_address_list(abf, folder, NULL); + ADDRITEM_NAME(person) = g_strdup(ADDRITEM_NAME(clipPerson)); + person->firstName = g_strdup(clipPerson->firstName); + person->lastName = g_strdup(clipPerson->lastName); + person->nickName = g_strdup(clipPerson->nickName); + for (cur = clipPerson->listEMail; cur != NULL; cur = cur->next) { + ItemEMail *email = cur->data; + addritem_person_add_email(person, addritem_copy_item_email(email)); + } + for (cur = clipPerson->listAttrib; cur != NULL; cur = cur->next) { + UserAttribute *attr = cur->data; + addritem_person_add_attribute(person, addritem_copy_attribute(attr)); + } + } + + if (gtkut_tree_row_reference_equal(addrbook.tree_selected, addrbook.tree_opened)) + addressbook_reopen(); + addressbook_modified(); +} + +static void close_cb(gpointer data, guint action, GtkWidget *widget) +{ + addressbook_close(); +} + +static void addressbook_file_save_cb(gpointer data, guint action, GtkWidget *widget) +{ + addressbook_export_to_file(); +} + +static void addressbook_person_expand_node(GtkTreeView *treeview, + GtkTreeIter *iter, GtkTreePath *path, + gpointer *data) +{ + GtkTreeModel *model; + ItemPerson *person = NULL; + + model = gtk_tree_view_get_model(treeview); + gtk_tree_model_get(model, iter, COL_L_OBJ, &person, -1); + if (person) + addritem_person_set_opened(person, TRUE); +} + +static void addressbook_person_collapse_node(GtkTreeView *treeview, + GtkTreeIter *iter, + GtkTreePath *path, gpointer *data) +{ + GtkTreeModel *model; + ItemPerson *person = NULL; + + model = gtk_tree_view_get_model(treeview); + gtk_tree_model_get(model, iter, COL_L_OBJ, &person, -1); + if (person) + addritem_person_set_opened(person, FALSE); +} + +static void addressbook_drag_begin(GtkWidget *widget, + GdkDragContext *drag_context, gpointer data) +{ + list_on_drag = TRUE; + /* gtk_drag_set_icon_default(drag_context); */ +} + +static void addressbook_drag_end(GtkWidget *widget, + GdkDragContext *drag_context, gpointer data) +{ +} + +static void addressbook_drag_data_get(GtkWidget *widget, + GdkDragContext *drag_context, + GtkSelectionData *selection_data, + guint info, guint time, gpointer data) +{ + if (info == DRAG_TYPE_OBJ) { + gtk_selection_data_set(selection_data, selection_data->target, + 8, (guchar *)"drag-from-list", 14); + } +} + +static gchar *addressbook_format_item_list(ItemPerson *person, ItemEMail *email) +{ + gchar *str = NULL; + gchar *eMailAlias = ADDRITEM_NAME(email); + + if (eMailAlias && *eMailAlias != '\0') { + if (person) { + str = g_strdup_printf("%s - %s", ADDRITEM_NAME(person), eMailAlias); + } else { + str = g_strdup(eMailAlias); + } + } + return str; +} + +static gboolean addressbook_match_item(const gchar *name, + const gchar *email_alias, + const gchar *addr, + const gchar *remarks, + const gchar *str) +{ + if (!name) + return FALSE; + if (!str || str[0] == '\0') + return TRUE; + if (strcasestr(name, str)) + return TRUE; + else if (email_alias && strcasestr(email_alias, str)) + return TRUE; + else if (addr && strcasestr(addr, str)) + return TRUE; + else if (remarks && strcasestr(remarks, str)) + return TRUE; + + return FALSE; +} + +static void addressbook_load_group(ItemGroup *itemGroup) +{ + GtkTreeView *listview = GTK_TREE_VIEW(addrbook.listview); + GtkTreeModel *model; + GtkTreeIter iter; + GList *items = itemGroup->listEMail; + AddressTypeControlItem *atci = addrbookctl_lookup(ADDR_ITEM_EMAIL); + const gchar *search_str; + + model = gtk_tree_view_get_model(listview); + search_str = gtk_entry_get_text(GTK_ENTRY(addrbook.entry)); + + for (; items != NULL; items = g_list_next(items)) { + ItemEMail *email = items->data; + ItemPerson *person; + const gchar *name; + gchar *str; + + if (!email) continue; + + person = (ItemPerson *)ADDRITEM_PARENT(email); + if (!person) { + g_warning("email %p (%s) don't have parent\n", email, email->address); + continue; + } + if (!addressbook_match_item(ADDRITEM_NAME(person), + ADDRITEM_NAME(email), + email->address, email->remarks, + search_str)) + continue; + + str = addressbook_format_item_list(person, email); + if (str) { + name = str; + } else { + name = ADDRITEM_NAME(person); + } + gtk_tree_store_append(GTK_TREE_STORE(model), &iter, NULL); + gtk_tree_store_set(GTK_TREE_STORE(model), &iter, + COL_NAME, name, + COL_ADDRESS, email->address ? email->address : "", + COL_NICKNAME, person->nickName ? person->nickName : "", + COL_REMARKS, email->remarks ? email->remarks : "", + COL_L_OBJ, email, + COL_L_PIXBUF, atci->icon_pixbuf, + -1); + g_free(str); + } +} + +static void addressbook_folder_load_person(ItemFolder *itemFolder) +{ + GtkTreeView *listview = GTK_TREE_VIEW(addrbook.listview); + GtkTreeModel *model; + GList *items; + AddressTypeControlItem *atci = addrbookctl_lookup(ADDR_ITEM_PERSON); + AddressTypeControlItem *atciMail = addrbookctl_lookup(ADDR_ITEM_EMAIL); + const gchar *search_str; + + if (atci == NULL) + return; + if (atciMail == NULL) + return; + + model = gtk_tree_view_get_model(listview); + search_str = gtk_entry_get_text(GTK_ENTRY(addrbook.entry)); + + /* Load email addresses */ + items = addritem_folder_get_person_list(itemFolder); + for (; items != NULL; items = g_list_next(items)) { + GtkTreeIter iperson, iemail; + gboolean flgFirst = TRUE, haveAddr = FALSE; + ItemPerson *person; + ItemEMail *email; + GList *node; + + person = (ItemPerson *)items->data; + if (!person) + continue; + + node = person->listEMail; + if (node && node->data) { + email = node->data; + if (!addressbook_match_item(ADDRITEM_NAME(person), ADDRITEM_NAME(email), email->address, email->remarks, search_str)) + continue; + } else { + if (!addressbook_match_item(ADDRITEM_NAME(person), NULL, NULL, NULL, search_str)) + continue; + } + + while (node) { + const gchar *name; + + email = node->data; + node = g_list_next(node); + + if (flgFirst) { + /* First email belongs with person */ + gchar *str = addressbook_format_item_list(person, email); + if (str) { + name = str; + } else { + name = ADDRITEM_NAME(person); + } + + gtk_tree_store_append(GTK_TREE_STORE(model), + &iperson, NULL); + gtk_tree_store_set(GTK_TREE_STORE(model), + &iperson, + COL_NAME, name, + COL_ADDRESS, email->address ? email->address : "", + COL_NICKNAME, person->nickName ? person->nickName : "", + COL_REMARKS, email->remarks ? email->remarks : "", + COL_L_OBJ, person, + COL_L_PIXBUF, atci->icon_pixbuf, + -1); + g_free(str); + } else { + /* Subsequent email is a child node of person */ + name = ADDRITEM_NAME(email); + gtk_tree_store_append(GTK_TREE_STORE(model), + &iemail, &iperson); + gtk_tree_store_set(GTK_TREE_STORE(model), + &iemail, + COL_NAME, name, + COL_ADDRESS, email->address ? email->address : "", + COL_NICKNAME, "", + COL_REMARKS, email->remarks ? email->remarks : "", + COL_L_OBJ, email, + COL_L_PIXBUF, atciMail->icon_pixbuf, + -1); + } + flgFirst = FALSE; + haveAddr = TRUE; + } + if (!haveAddr) { + /* Have name without EMail */ + gtk_tree_store_append(GTK_TREE_STORE(model), + &iperson, NULL); + gtk_tree_store_set(GTK_TREE_STORE(model), + &iperson, + COL_NAME, ADDRITEM_NAME(person), + COL_ADDRESS, "", + COL_NICKNAME, person->nickName ? person->nickName : "", + COL_REMARKS, "", + COL_L_OBJ, person, + COL_L_PIXBUF, atci->icon_pixbuf, + -1); + } + if (person->isOpened) { + GtkTreePath *path; + path = gtk_tree_model_get_path(model, &iperson); + gtk_tree_view_expand_row(listview, path, TRUE); + gtk_tree_path_free(path); + } + } + + /* Free up the list */ + mgu_clear_list(items); + g_list_free(items); +} + +static void addressbook_folder_load_group(ItemFolder *itemFolder) +{ + GtkTreeView *listview = GTK_TREE_VIEW(addrbook.listview); + GtkTreeModel *model; + GList *items; + AddressTypeControlItem *atci = addrbookctl_lookup(ADDR_ITEM_GROUP); + const gchar *search_str; + + if (!atci) + return; + + model = gtk_tree_view_get_model(listview); + search_str = gtk_entry_get_text(GTK_ENTRY(addrbook.entry)); + + /* Load any groups */ + items = addritem_folder_get_group_list(itemFolder); + for (; items != NULL; items = g_list_next(items)) { + GtkTreeIter iter; + ItemGroup *group = items->data; + + if (!group) + continue; + if (!addressbook_match_item(ADDRITEM_NAME(group), + NULL, NULL, NULL, search_str)) + continue; + + gtk_tree_store_append(GTK_TREE_STORE(model), &iter, NULL); + gtk_tree_store_set(GTK_TREE_STORE(model), &iter, + COL_NAME, ADDRITEM_NAME(group), + COL_ADDRESS, "", + COL_NICKNAME, "", + COL_REMARKS, "", + COL_L_OBJ, group, + COL_L_PIXBUF, atci->icon_pixbuf, + -1); + } + + /* Free up the list */ + mgu_clear_list(items); + g_list_free(items); +} + +static AddressDataSource *addressbook_find_datasource(GtkTreeIter *iter) +{ + GtkTreeModel *model; + GtkTreeIter iter_, parent; + AddressDataSource *ds = NULL; + AddressObject *ao; + + g_return_val_if_fail(addrbook.treeview != NULL, NULL); + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(addrbook.treeview)); + parent = *iter; + + do { + iter_ = parent; + if (gtk_tree_store_iter_depth(GTK_TREE_STORE(model), &iter_) < 1) + return NULL; + ao = NULL; + gtk_tree_model_get(model, &iter_, COL_OBJ, &ao, -1); + if (ao) { + /* g_print("ao->type = %d\n", ao->type); */ + if (ao->type == ADDR_DATASOURCE) { + AdapterDSource *ads = ADAPTER_DSOURCE(ao); + /* g_print("found it\n"); */ + ds = ads->dataSource; + break; + } + } + } while (gtk_tree_model_iter_parent(model, &parent, &iter_)); + + return ds; +} + +/* +* Load address list widget with children of specified object. +* Enter: obj Parent object to be loaded. +*/ +static void addressbook_set_list(AddressObject *obj) +{ + GtkTreeView *listview = GTK_TREE_VIEW(addrbook.listview); + GtkTreeModel *model; + AddressDataSource *ds = NULL; + AdapterDSource *ads = NULL; + gboolean sorted; + gint address_list_sort_id = COL_NAME; + GtkSortType order = GTK_SORT_ASCENDING; + + debug_print("addressbook_set_list\n"); + + model = gtk_tree_view_get_model(listview); + + if (obj == NULL) { + gtk_tree_store_clear(GTK_TREE_STORE(model)); + return; + } + + if (obj->type == ADDR_INTERFACE) { + return; + } + + gtk_tree_store_clear(GTK_TREE_STORE(model)); + sorted = gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(model), + &address_list_sort_id, + &order); + if (!sorted) { + address_list_sort_id = COL_NAME; + order = GTK_SORT_ASCENDING; + } + gtkut_tree_sortable_unset_sort_column_id(GTK_TREE_SORTABLE(model)); + + if (obj->type == ADDR_DATASOURCE) { + ads = ADAPTER_DSOURCE(obj); + ds = ADAPTER_DSOURCE(obj)->dataSource; + if (ds) { + /* Load root folder */ + ItemFolder *rootFolder = NULL; + rootFolder = addrindex_ds_get_root_folder(ds); + addressbook_folder_load_person(addrindex_ds_get_root_folder(ds)); + addressbook_folder_load_group(addrindex_ds_get_root_folder(ds)); + } + } else if (obj->type == ADDR_ITEM_GROUP) { + /* Load groups */ + ItemGroup *itemGroup = ADAPTER_GROUP(obj)->itemGroup; + addressbook_load_group(itemGroup); + } else if (obj->type == ADDR_ITEM_FOLDER) { + /* Load folders */ + ItemFolder *itemFolder = ADAPTER_FOLDER(obj)->itemFolder; + addressbook_folder_load_person(itemFolder); + addressbook_folder_load_group(itemFolder); + } + + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), + address_list_sort_id, order); +} + +/* +* Free adaptor for specified node. +*/ +static void addressbook_free_adapter(GtkTreeIter *iter) +{ + GtkTreeModel *model; + AddressObject *ao = NULL; + + g_return_if_fail(addrbook.treeview != NULL); + g_return_if_fail(iter != NULL); + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(addrbook.treeview)); + if (gtk_tree_store_iter_depth(GTK_TREE_STORE(model), iter) < 1) + return; + gtk_tree_model_get(model, iter, COL_OBJ, &ao, -1); + if (ao == NULL) + return; + + if (ao->type == ADDR_INTERFACE) { + AdapterInterface *ai = ADAPTER_INTERFACE(ao); + addrbookctl_free_interface(ai); + } else if (ao->type == ADDR_DATASOURCE) { + AdapterDSource *ads = ADAPTER_DSOURCE(ao); + addrbookctl_free_datasource(ads); + } else if (ao->type == ADDR_ITEM_FOLDER) { + AdapterFolder *af = ADAPTER_FOLDER(ao); + addrbookctl_free_folder(af); + } else if (ao->type == ADDR_ITEM_GROUP) { + AdapterGroup *ag = ADAPTER_GROUP(ao); + addrbookctl_free_group(ag); + } + + gtk_tree_store_set(GTK_TREE_STORE(model), iter, COL_OBJ, NULL, -1); +} + +/* +* Free all children adapters. +*/ +static void addressbook_free_child_adapters(GtkTreeIter *iter) +{ + GtkTreeModel *model; + GtkTreeIter child; + gboolean valid; + + g_return_if_fail(iter != NULL); + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(addrbook.treeview)); + valid = gtk_tree_model_iter_children(model, &child, iter); + while (valid) { + addressbook_free_child_adapters(&child); + addressbook_free_adapter(&child); + valid = gtk_tree_model_iter_next(model, &child); + } +} + +AdapterDSource *addressbook_create_ds_adapter(AddressDataSource *ds, + AddressObjectType otype, + gchar *name) +{ + AdapterDSource *adapter = g_new0(AdapterDSource, 1); + ADDRESS_OBJECT(adapter)->type = ADDR_DATASOURCE; + ADDRESS_OBJECT_NAME(adapter) = g_strdup(name); + adapter->dataSource = ds; + adapter->subType = otype; + return adapter; +} + +void addressbook_ads_set_name(AdapterDSource *adapter, gchar *value) +{ + ADDRESS_OBJECT_NAME(adapter) = + mgu_replace_string(ADDRESS_OBJECT_NAME(adapter), value); +} + +/* + * Load tree from address index with the initial data. + */ +static void addressbook_load_tree(void) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeModel *model; + GtkTreeIter iter, new_iter; + GtkTreePath *path; + GList *nodeIf, *nodeDS; + AdapterInterface *adapter; + AddressInterface *iface; + AddressTypeControlItem *atci; + AddressDataSource *ds; + AdapterDSource *ads; + gchar *name; + + model = gtk_tree_view_get_model(treeview); + + nodeIf = _addressInterfaceList_; + while (nodeIf) { + adapter = nodeIf->data; + gtkut_tree_row_reference_get_iter(model, adapter->tree_row, &iter); + iface = adapter->iface; + atci = adapter->atci; + if (iface) { + if (iface->useInterface) { + /* Load data sources below interface node */ + nodeDS = iface->listSource; + while (nodeDS) { + ds = nodeDS->data; + name = addrindex_ds_get_name(ds); + ads = addressbook_create_ds_adapter(ds, atci->objectType, name); + addressbook_add_object(&iter, &new_iter, ADDRESS_OBJECT(ads)); + nodeDS = g_list_next( nodeDS ); + } + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_view_expand_row(treeview, path, TRUE); + gtk_tree_path_free(path); + } + } + nodeIf = g_list_next(nodeIf); + } +} + +/* + * Convert the old address book to new format. + */ +static gboolean addressbook_convert(AddressIndex *addrIndex) +{ + gboolean retVal = FALSE; + gboolean errFlag = TRUE; + gchar *msg = NULL; + + /* Read old address book, performing conversion */ + debug_print( "Reading and converting old address book...\n" ); + addrindex_set_file_name( addrIndex, ADDRESSBOOK_OLD_FILE ); + addrindex_read_data( addrIndex ); + if( addrIndex->retVal == MGU_NO_FILE ) { + /* We do not have a file - new user */ + debug_print( "New user... create new books...\n" ); + addrindex_create_new_books( addrIndex ); + if( addrIndex->retVal == MGU_SUCCESS ) { + /* Save index file */ + addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE ); + addrindex_save_data( addrIndex ); + if( addrIndex->retVal == MGU_SUCCESS ) { + retVal = TRUE; + errFlag = FALSE; + } + else { + msg = _( "New user, could not save index file." ); + } + } + else { + msg = _( "New user, could not save address book files." ); + } + } + else { + /* We have an old file */ + if( addrIndex->wasConverted ) { + /* Converted successfully - save address index */ + addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE ); + addrindex_save_data( addrIndex ); + if( addrIndex->retVal == MGU_SUCCESS ) { + msg = _( "Old address book converted successfully." ); + retVal = TRUE; + errFlag = FALSE; + } + else { + msg = _("Old address book converted,\n" + "could not save new address index file" ); + } + } + else { + /* File conversion failed - just create new books */ + debug_print( "File conversion failed... just create new books...\n" ); + addrindex_create_new_books( addrIndex ); + if( addrIndex->retVal == MGU_SUCCESS ) { + /* Save index */ + addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE ); + addrindex_save_data( addrIndex ); + if( addrIndex->retVal == MGU_SUCCESS ) { + msg = _("Could not convert address book,\n" + "but created empty new address book files." ); + retVal = TRUE; + errFlag = FALSE; + } + else { + msg = _("Could not convert address book,\n" + "could not create new address book files." ); + } + } + else { + msg = _("Could not convert address book\n" + "and could not create new address book files." ); + } + } + } + if( errFlag ) { + debug_print( "Error\n%s\n", msg ); + alertpanel( _( "Address book conversion error" ), msg, GTK_STOCK_CLOSE, NULL, NULL ); + } + else if( msg ) { + debug_print( "Warning\n%s\n", msg ); + alertpanel( _( "Address book conversion" ), msg, GTK_STOCK_CLOSE, NULL, NULL ); + } + + return retVal; +} + +void addressbook_read_file(void) +{ + AddressIndex *addrIndex = NULL; + + debug_print( "Reading address index...\n" ); + if (_addressIndex_) { + debug_print("address book already read!\n"); + return; + } + + addrIndex = addrindex_create_index(); + + /* Use new address book index. */ + addrindex_set_file_path(addrIndex, get_rc_dir()); + addrindex_set_file_name(addrIndex, ADDRESSBOOK_INDEX_FILE); + addrindex_read_data(addrIndex); + if (addrIndex->retVal == MGU_NO_FILE) { + /* Conversion required */ + debug_print("Converting...\n"); + if (addressbook_convert(addrIndex)) { + addrindex_create_extra_books(addrIndex); + _addressIndex_ = addrIndex; + } + } else if (addrIndex->retVal == MGU_SUCCESS) { + addrindex_create_extra_books(addrIndex); + _addressIndex_ = addrIndex; + } else { + gchar msg[1024]; + + /* Error reading address book */ + debug_print("Could not read address index.\n"); + addrindex_print_index(addrIndex, stdout); + g_snprintf(msg, sizeof(msg), + _("Could not read address index:\n\n%s%c%s"), + addrIndex->filePath, G_DIR_SEPARATOR, + addrIndex->fileName); + alertpanel_message(_("Address Book Error"), msg, ALERT_ERROR); + } + debug_print( "done.\n" ); +} + +#if 0 +void addressbook_read_file_old( void ) { + AddressIndex *addrIndex = NULL; + gboolean errFlag = TRUE; + gchar *msg = NULL; + + if( _addressIndex_ ) { + debug_print( "address book already read!!!\n" ); + return; + } + + addrIndex = addrindex_create_index(); + + /* Use use new address book. */ + /* addrindex_set_file_path( addrIndex, "/home/match/tmp/empty-dir" ); */ + addrindex_set_file_path( addrIndex, get_rc_dir() ); + addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE ); + + debug_print( "Reading address index...\n" ); + addrindex_read_data( addrIndex ); + if( addrIndex->retVal == MGU_NO_FILE ) { + /* Read old address book, performing conversion */ + debug_print( "Reading and converting old address book...\n" ); + addrindex_set_file_name( addrIndex, ADDRESSBOOK_OLD_FILE ); + addrindex_read_data( addrIndex ); + if( addrIndex->retVal == MGU_NO_FILE ) { + /* We do not have a file - new user */ + debug_print( "New user... create new books...\n" ); + addrindex_create_new_books( addrIndex ); + if( addrIndex->retVal == MGU_SUCCESS ) { + /* Save index file */ + addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE ); + addrindex_save_data( addrIndex ); + if( addrIndex->retVal == MGU_SUCCESS ) { + errFlag = FALSE; + } + else { + msg = g_strdup( _( "New user, could not save index file." ) ); + } + } + else { + msg = g_strdup( _( "New user, could not save address book files." ) ); + } + } + else { + /* We have an old file */ + if( addrIndex->wasConverted ) { + /* Converted successfully - save address index */ + addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE ); + addrindex_save_data( addrIndex ); + if( addrIndex->retVal == MGU_SUCCESS ) { + msg = g_strdup( _( "Old address book converted successfully." ) ); + errFlag = FALSE; + } + else { + msg = g_strdup( _( + "Old address book converted, " \ + "could not save new address index file" ) ); + } + } + else { + /* File conversion failed - just create new books */ + debug_print( "File conversion failed... just create new books...\n" ); + addrindex_create_new_books( addrIndex ); + if( addrIndex->retVal == MGU_SUCCESS ) { + /* Save index */ + addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE ); + addrindex_save_data( addrIndex ); + if( addrIndex->retVal == MGU_SUCCESS ) { + msg = g_strdup( _( + "Could not convert address book, " \ + "but created empty new address book files." ) ); + errFlag = FALSE; + } + else { + msg = g_strdup( _( + "Could not convert address book, " \ + "could not create new address book files." ) ); + } + } + else { + msg = g_strdup( _( + "Could not convert address book " \ + "and could not create new address book files." ) ); + } + } + } + } + else if( addrIndex->retVal == MGU_SUCCESS ) { + errFlag = FALSE; + } + else { + debug_print( "Could not read address index.\n" ); + addrindex_print_index( addrIndex, stdout ); + msg = g_strdup( _( "Could not read address index" ) ); + } + _addressIndex_ = addrIndex; + + if( errFlag ) { + debug_print( "Error\n%s\n", msg ); + alertpanel( _( "Address Book Conversion Error" ), msg, + GTK_STOCK_CLOSE, NULL, NULL ); + } + else { + if( msg ) { + debug_print( "Warning\n%s\n", msg ); + alertpanel( _( "Address Book Conversion" ), msg, + GTK_STOCK_CLOSE, NULL, NULL ); + } + } + if( msg ) g_free( msg ); + debug_print( "done.\n" ); +} +#endif + +/* +* Add object into the address index tree widget. +* Enter: node Parent node. +* obj Object to add. +* Return: Node that was added, or NULL if object not added. +*/ +static gboolean addressbook_add_object(GtkTreeIter *iter, GtkTreeIter *new_iter, + AddressObject *obj) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeModel *model; + GtkTreeIter added; + AddressObject *pobj; + AddressObjectType otype; + AddressTypeControlItem *atci = NULL; + const gchar *name; + + g_return_val_if_fail(iter != NULL, FALSE); + g_return_val_if_fail(obj != NULL, FALSE); + + model = gtk_tree_view_get_model(treeview); + gtk_tree_model_get(model, iter, COL_OBJ, &pobj, -1); + g_return_val_if_fail(pobj != NULL, FALSE); + + /* Determine object type to be displayed */ + if (obj->type == ADDR_DATASOURCE) { + otype = ADAPTER_DSOURCE(obj)->subType; + } else { + otype = obj->type; + } + + /* Handle any special conditions. */ + atci = addrbookctl_lookup(otype); + if (atci && atci->showInTree) { + /* Add object to tree */ + debug_print("addressbook_add_object: obj: %s\n", obj->name); + if (otype == ADDR_BOOK && obj->name && + !strcmp(obj->name, ADDR_DS_AUTOREG)) + name = _("Auto-registered address"); + else + name = obj->name; + gtk_tree_store_append(GTK_TREE_STORE(model), &added, iter); + gtk_tree_store_set(GTK_TREE_STORE(model), &added, + COL_FOLDER_NAME, name, + COL_OBJ, obj, + COL_PIXBUF, atci->icon_pixbuf, + COL_PIXBUF_OPEN, atci->icon_open_pixbuf, + -1); + if (new_iter) + *new_iter = added; + return TRUE; + } + + return FALSE; +} + +/* +* Add group into the address index tree. +* Enter: node Parent node. +* ds Data source. +* itemGroup Group to add. +* Return: Inserted node. +*/ +static gboolean addressbook_node_add_group(GtkTreeIter *iter, AddressDataSource *ds, ItemGroup *itemGroup, GtkTreeIter *new_iter) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeStore *store; + GtkTreeIter new_iter_; + AdapterGroup *adapter; + AddressTypeControlItem *atci = NULL; + gchar *name; + + if(ds == NULL) return FALSE; + if(iter == NULL || itemGroup == NULL) return FALSE; + + name = itemGroup->obj.name; + + atci = addrbookctl_lookup(ADDR_ITEM_GROUP); + + adapter = g_new0(AdapterGroup, 1); + ADDRESS_OBJECT_TYPE(adapter) = ADDR_ITEM_GROUP; + ADDRESS_OBJECT_NAME(adapter) = g_strdup(ADDRITEM_NAME(itemGroup)); + adapter->itemGroup = itemGroup; + + debug_print("addressbook_node_add_group: name: %s\n", name); + store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview)); + gtk_tree_store_append(store, &new_iter_, iter); + gtk_tree_store_set(store, &new_iter_, + COL_FOLDER_NAME, name, + COL_OBJ, adapter, + COL_PIXBUF, atci->icon_pixbuf, + COL_PIXBUF_OPEN, atci->icon_open_pixbuf, + -1); + if (new_iter) + *new_iter = new_iter_; + return TRUE; +} + +/* +* Add folder into the address index tree. +* Enter: iter Parent node. +* ds Data source. +* itemFolder Folder to add. +* otype Object type to display. +* new_iter Inserted node. +* Return: TRUE if inserted. +*/ +static gboolean addressbook_node_add_folder(GtkTreeIter *iter, AddressDataSource *ds, ItemFolder *itemFolder, AddressObjectType otype, GtkTreeIter *new_iter) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeStore *store; + GtkTreeIter new_iter_; + AdapterFolder *adapter; + AddressTypeControlItem *atci = NULL; + GList *listItems = NULL; + gchar *name; + ItemFolder *rootFolder; + + if (ds == NULL) + return FALSE; + if (iter == NULL || itemFolder == NULL) + return FALSE; + + /* Determine object type */ + atci = addrbookctl_lookup(otype); + if (atci == NULL) + return FALSE; + + store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview)); + + rootFolder = addrindex_ds_get_root_folder(ds); + if (itemFolder == rootFolder) { + new_iter_ = *iter; + } else { + name = itemFolder->obj.name; + + adapter = g_new0(AdapterFolder, 1); + ADDRESS_OBJECT_TYPE(adapter) = ADDR_ITEM_FOLDER; + ADDRESS_OBJECT_NAME(adapter) = g_strdup(ADDRITEM_NAME(itemFolder)); + adapter->itemFolder = itemFolder; + + debug_print("addressbook_node_add_folder: name: %s\n", name); + gtk_tree_store_append(store, &new_iter_, iter); + gtk_tree_store_set(store, &new_iter_, + COL_FOLDER_NAME, name, + COL_OBJ, adapter, + COL_PIXBUF, atci->icon_pixbuf, + COL_PIXBUF_OPEN, atci->icon_open_pixbuf, + -1); + } + + listItems = itemFolder->listFolder; + while (listItems) { + ItemFolder *item = listItems->data; + addressbook_node_add_folder(&new_iter_, ds, item, otype, NULL); + listItems = g_list_next(listItems); + } + listItems = itemFolder->listGroup; + while (listItems) { + ItemGroup *item = listItems->data; + addressbook_node_add_group(&new_iter_, ds, item, NULL); + listItems = g_list_next(listItems); + } + if (new_iter) + *new_iter = new_iter_; + return TRUE; +} + +#if 0 +static void addressbook_delete_object(AddressObject *obj) { + AdapterDSource *ads = NULL; + AddressDataSource *ds = NULL; + if (!obj) return; + + /* Remove data source. */ + /* printf( "Delete obj type : %d\n", obj->type ); */ + + ads = ADAPTER_DSOURCE(obj); + if( ads == NULL ) return; + ds = ads->dataSource; + if( ds == NULL ) return; + + /* Remove data source */ + if( addrindex_index_remove_datasource( _addressIndex_, ds ) ) { + addrindex_free_datasource( _addressIndex_, ds ); + } + /* Free up Adapter object */ + g_free( ADAPTER_DSOURCE(obj) ); +} +#endif + +void addressbook_export_to_file( void ) { + if( _addressIndex_ ) { + /* Save all new address book data */ + debug_print( "Saving address books...\n" ); + addrindex_save_all_books( _addressIndex_ ); + + debug_print( "Exporting addressbook to file...\n" ); + addrindex_save_data( _addressIndex_ ); + if( _addressIndex_->retVal != MGU_SUCCESS ) { + addrindex_print_index( _addressIndex_, stdout ); + } + + /* Notify address completion of new data */ + addressbook_modified(); + } +} + +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + addressbook_close(); + return FALSE; +} + +static void size_allocated(GtkWidget *widget, GtkAllocation *allocation, + gpointer data) +{ + if (allocation->width <= 1 || allocation->height <= 1) + return; + + prefs_common.addressbook_width = allocation->width; + prefs_common.addressbook_height = allocation->height; +} + +/* +* Comparison using cell contents (text in first column). +*/ +static gint addressbook_tree_compare(GtkTreeModel *model, GtkTreeIter *a, + GtkTreeIter *b, gpointer data) +{ + gchar *name1 = NULL, *name2 = NULL; + AddressObject *obj1 = NULL, *obj2 = NULL; + gint ret; + + gtk_tree_model_get(model, a, COL_FOLDER_NAME, &name1, COL_OBJ, &obj1, + -1); + gtk_tree_model_get(model, b, COL_FOLDER_NAME, &name2, COL_OBJ, &obj2, + -1); + + /* Do not sort toplevel row */ + if (obj1 && obj1->type == ADDR_INTERFACE) + return 0; + + if (!name1 || !name2) { + if (!name1) + ret = (name2 != NULL); + else + ret = -1; + g_free(name2); + g_free(name1); + return ret; + } + ret = g_ascii_strcasecmp(name1, name2); + g_free(name2); + g_free(name1); + return ret; +} + +static gint addressbook_list_col_compare(GtkTreeModel *model, GtkTreeIter *a, + GtkTreeIter *b, gint col) +{ + gchar *name1 = NULL, *name2 = NULL; + gint ret; + + gtk_tree_model_get(model, a, col, &name1, -1); + gtk_tree_model_get(model, b, col, &name2, -1); + + if (!name1) + name1 = g_strdup(""); + if (!name2) + name2 = g_strdup(""); + ret = g_ascii_strcasecmp(name1, name2); + g_free(name2); + g_free(name1); + return ret; +} + +static gint addressbook_list_name_compare(GtkTreeModel *model, GtkTreeIter *a, + GtkTreeIter *b, gpointer data) +{ + gint ret; + + ret = addressbook_list_col_compare(model, a, b, COL_NAME); + if (ret == 0) + ret = addressbook_list_col_compare(model, a, b, COL_NICKNAME); + if (ret == 0) + ret = addressbook_list_col_compare(model, a, b, COL_ADDRESS); + if (ret == 0) + ret = addressbook_list_col_compare(model, a, b, COL_REMARKS); + + return ret; +} + +static gint addressbook_list_address_compare(GtkTreeModel *model, + GtkTreeIter *a, GtkTreeIter *b, + gpointer data) +{ + gint ret; + + ret = addressbook_list_col_compare(model, a, b, COL_ADDRESS); + if (ret == 0) + ret = addressbook_list_col_compare(model, a, b, COL_NAME); + if (ret == 0) + ret = addressbook_list_col_compare(model, a, b, COL_NICKNAME); + if (ret == 0) + ret = addressbook_list_col_compare(model, a, b, COL_REMARKS); + + return ret; +} + +static gint addressbook_list_nickname_compare(GtkTreeModel *model, + GtkTreeIter *a, GtkTreeIter *b, + gpointer data) +{ + gint ret; + + ret = addressbook_list_col_compare(model, a, b, COL_NICKNAME); + if (ret == 0) + ret = addressbook_list_col_compare(model, a, b, COL_NAME); + if (ret == 0) + ret = addressbook_list_col_compare(model, a, b, COL_ADDRESS); + if (ret == 0) + ret = addressbook_list_col_compare(model, a, b, COL_REMARKS); + + return ret; +} + +static gint addressbook_list_remarks_compare(GtkTreeModel *model, + GtkTreeIter *a, GtkTreeIter *b, + gpointer data) +{ + gint ret; + + ret = addressbook_list_col_compare(model, a, b, COL_REMARKS); + if (ret == 0) + ret = addressbook_list_col_compare(model, a, b, COL_NAME); + if (ret == 0) + ret = addressbook_list_col_compare(model, a, b, COL_NICKNAME); + if (ret == 0) + ret = addressbook_list_col_compare(model, a, b, COL_ADDRESS); + + return ret; +} + +static gboolean addressbook_list_select_func(GtkTreeSelection *selection, + GtkTreeModel *model, + GtkTreePath *path, + gboolean cur_selected, + gpointer data) +{ + return can_toggle_list_selection; +} + +/* static */ +gint addressbook_obj_name_compare(gconstpointer a, gconstpointer b) +{ + const AddressObject *obj = a; + const gchar *name = b; + AddressTypeControlItem *atci = NULL; + + if (!obj || !name) return -1; + + atci = addrbookctl_lookup( obj->type ); + if( ! atci ) return -1; + if( ! obj->name ) return -1; + return g_ascii_strcasecmp(obj->name, name); +} + +#if 0 +static void addressbook_book_show_message( AddressBookFile *abf ) { + gchar msgbuf[ADDRESSBOOK_MSGBUF_SIZE] = ""; + + if (abf) { + if (abf->retVal == MGU_SUCCESS) { + g_snprintf(msgbuf, sizeof(msgbuf), "%s", abf->name); + } else { + g_snprintf(msgbuf, sizeof(msgbuf), "%s: %s", abf->name, mgu_error2string(abf->retVal)); + } + } + addressbook_status_show(msgbuf); +} +#endif + +static void addressbook_new_book_cb(gpointer data, guint action, GtkWidget *widget) +{ + GtkTreeModel *model; + GtkTreeIter iter; + AdapterDSource *ads; + AdapterInterface *adapter; + + adapter = addrbookctl_find_interface(ADDR_IF_BOOK); + if (adapter == NULL) + return; + if (!gtkut_tree_row_reference_equal(addrbook.tree_selected, adapter->tree_row)) + return; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(addrbook.treeview)); + gtkut_tree_row_reference_get_iter(model, addrbook.tree_selected, &iter); + + ads = addressbook_edit_book(_addressIndex_, NULL); + if (ads) { + addressbook_add_object(&iter, NULL, ADDRESS_OBJECT(ads)); + if (gtkut_tree_row_reference_equal(addrbook.tree_selected, addrbook.tree_opened)) { + addressbook_reopen(); + } + } +} + +static void addressbook_new_vcard_cb(gpointer data, guint action, GtkWidget *widget) +{ + GtkTreeModel *model; + GtkTreeIter iter; + AdapterDSource *ads; + AdapterInterface *adapter; + + adapter = addrbookctl_find_interface(ADDR_IF_VCARD); + if (adapter == NULL) + return; + if (!gtkut_tree_row_reference_equal(addrbook.tree_selected, adapter->tree_row)) + return; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(addrbook.treeview)); + gtkut_tree_row_reference_get_iter(model, addrbook.tree_selected, &iter); + + ads = addressbook_edit_vcard(_addressIndex_, NULL); + if (ads) { + addressbook_add_object(&iter, NULL, ADDRESS_OBJECT(ads)); + if (gtkut_tree_row_reference_equal(addrbook.tree_selected, addrbook.tree_opened)) { + addressbook_reopen(); + } + addressbook_modified(); + } +} + +#if 0 +static void addressbook_vcard_show_message( VCardFile *vcf ) { + gchar msgbuf[ADDRESSBOOK_MSGBUF_SIZE] = ""; + + if (vcf) { + if (vcf->retVal == MGU_SUCCESS) { + g_snprintf(msgbuf, sizeof(msgbuf), "%s", vcf->name); + } else { + g_snprintf(msgbuf, sizeof(msgbuf), "%s: %s", vcf->name, mgu_error2string(vcf->retVal)); + } + } + addressbook_status_show(msgbuf); +} +#endif + +#ifdef USE_JPILOT +static void addressbook_new_jpilot_cb(gpointer data, guint action, GtkWidget *widget) +{ + GtkTreeModel *model; + GtkTreeIter iter; + AdapterDSource *ads; + AdapterInterface *adapter; + AddressInterface *iface; + + adapter = addrbookctl_find_interface(ADDR_IF_JPILOT); + if (adapter == NULL) + return; + if (!gtkut_tree_row_reference_equal(addrbook.tree_selected, adapter->tree_row)) + return; + iface = adapter->iface; + if (!iface->haveLibrary) + return; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(addrbook.treeview)); + gtkut_tree_row_reference_get_iter(model, addrbook.tree_selected, &iter); + + ads = addressbook_edit_jpilot(_addressIndex_, NULL); + if (ads) { + addressbook_add_object(&iter, NULL, ADDRESS_OBJECT(ads)); + if (gtkut_tree_row_reference_equal(addrbook.tree_selected, addrbook.tree_opened)) { + addressbook_reopen(); + } + } +} + +#if 0 +static void addressbook_jpilot_show_message( JPilotFile *jpf ) { + gchar msgbuf[ADDRESSBOOK_MSGBUF_SIZE] = ""; + + if (jpf) { + if (jpf->retVal == MGU_SUCCESS) { + g_snprintf(msgbuf, sizeof(msgbuf), "%s", jpf->name); + } else { + g_snprintf(msgbuf, sizeof(msgbuf), "%s: %s", jpf->name, mgu_error2string(jpf->retVal)); + } + } + addressbook_status_show(msgbuf); +} +#endif +#endif /* USE_JPILOT */ + +#ifdef USE_LDAP +static void addressbook_new_ldap_cb(gpointer data, guint action, GtkWidget *widget) +{ + GtkTreeModel *model; + GtkTreeIter iter; + AdapterDSource *ads; + AdapterInterface *adapter; + AddressInterface *iface; + + adapter = addrbookctl_find_interface(ADDR_IF_LDAP); + if (adapter == NULL) + return; + if (!gtkut_tree_row_reference_equal(addrbook.tree_selected, adapter->tree_row)) + return; + iface = adapter->iface; + if (!iface->haveLibrary) + return; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(addrbook.treeview)); + gtkut_tree_row_reference_get_iter(model, addrbook.tree_selected, &iter); + + ads = addressbook_edit_ldap(_addressIndex_, NULL); + if( ads ) { + addressbook_add_object(&iter, NULL, ADDRESS_OBJECT(ads)); + if (gtkut_tree_row_reference_equal(addrbook.tree_selected, addrbook.tree_opened)) { + addressbook_reopen(); + } + } +} + +static void addressbook_ldap_show_message(SyldapServer *svr) +{ + gchar msgbuf[ADDRESSBOOK_MSGBUF_SIZE] = ""; + + if (svr) { + if (svr->busyFlag) { + g_snprintf(msgbuf, sizeof(msgbuf), "%s: %s", svr->name, ADDRESSBOOK_LDAP_BUSYMSG); + } else { + if (svr->retVal == MGU_SUCCESS) { + g_snprintf(msgbuf, sizeof(msgbuf), "%s", svr->name); + } else { + g_snprintf(msgbuf, sizeof(msgbuf), "%s: %s", svr->name, mgu_error2string(svr->retVal)); + } + } + } + addressbook_status_show(msgbuf); +} + +static void ldapsearch_callback(SyldapServer *sls) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + AddressObject *obj; + AdapterDSource *ads = NULL; + AddressDataSource *ds = NULL; + AddressInterface *iface = NULL; + + if (sls == NULL) + return; + + selection = gtk_tree_view_get_selection(treeview); + if (!gtk_tree_selection_get_selected(selection, &model, &iter)) + return; + if (gtk_tree_store_iter_depth(GTK_TREE_STORE(model), &iter) == 0) + return; + + gtk_tree_model_get(model, &iter, COL_OBJ, &obj, -1); + if (obj == NULL) + return; + if (obj->type == ADDR_DATASOURCE) { + ads = ADAPTER_DSOURCE(obj); + if (ads->subType == ADDR_LDAP) { + SyldapServer *server; + + ds = ads->dataSource; + if (ds == NULL) + return; + iface = ds->iface; + if (!iface->haveLibrary) + return; + server = ds->rawDataSource; + if (server == sls) { + /* Read from cache */ + gtk_widget_show(addrbook.window); + addressbook_set_list(obj); + addressbook_ldap_show_message(sls); + gtk_widget_show(addrbook.window); + gtk_entry_set_text(GTK_ENTRY(addrbook.entry), ""); + } + } + } +} +#endif + +/* + * Lookup button handler. + */ +static void addressbook_lup_clicked(GtkButton *button, gpointer data) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + AddressObject *obj; + AdapterDSource *ads = NULL; +#ifdef USE_LDAP + AddressDataSource *ds = NULL; + AddressInterface *iface = NULL; +#endif /* USE_LDAP */ + gchar *sLookup; + + selection = gtk_tree_view_get_selection(treeview); + if (!gtk_tree_selection_get_selected(selection, &model, &iter)) + return; + if (gtk_tree_store_iter_depth(GTK_TREE_STORE(model), &iter) == 0) + return; + + gtk_tree_model_get(model, &iter, COL_OBJ, &obj, -1); + if (obj == NULL) + return; + + sLookup = gtk_editable_get_chars(GTK_EDITABLE(addrbook.entry), 0, -1); + g_strstrip(sLookup); + + if (obj->type == ADDR_DATASOURCE) { + ads = ADAPTER_DSOURCE(obj); +#ifdef USE_LDAP + if (ads->subType == ADDR_LDAP) { + SyldapServer *server; + + ds = ads->dataSource; + if (ds == NULL) + return; + iface = ds->iface; + if (!iface->haveLibrary) + return; + server = ds->rawDataSource; + if (server) { + syldap_cancel_read(server); + if (*sLookup == '\0' || strlen(sLookup) < 1 ) + return; + syldap_set_search_value(server, sLookup); + syldap_set_callback(server, ldapsearch_callback); + syldap_read_data_th(server); + addressbook_ldap_show_message(server); + } + } else +#endif /* USE_LDAP */ + addressbook_set_list(obj); + } else { + addressbook_set_list(obj); + } + + g_free(sLookup); +} + +static void addressbook_close_clicked(GtkButton *button, gpointer data) +{ + addressbook_close(); +} + +/* ********************************************************************** +* Build lookup tables. +* *********************************************************************** +*/ + +/* +* Build table that controls the rendering of object types. +*/ +void addrbookctl_build_map(GtkWidget *window) +{ + AddressTypeControlItem *atci; + + /* Build icons */ + stock_pixbuf_gdk(window, STOCK_PIXMAP_FOLDER_CLOSE, &folderpix); + stock_pixbuf_gdk(window, STOCK_PIXMAP_FOLDER_OPEN, &folderopenpix); + stock_pixbuf_gdk(window, STOCK_PIXMAP_GROUP, &grouppix); + stock_pixbuf_gdk(window, STOCK_PIXMAP_VCARD, &vcardpix); + stock_pixbuf_gdk(window, STOCK_PIXMAP_BOOK, &bookpix); + stock_pixbuf_gdk(window, STOCK_PIXMAP_PERSON, &personpix); + stock_pixbuf_gdk(window, STOCK_PIXMAP_ADDRESS, &addresspix); + stock_pixbuf_gdk(window, STOCK_PIXMAP_JPILOT, &jpilotpix); + stock_pixbuf_gdk(window, STOCK_PIXMAP_CATEGORY, &categorypix); + stock_pixbuf_gdk(window, STOCK_PIXMAP_LDAP, &ldappix); + + _addressBookTypeHash_ = g_hash_table_new(g_int_hash, g_int_equal); + _addressBookTypeList_ = NULL; + + /* Interface */ + atci = g_new0(AddressTypeControlItem, 1); + atci->objectType = ADDR_INTERFACE; + atci->interfaceType = ADDR_IF_NONE; + atci->showInTree = TRUE; + atci->treeExpand = TRUE; + atci->treeLeaf = FALSE; + atci->displayName = _("Interface"); + atci->icon_pixbuf = folderpix; + atci->icon_open_pixbuf = folderopenpix; + atci->menuCommand = NULL; + g_hash_table_insert(_addressBookTypeHash_, &atci->objectType, atci); + _addressBookTypeList_ = g_list_append(_addressBookTypeList_, atci); + + /* Address book */ + atci = g_new0(AddressTypeControlItem, 1); + atci->objectType = ADDR_BOOK; + atci->interfaceType = ADDR_IF_BOOK; + atci->showInTree = TRUE; + atci->treeExpand = TRUE; + atci->treeLeaf = FALSE; + atci->displayName = _("Address Book"); + atci->icon_pixbuf = bookpix; + atci->icon_open_pixbuf = bookpix; + atci->menuCommand = "/File/New Book"; + g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci ); + _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci ); + + /* Item person */ + atci = g_new0(AddressTypeControlItem, 1); + atci->objectType = ADDR_ITEM_PERSON; + atci->interfaceType = ADDR_IF_NONE; + atci->showInTree = FALSE; + atci->treeExpand = FALSE; + atci->treeLeaf = FALSE; + atci->displayName = _("Person"); + atci->icon_pixbuf = personpix; + atci->icon_open_pixbuf = personpix; + atci->menuCommand = NULL; + g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci ); + _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci ); + + /* Item email */ + atci = g_new0(AddressTypeControlItem, 1); + atci->objectType = ADDR_ITEM_EMAIL; + atci->interfaceType = ADDR_IF_NONE; + atci->showInTree = FALSE; + atci->treeExpand = FALSE; + atci->treeLeaf = TRUE; + atci->displayName = _("EMail Address"); + atci->icon_pixbuf = addresspix; + atci->icon_open_pixbuf = addresspix; + atci->menuCommand = NULL; + g_hash_table_insert(_addressBookTypeHash_, &atci->objectType, atci); + _addressBookTypeList_ = g_list_append(_addressBookTypeList_, atci); + + /* Item group */ + atci = g_new0(AddressTypeControlItem, 1); + atci->objectType = ADDR_ITEM_GROUP; + atci->interfaceType = ADDR_IF_BOOK; + atci->showInTree = TRUE; + atci->treeExpand = FALSE; + atci->treeLeaf = FALSE; + atci->displayName = _("Group"); + atci->icon_pixbuf = grouppix; + atci->icon_open_pixbuf = grouppix; + atci->menuCommand = NULL; + g_hash_table_insert(_addressBookTypeHash_, &atci->objectType, atci); + _addressBookTypeList_ = g_list_append(_addressBookTypeList_, atci); + + /* Item folder */ + atci = g_new0(AddressTypeControlItem, 1); + atci->objectType = ADDR_ITEM_FOLDER; + atci->interfaceType = ADDR_IF_BOOK; + atci->showInTree = TRUE; + atci->treeExpand = FALSE; + atci->treeLeaf = FALSE; + atci->displayName = _("Folder"); + atci->icon_pixbuf = folderpix; + atci->icon_open_pixbuf = folderopenpix; + atci->menuCommand = NULL; + g_hash_table_insert(_addressBookTypeHash_, &atci->objectType, atci); + _addressBookTypeList_ = g_list_append(_addressBookTypeList_, atci); + + /* vCard */ + atci = g_new0(AddressTypeControlItem, 1); + atci->objectType = ADDR_VCARD; + atci->interfaceType = ADDR_IF_VCARD; + atci->showInTree = TRUE; + atci->treeExpand = TRUE; + atci->treeLeaf = TRUE; + atci->displayName = _("vCard"); + atci->icon_pixbuf = vcardpix; + atci->icon_open_pixbuf = vcardpix; + atci->menuCommand = "/File/New vCard"; + g_hash_table_insert(_addressBookTypeHash_, &atci->objectType, atci); + _addressBookTypeList_ = g_list_append(_addressBookTypeList_, atci); + + /* JPilot */ + atci = g_new0(AddressTypeControlItem, 1); + atci->objectType = ADDR_JPILOT; + atci->interfaceType = ADDR_IF_JPILOT; + atci->showInTree = TRUE; + atci->treeExpand = TRUE; + atci->treeLeaf = FALSE; + atci->displayName = _("JPilot"); + atci->icon_pixbuf = jpilotpix; + atci->icon_open_pixbuf = jpilotpix; + atci->menuCommand = "/File/New JPilot"; + g_hash_table_insert(_addressBookTypeHash_, &atci->objectType, atci); + _addressBookTypeList_ = g_list_append(_addressBookTypeList_, atci); + + /* Category */ + atci = g_new0(AddressTypeControlItem, 1); + atci->objectType = ADDR_CATEGORY; + atci->interfaceType = ADDR_IF_JPILOT; + atci->showInTree = TRUE; + atci->treeExpand = TRUE; + atci->treeLeaf = TRUE; + atci->displayName = _("JPilot"); + atci->icon_pixbuf = categorypix; + atci->icon_open_pixbuf = categorypix; + atci->menuCommand = NULL; + g_hash_table_insert(_addressBookTypeHash_, &atci->objectType, atci); + _addressBookTypeList_ = g_list_append(_addressBookTypeList_, atci); + + /* LDAP Server */ + atci = g_new0(AddressTypeControlItem, 1); + atci->objectType = ADDR_LDAP; + atci->interfaceType = ADDR_IF_LDAP; + atci->showInTree = TRUE; + atci->treeExpand = TRUE; + atci->treeLeaf = TRUE; + atci->displayName = _("LDAP Server"); + atci->icon_pixbuf = ldappix; + atci->icon_open_pixbuf = ldappix; + atci->menuCommand = "/File/New LDAP Server"; + g_hash_table_insert(_addressBookTypeHash_, &atci->objectType, atci); + _addressBookTypeList_ = g_list_append(_addressBookTypeList_, atci); +} + +/* +* Search for specified object type. +*/ +AddressTypeControlItem *addrbookctl_lookup(gint ot) +{ + gint objType = ot; + return (AddressTypeControlItem *)g_hash_table_lookup(_addressBookTypeHash_, &objType); +} + +/* +* Search for specified interface type. +*/ +AddressTypeControlItem *addrbookctl_lookup_iface( AddressIfType ifType ) { + GList *node = _addressBookTypeList_; + while( node ) { + AddressTypeControlItem *atci = node->data; + if( atci->interfaceType == ifType ) return atci; + node = g_list_next( node ); + } + return NULL; +} + +static void addrbookctl_free_address( AddressObject *obj ) { + g_free( obj->name ); + obj->type = ADDR_NONE; + obj->name = NULL; +} + +static void addrbookctl_free_interface(AdapterInterface *adapter) +{ + addrbookctl_free_address(ADDRESS_OBJECT(adapter)); + adapter->iface = NULL; + adapter->interfaceType = ADDR_IF_NONE; + adapter->atci = NULL; + adapter->enabled = FALSE; + adapter->haveLibrary = FALSE; + if (adapter->tree_row) { + gtk_tree_row_reference_free(adapter->tree_row); + adapter->tree_row = NULL; + } + g_free(adapter); +} + +static void addrbookctl_free_datasource( AdapterDSource *adapter ) { + addrbookctl_free_address( ADDRESS_OBJECT(adapter) ); + adapter->dataSource = NULL; + adapter->subType = ADDR_NONE; + g_free( adapter ); +} + +static void addrbookctl_free_folder( AdapterFolder *adapter ) { + addrbookctl_free_address( ADDRESS_OBJECT(adapter) ); + adapter->itemFolder = NULL; + g_free( adapter ); +} + +static void addrbookctl_free_group( AdapterGroup *adapter ) { + addrbookctl_free_address( ADDRESS_OBJECT(adapter) ); + adapter->itemGroup = NULL; + g_free( adapter ); +} + +/* + * Build GUI interface list. + */ +void addrbookctl_build_iflist(void) +{ + AddressTypeControlItem *atci; + AdapterInterface *adapter; + GList *list = NULL; + + if(_addressIndex_ == NULL) { + _addressIndex_ = addrindex_create_index(); + } + _addressInterfaceList_ = NULL; + list = addrindex_get_interface_list(_addressIndex_); + while (list) { + AddressInterface *iface = list->data; + atci = addrbookctl_lookup_iface(iface->type); + if (atci) { + adapter = g_new0(AdapterInterface, 1); + adapter->interfaceType = iface->type; + adapter->atci = atci; + adapter->iface = iface; + adapter->tree_row = NULL; + adapter->enabled = TRUE; + adapter->haveLibrary = iface->haveLibrary; + ADDRESS_OBJECT(adapter)->type = ADDR_INTERFACE; + ADDRESS_OBJECT_NAME(adapter) = g_strdup(atci->displayName); + _addressInterfaceList_ = g_list_append(_addressInterfaceList_, adapter); + } + list = g_list_next(list); + } +} + +void addrbookctl_free_selection( GList *list ) { + GList *node = list; + while( node ) { + AdapterInterface *adapter = node->data; + adapter = NULL; + node = g_list_next( node ); + } + g_list_free( list ); +} + +/* +* Find GUI interface type specified interface type. +* Return: Interface item, or NULL if not found. +*/ +AdapterInterface *addrbookctl_find_interface( AddressIfType ifType ) { + GList *node = _addressInterfaceList_; + while( node ) { + AdapterInterface *adapter = node->data; + if( adapter->interfaceType == ifType ) return adapter; + node = g_list_next( node ); + } + return NULL; +} + +/* +* Build interface list selection. +*/ +void addrbookctl_build_ifselect(void) +{ + GList *newList = NULL; + gchar *selectStr; + gchar **splitStr; + gint ifType; + gint i; + gchar *endptr = NULL; + gboolean enabled; + AdapterInterface *adapter; + /* GList *node; */ + + selectStr = g_strdup( ADDRESSBOOK_IFACE_SELECTION ); + + /* Parse string */ + splitStr = g_strsplit( selectStr, ",", -1 ); + for( i = 0; i < ADDRESSBOOK_MAX_IFACE; i++ ) { + if( splitStr[i] ) { + /* printf( "%d : %s\n", i, splitStr[i] ); */ + ifType = strtol( splitStr[i], &endptr, 10 ); + enabled = TRUE; + if( *endptr ) { + if( strcmp( endptr, "/n" ) == 0 ) { + enabled = FALSE; + } + } + /* printf( "\t%d : %s\n", ifType, enabled ? "yes" : "no" ); */ + adapter = addrbookctl_find_interface( ifType ); + if( adapter ) { + newList = g_list_append( newList, adapter ); + } + } + else { + break; + } + } + /* printf( "i=%d\n", i ); */ + g_strfreev( splitStr ); + g_free( selectStr ); + + /* Replace existing list */ + mgu_clear_list( _addressIFaceSelection_ ); + g_list_free( _addressIFaceSelection_ ); + _addressIFaceSelection_ = newList; + newList = NULL; + +} + +/* ********************************************************************** +* Add sender to address book. +* *********************************************************************** +*/ + +/* + * This function is used by the Add sender to address book function. + */ +gboolean addressbook_add_contact(const gchar *name, const gchar *address, const gchar *remarks) +{ + debug_print("addressbook_add_contact: name/address: %s <%s>\n", name ? name : "", address); + if (addressadd_selection(_addressIndex_, name, address, remarks)) { + debug_print("addressbook_add_contact - added\n"); + addressbook_refresh(); + } + return TRUE; +} + +/* + * This function is used by the automatic address registration. + */ +gboolean addressbook_add_contact_autoreg(const gchar *name, const gchar *address, const gchar *remarks) +{ + debug_print("addressbook_add_contact_autoreg: name/address: %s <%s>\n", name ? name : "", address); + if (addressadd_autoreg(_addressIndex_, name, address, remarks)) { + addressbook_refresh(); + } + return TRUE; +} + +/* ********************************************************************** +* Address completion support. +* *********************************************************************** +*/ + +/* +* This function is used by the address completion function to load +* addresses. +* Enter: callBackFunc Function to be called when an address is +* to be loaded. +* Return: TRUE if data loaded, FALSE if address index not loaded. +*/ +gboolean addressbook_load_completion_full(AddressBookCompletionFunc func) +{ + /* AddressInterface *interface; */ + AddressDataSource *ds; + GList *nodeIf, *nodeDS; + GList *listP, *nodeP; + GList *nodeM; + gchar *sName, *sAddress, *sAlias, *sNickName; + gchar *sFirstName, *sLastName; + + debug_print( "addressbook_load_completion\n" ); + + if( _addressIndex_ == NULL ) return FALSE; + + nodeIf = addrindex_get_interface_list( _addressIndex_ ); + while( nodeIf ) { + AddressInterface *iface = nodeIf->data; + nodeDS = iface->listSource; + while( nodeDS ) { + ds = nodeDS->data; + + /* Read address book */ + if( ! addrindex_ds_get_read_flag( ds ) ) { + addrindex_ds_read_data( ds ); + } + + /* Get all persons */ + listP = addrindex_ds_get_all_persons( ds ); + nodeP = listP; + while( nodeP ) { + ItemPerson *person = nodeP->data; + nodeM = person->listEMail; + + /* Figure out name to use */ + sName = ADDRITEM_NAME(person); + sNickName = person->nickName; + if( sName == NULL || *sName == '\0' ) { + if (sNickName) + sName = sNickName; + } + sFirstName = person->firstName; + sLastName = person->lastName; + + /* Process each E-Mail address */ + while( nodeM ) { + ItemEMail *email = nodeM->data; + /* Have mail */ + sAddress = email->address; + if( sAddress && *sAddress != '\0' ) { + sAlias = ADDRITEM_NAME(email); + if( sAlias && *sAlias != '\0' ) { + func( sName, sFirstName, sLastName, sAlias, sAddress ); + } else { + func( sName, sFirstName, sLastName, sNickName, sAddress ); + } + } + nodeM = g_list_next( nodeM ); + } + nodeP = g_list_next( nodeP ); + } + /* Free up the list */ + g_list_free( listP ); + + nodeDS = g_list_next( nodeDS ); + } + nodeIf = g_list_next( nodeIf ); + } + debug_print( "addressbook_load_completion... done\n" ); + + return TRUE; +} + +static gint (*real_func)(const gchar *, const gchar *, const gchar *); + +static gint wrapper_func(const gchar *name, const gchar *firstname, const gchar *lastname, const gchar *nickname, const gchar *address) +{ + return real_func(name, address, nickname); +} + +gboolean addressbook_load_completion(gint (*callBackFunc)(const gchar *, const gchar *, const gchar *)) +{ + gboolean ret; + + real_func = callBackFunc; + ret = addressbook_load_completion_full(wrapper_func); + real_func = NULL; + + return ret; +} + +/* ********************************************************************** +* Address Import. +* *********************************************************************** +*/ + +/* +* Import LDIF file. +*/ +static void addressbook_import_ldif_cb(void) +{ + AddressDataSource *ds = NULL; + AdapterDSource *ads = NULL; + AddressBookFile *abf = NULL; + AdapterInterface *adapter; + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeModel *model; + GtkTreeIter iter, new_iter; + GtkTreePath *path; + + adapter = addrbookctl_find_interface(ADDR_IF_BOOK); + if (!adapter || !adapter->tree_row) + return; + + abf = addressbook_imp_ldif(_addressIndex_); + gtk_window_present(GTK_WINDOW(addrbook.window)); + if (!abf) + return; + + ds = addrindex_index_add_datasource(_addressIndex_, ADDR_IF_BOOK, abf); + ads = addressbook_create_ds_adapter(ds, ADDR_BOOK, NULL); + addressbook_ads_set_name(ads, abf->name); + + model = gtk_tree_view_get_model(treeview); + gtkut_tree_row_reference_get_iter(model, adapter->tree_row, &iter); + + if (addressbook_add_object(&iter, &new_iter, ADDRESS_OBJECT(ads))) { + path = gtk_tree_model_get_path(model, &new_iter); + gtk_tree_view_set_cursor(treeview, path, NULL, FALSE); + gtk_tree_path_free(path); + } + + /* Notify address completion */ + addressbook_modified(); +} + +gboolean addressbook_import_ldif_file(const gchar *file, const gchar *book_name) +{ + AddressBookFile *abf; + + g_return_val_if_fail(file != NULL, FALSE); + g_return_val_if_fail(book_name != NULL, FALSE); + + abf = addressbook_imp_ldif_file(_addressIndex_, file, book_name); + if (!abf) + return FALSE; + + addrindex_index_add_datasource(_addressIndex_, ADDR_IF_BOOK, abf); + addrindex_save_data(_addressIndex_); + addressbook_modified(); + + return TRUE; +} + +/* +* Import CSV file. +*/ +static void addressbook_import_csv_cb(void) +{ + AddressDataSource *ds = NULL; + AdapterDSource *ads = NULL; + AddressBookFile *abf = NULL; + AdapterInterface *adapter; + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeModel *model; + GtkTreeIter iter, new_iter; + GtkTreePath *path; + + adapter = addrbookctl_find_interface(ADDR_IF_BOOK); + if (!adapter || !adapter->tree_row) + return; + + abf = addressbook_imp_csv(_addressIndex_); + gtk_window_present(GTK_WINDOW(addrbook.window)); + if (!abf) + return; + + ds = addrindex_index_add_datasource(_addressIndex_, ADDR_IF_BOOK, abf); + ads = addressbook_create_ds_adapter(ds, ADDR_BOOK, NULL); + addressbook_ads_set_name(ads, abf->name); + + model = gtk_tree_view_get_model(treeview); + gtkut_tree_row_reference_get_iter(model, adapter->tree_row, &iter); + + if (addressbook_add_object(&iter, &new_iter, ADDRESS_OBJECT(ads))) { + path = gtk_tree_model_get_path(model, &new_iter); + gtk_tree_view_set_cursor(treeview, path, NULL, FALSE); + gtk_tree_path_free(path); + } + + /* Notify address completion */ + addressbook_modified(); +} + +/* +* Export CSV file. +*/ +static void addressbook_export_csv_cb(void) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(addrbook.treeview); + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path; + AddressObject *obj; + ItemFolder *itemFolder = NULL; + ItemGroup *itemGroup = NULL; + GList *items; + GList *cur; + gchar delim = ','; + + if (!addrbook.tree_opened) { + return; + } + + model = gtk_tree_view_get_model(treeview); + gtkut_tree_row_reference_get_iter(model, addrbook.tree_opened, &iter); + gtk_tree_model_get(model, &iter, COL_OBJ, &obj, -1); + g_return_if_fail(obj != NULL); + + if (obj->type == ADDR_INTERFACE) { + return; + } + + addressbook_exp_csv(obj); +} + +/* ********************************************************************** +* Address Book Fast Search. +* *********************************************************************** +*/ + +static GHashTable *addr_table; + +#if USE_THREADS +G_LOCK_DEFINE_STATIC(addr_table); +#define S_LOCK(name) G_LOCK(name) +#define S_UNLOCK(name) G_UNLOCK(name) +#else +#define S_LOCK(name) +#define S_UNLOCK(name) +#endif + +static gint load_address(const gchar *name, const gchar *address, + const gchar *nickname) +{ + gchar *addr; + + if (!address) + return -1; + + addr = g_ascii_strdown(address, -1); + + if (g_hash_table_lookup(addr_table, addr) == NULL) + g_hash_table_insert(addr_table, addr, addr); + else + g_free(addr); + + return 0; +} + +static void addressbook_modified(void) +{ + S_LOCK(addr_table); + + if (addr_table) { + hash_free_strings(addr_table); + g_hash_table_destroy(addr_table); + addr_table = NULL; + } + + S_UNLOCK(addr_table); + + invalidate_address_completion(); +} + +gboolean addressbook_has_address(const gchar *address) +{ + GSList *list, *cur; + gchar *addr; + gboolean found = FALSE; + + if (!address) + return FALSE; + + /* debug_print("addressbook_has_address: check if addressbook has address: %s\n", address); */ + + list = address_list_append(NULL, address); + if (!list) + return FALSE; + + S_LOCK(addr_table); + + if (!addr_table) { + addr_table = g_hash_table_new(g_str_hash, g_str_equal); + addressbook_load_completion(load_address); + } + + for (cur = list; cur != NULL; cur = cur->next) { + addr = g_ascii_strdown((gchar *)cur->data, -1); + + if (g_hash_table_lookup(addr_table, addr)) { + found = TRUE; + /* debug_print("<%s> is in addressbook\n", addr); */ + } else { + found = FALSE; + g_free(addr); + break; + } + g_free(addr); + } + + S_UNLOCK(addr_table); + + slist_free_strings(list); + + return found; +} + +/* +* End of Source. +*/ diff --git a/src/addressbook.h b/src/addressbook.h new file mode 100644 index 0000000..ee0b744 --- /dev/null +++ b/src/addressbook.h @@ -0,0 +1,61 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2012 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __ADDRESSBOOK_H__ +#define __ADDRESSBOOK_H__ + +#include +#include + +#include "compose.h" + +typedef gint (*AddressBookCompletionFunc) (const gchar *name, const gchar *firstname, const gchar *lastname, const gchar *nickname, const gchar *address); + +void addressbook_open (Compose *target); +void addressbook_set_target_compose (Compose *target); +Compose *addressbook_get_target_compose (void); +void addressbook_read_file (void); +void addressbook_export_to_file (void); +gint addressbook_obj_name_compare (gconstpointer a, + gconstpointer b); +/* static gint addressbook_obj_name_compare(gconstpointer a, + gconstpointer b); */ + +/* provisional API for accessing the address book */ + +void addressbook_access (void); +void addressbook_unaccess (void); + +gboolean addressbook_add_contact (const gchar *name, + const gchar *address, + const gchar *remarks); +gboolean addressbook_add_contact_autoreg(const gchar *name, + const gchar *address, + const gchar *remarks); + +gboolean addressbook_load_completion_full + (AddressBookCompletionFunc func); +gboolean addressbook_load_completion ( gint (*callBackFunc) ( const gchar *, const gchar *, const gchar * ) ); + +gboolean addressbook_has_address (const gchar *address); + +gboolean addressbook_import_ldif_file (const gchar *file, + const gchar *book_name); + +#endif /* __ADDRESSBOOK_H__ */ diff --git a/src/addressitem.h b/src/addressitem.h new file mode 100644 index 0000000..8da6614 --- /dev/null +++ b/src/addressitem.h @@ -0,0 +1,157 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999,2000 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Address item data. Shared among GUI components only. + */ + +#ifndef __ADDRESSITEM_H__ +#define __ADDRESSITEM_H__ + +#include +#include +#include + +#include "compose.h" +#include "addrindex.h" + +#define ADDRESS_OBJECT(obj) ((AddressObject *)obj) +#define ADDRESS_OBJECT_TYPE(obj) (ADDRESS_OBJECT(obj)->type) +#define ADDRESS_OBJECT_NAME(obj) (ADDRESS_OBJECT(obj)->name) + +#define ADAPTER_INTERFACE(obj) ((AdapterInterface *)obj) +#define ADAPTER_FOLDER(obj) ((AdapterFolder *)obj) +#define ADAPTER_GROUP(obj) ((AdapterGroup *)obj) +#define ADAPTER_DSOURCE(obj) ((AdapterDSource *)obj) + +typedef enum { + ADDR_NONE, + ADDR_ITEM_PERSON, + ADDR_ITEM_EMAIL, + ADDR_ITEM_FOLDER, + ADDR_ITEM_GROUP, + ADDR_INTERFACE, + ADDR_DATASOURCE, + ADDR_BOOK, /* Sub-type */ + ADDR_VCARD, /* Sub-type */ + ADDR_JPILOT, /* Sub-type */ + ADDR_CATEGORY, /* Sub-type */ + ADDR_LDAP /* Sub-type */ +} AddressObjectType; + +typedef struct _AddressBook_win AddressBook_win; +struct _AddressBook_win +{ + GtkWidget *window; + GtkWidget *menubar; + GtkWidget *treeview; + GtkWidget *listview; + GtkWidget *entry; + GtkWidget *statusbar; + + GtkWidget *to_btn; + GtkWidget *cc_btn; + GtkWidget *bcc_btn; + GtkWidget *del_btn; + GtkWidget *reg_btn; + GtkWidget *lup_btn; + GtkWidget *close_btn; + + GtkWidget *tree_popup; + GtkWidget *list_popup; + GtkItemFactory *tree_factory; + GtkItemFactory *list_factory; + GtkItemFactory *menu_factory; + + GtkTreeRowReference *tree_selected; + GtkTreeRowReference *tree_opened; + GtkTreeRowReference *list_selected; + + Compose *target_compose; + gint status_cid; +}; + +typedef struct _AddressTypeControlItem AddressTypeControlItem; +struct _AddressTypeControlItem { + AddressObjectType objectType; + AddressIfType interfaceType; + gchar *displayName; + gboolean showInTree; + gboolean treeExpand; + gboolean treeLeaf; + gchar *menuCommand; + GdkPixbuf *icon_pixbuf; + GdkPixbuf *icon_open_pixbuf; +}; + +typedef struct _AddressObject AddressObject; +struct _AddressObject { + AddressObjectType type; + gchar *name; +}; + +typedef struct _AdapterInterface AdapterInterface; +struct _AdapterInterface { + AddressObject obj; + AddressInterface *iface; + AddressIfType interfaceType; + AddressTypeControlItem *atci; + gboolean enabled; + gboolean haveLibrary; + GtkTreeRowReference *tree_row; +}; + +typedef struct _AdapterDSource AdapterDSource; +struct _AdapterDSource { + AddressObject obj; + AddressDataSource *dataSource; + AddressObjectType subType; +}; + +typedef struct _AdapterFolder AdapterFolder; +struct _AdapterFolder { + AddressObject obj; + ItemFolder *itemFolder; +}; + +typedef struct _AdapterGroup AdapterGroup; +struct _AdapterGroup { + AddressObject obj; + ItemGroup *itemGroup; +}; + +typedef struct _AddressFileSelection AddressFileSelection; +struct _AddressFileSelection { + GtkWidget *fileSelector; + gboolean cancelled; +}; + +AdapterDSource *addressbook_create_ds_adapter ( AddressDataSource *ds, + AddressObjectType otype, + gchar *name ); + +void addressbook_ads_set_name ( AdapterDSource *adapter, + gchar *value ); + +#endif /* __ADDRESSITEM_H__ */ + +/* +* End of Source. +*/ + diff --git a/src/addrindex.c b/src/addrindex.c new file mode 100644 index 0000000..efe0b3f --- /dev/null +++ b/src/addrindex.c @@ -0,0 +1,1925 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * General functions for accessing address index file. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include + +#include "mgutils.h" +#include "addritem.h" +#include "addrcache.h" +#include "addrbook.h" +#include "addrindex.h" +#include "xml.h" +#include "utils.h" + +#ifndef DEV_STANDALONE +#include "prefs.h" +#include "codeconv.h" +#endif + +#include "vcard.h" + +#ifdef USE_JPILOT +#include "jpilot.h" +#endif + +#ifdef USE_LDAP +#include "syldap.h" +#endif + +#define TAG_ADDRESS_INDEX "addressbook" + +#define TAG_IF_ADDRESS_BOOK "book_list" +#define TAG_IF_VCARD "vcard_list" +#define TAG_IF_JPILOT "jpilot_list" +#define TAG_IF_LDAP "ldap_list" + +#define TAG_DS_ADDRESS_BOOK "book" +#define TAG_DS_VCARD "vcard" +#define TAG_DS_JPILOT "jpilot" +#define TAG_DS_LDAP "server" + +/* XML Attribute names */ +#define ATTAG_BOOK_NAME "name" +#define ATTAG_BOOK_FILE "file" + +#define ATTAG_VCARD_NAME "name" +#define ATTAG_VCARD_FILE "file" + +#define ATTAG_JPILOT_NAME "name" +#define ATTAG_JPILOT_FILE "file" +#define ATTAG_JPILOT_CUSTOM_1 "custom-1" +#define ATTAG_JPILOT_CUSTOM_2 "custom-2" +#define ATTAG_JPILOT_CUSTOM_3 "custom-3" +#define ATTAG_JPILOT_CUSTOM_4 "custom-4" +#define ATTAG_JPILOT_CUSTOM "custom-" + +#define ATTAG_LDAP_NAME "name" +#define ATTAG_LDAP_HOST "host" +#define ATTAG_LDAP_PORT "port" +#define ATTAG_LDAP_BASE_DN "base-dn" +#define ATTAG_LDAP_BIND_DN "bind-dn" +#define ATTAG_LDAP_BIND_PASS "bind-pass" +#define ATTAG_LDAP_CRITERIA "criteria" +#define ATTAG_LDAP_MAX_ENTRY "max-entry" +#define ATTAG_LDAP_TIMEOUT "timeout" + +#if 0 +N_("Common address") +N_("Personal address") +#endif + +#define DISP_NEW_COMMON _("Common address") +#define DISP_NEW_PERSONAL _("Personal address") + +/* Old address book */ +#define TAG_IF_OLD_COMMON "common_address" +#define TAG_IF_OLD_PERSONAL "personal_address" + +#define DISP_OLD_COMMON _("Common address") +#define DISP_OLD_PERSONAL _("Personal address") + +typedef struct _AddressIfAttr AddressIfAttrib; +struct _AddressIfAttr { + gchar *name; + gchar *value; +}; + +/* +* Build interface with default values. +*/ +static AddressInterface *addrindex_create_interface( gint type, gchar *name, gchar *tagIf, gchar *tagDS ) { + AddressInterface *iface = g_new0( AddressInterface, 1 ); + ADDRITEM_TYPE(iface) = ITEMTYPE_INTERFACE; + ADDRITEM_ID(iface) = NULL; + ADDRITEM_NAME(iface) = g_strdup( name ); + ADDRITEM_PARENT(iface) = NULL; + ADDRITEM_SUBTYPE(iface) = type; + iface->type = type; + iface->name = g_strdup( name ); + iface->listTag = g_strdup( tagIf ); + iface->itemTag = g_strdup( tagDS ); + iface->legacyFlag = FALSE; + iface->haveLibrary = TRUE; + iface->useInterface = TRUE; + iface->readOnly = TRUE; + iface->getAccessFlag = NULL; + iface->getModifyFlag = NULL; + iface->getReadFlag = NULL; + iface->getStatusCode = NULL; + iface->getReadData = NULL; + iface->getRootFolder = NULL; + iface->getListFolder = NULL; + iface->getListPerson = NULL; + iface->getAllPersons = NULL; + iface->getAllGroups = NULL; + iface->getName = NULL; + iface->listSource = NULL; + return iface; +} + +/* +* Build table of interfaces. +*/ +static void addrindex_build_if_list( AddressIndex *addrIndex ) { + AddressInterface *iface; + + iface = addrindex_create_interface( ADDR_IF_BOOK, "Address Book", TAG_IF_ADDRESS_BOOK, TAG_DS_ADDRESS_BOOK ); + iface->readOnly = FALSE; + iface->getModifyFlag = ( void * ) addrbook_get_modified; + iface->getAccessFlag = ( void * ) addrbook_get_accessed; + iface->getReadFlag = ( void * ) addrbook_get_read_flag; + iface->getStatusCode = ( void * ) addrbook_get_status; + iface->getReadData = ( void * ) addrbook_read_data; + iface->getRootFolder = ( void * ) addrbook_get_root_folder; + iface->getListFolder = ( void * ) addrbook_get_list_folder; + iface->getListPerson = ( void * ) addrbook_get_list_person; + iface->getAllPersons = ( void * ) addrbook_get_all_persons; + iface->getName = ( void * ) addrbook_get_name; + iface->setAccessFlag = ( void * ) addrbook_set_accessed; + addrIndex->interfaceList = g_list_append( addrIndex->interfaceList, iface ); + ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex); + + iface = addrindex_create_interface( ADDR_IF_VCARD, "vCard", TAG_IF_VCARD, TAG_DS_VCARD ); + iface->getModifyFlag = ( void * ) vcard_get_modified; + iface->getAccessFlag = ( void * ) vcard_get_accessed; + iface->getReadFlag = ( void * ) vcard_get_read_flag; + iface->getStatusCode = ( void * ) vcard_get_status; + iface->getReadData = ( void * ) vcard_read_data; + iface->getRootFolder = ( void * ) vcard_get_root_folder; + iface->getListFolder = ( void * ) vcard_get_list_folder; + iface->getListPerson = ( void * ) vcard_get_list_person; + iface->getAllPersons = ( void * ) vcard_get_all_persons; + iface->getName = ( void * ) vcard_get_name; + iface->setAccessFlag = ( void * ) vcard_set_accessed; + addrIndex->interfaceList = g_list_append( addrIndex->interfaceList, iface ); + ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex); + + iface = addrindex_create_interface( ADDR_IF_JPILOT, "JPilot", TAG_IF_JPILOT, TAG_DS_JPILOT ); +#ifdef USE_JPILOT + /* iface->haveLibrary = jpilot_test_pilot_lib(); */ + iface->haveLibrary = TRUE; + iface->useInterface = iface->haveLibrary; + iface->getModifyFlag = ( void * ) jpilot_get_modified; + iface->getAccessFlag = ( void * ) jpilot_get_accessed; + iface->getReadFlag = ( void * ) jpilot_get_read_flag; + iface->getStatusCode = ( void * ) jpilot_get_status; + iface->getReadData = ( void * ) jpilot_read_data; + iface->getRootFolder = ( void * ) jpilot_get_root_folder; + iface->getListFolder = ( void * ) jpilot_get_list_folder; + iface->getListPerson = ( void * ) jpilot_get_list_person; + iface->getAllPersons = ( void * ) jpilot_get_all_persons; + iface->getName = ( void * ) jpilot_get_name; + iface->setAccessFlag = ( void * ) jpilot_set_accessed; +#else + iface->useInterface = FALSE; + iface->haveLibrary = FALSE; +#endif + addrIndex->interfaceList = g_list_append( addrIndex->interfaceList, iface ); + ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex); + + iface = addrindex_create_interface( ADDR_IF_LDAP, "LDAP", TAG_IF_LDAP, TAG_DS_LDAP ); +#ifdef USE_LDAP + /* iface->haveLibrary = syldap_test_ldap_lib(); */ + iface->haveLibrary = TRUE; + iface->useInterface = iface->haveLibrary; + iface->getAccessFlag = ( void * ) syldap_get_accessed; + /* iface->getModifyFlag = ( void * ) syldap_get_modified; */ + /* iface->getReadFlag = ( void * ) syldap_get_read_flag; */ + iface->getStatusCode = ( void * ) syldap_get_status; + iface->getReadData = ( void * ) syldap_read_data; + iface->getRootFolder = ( void * ) syldap_get_root_folder; + iface->getListFolder = ( void * ) syldap_get_list_folder; + iface->getListPerson = ( void * ) syldap_get_list_person; + iface->getName = ( void * ) syldap_get_name; + iface->setAccessFlag = ( void * ) syldap_set_accessed; +#else + iface->useInterface = FALSE; + iface->haveLibrary = FALSE; +#endif + addrIndex->interfaceList = g_list_append( addrIndex->interfaceList, iface ); + ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex); + + /* Two old legacy data sources */ + iface = addrindex_create_interface( ADDR_IF_COMMON, "Old Address - common", TAG_IF_OLD_COMMON, NULL ); + iface->legacyFlag = TRUE; + addrIndex->interfaceList = g_list_append( addrIndex->interfaceList, iface ); + ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex); + + iface = addrindex_create_interface( ADDR_IF_COMMON, "Old Address - personal", TAG_IF_OLD_PERSONAL, NULL ); + iface->legacyFlag = TRUE; + addrIndex->interfaceList = g_list_append( addrIndex->interfaceList, iface ); + ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex); + +} + +/* +* Free name-value pairs. +*/ +static void addrindex_free_attributes( GList *list ) { + GList *node = list; + while( node ) { + AddressIfAttrib *nv = node->data; + g_free( nv->name ); nv->name = NULL; + g_free( nv->value ); nv->value = NULL; + g_free( nv ); + node->data = NULL; + node = g_list_next( node ); + } + g_list_free( list ); +} + +/* +* Free up data source. +*/ +void addrindex_free_datasource( AddressIndex *addrIndex, AddressDataSource *ds ) { + AddressInterface *iface = NULL; + g_return_if_fail( addrIndex != NULL ); + g_return_if_fail( ds != NULL ); + + if( ds->iface == NULL ) { + iface = addrindex_get_interface( addrIndex, ds->type ); + } + if( iface == NULL ) return; + + if( iface->useInterface ) { + if( iface->type == ADDR_IF_BOOK ) { + AddressBookFile *abf = ds->rawDataSource; + if( abf ) { + addrbook_free_book( abf ); + } + } + else if( iface->type == ADDR_IF_VCARD ) { + VCardFile *vcf = ds->rawDataSource; + if( vcf ) { + vcard_free( vcf ); + } + } +#ifdef USE_JPILOT + else if( iface->type == ADDR_IF_JPILOT ) { + JPilotFile *jpf = ds->rawDataSource; + if( jpf ) { + jpilot_free( jpf ); + } + } +#endif +#ifdef USE_LDAP + else if( iface->type == ADDR_IF_LDAP ) { + SyldapServer *server = ds->rawDataSource; + if( server ) { + syldap_free( server ); + } + } +#endif + } + else { + GList *list = ds->rawDataSource; + addrindex_free_attributes( list ); + } + + g_free( ADDRITEM_ID(addrIndex) ); + g_free( ADDRITEM_NAME(addrIndex) ); + + ADDRITEM_TYPE(addrIndex) = ITEMTYPE_NONE; + ADDRITEM_ID(addrIndex) = NULL; + ADDRITEM_NAME(addrIndex) = NULL; + ADDRITEM_PARENT(addrIndex) = NULL; + ADDRITEM_SUBTYPE(addrIndex) = 0; + ds->type = ADDR_IF_NONE; + ds->rawDataSource = NULL; + ds->iface = NULL; + + ds->type = ADDR_IF_NONE; + ds->rawDataSource = NULL; + ds->iface = NULL; + g_free( ds ); +} + +static void addrindex_free_all_datasources( AddressInterface *iface ) { + GList *node = iface->listSource; + while( node ) { + AddressDataSource *ds = node->data; + if( iface->useInterface ) { + if( iface->type == ADDR_IF_BOOK ) { + AddressBookFile *abf = ds->rawDataSource; + if( abf ) { + addrbook_free_book( abf ); + } + } + else if( iface->type == ADDR_IF_VCARD ) { + VCardFile *vcf = ds->rawDataSource; + if( vcf ) { + vcard_free( vcf ); + } + } +#ifdef USE_JPILOT + else if( iface->type == ADDR_IF_JPILOT ) { + JPilotFile *jpf = ds->rawDataSource; + if( jpf ) { + jpilot_free( jpf ); + } + } +#endif +#ifdef USE_LDAP + else if( iface->type == ADDR_IF_LDAP ) { + SyldapServer *server = ds->rawDataSource; + if( server ) { + syldap_free( server ); + } + } +#endif + } + else { + GList *list = ds->rawDataSource; + addrindex_free_attributes( list ); + } + + ds->type = ADDR_IF_NONE; + ds->rawDataSource = NULL; + ds->iface = NULL; + g_free( ds ); + node->data = NULL; + node = g_list_next( node ); + } +} + +static void addrindex_free_interface( AddressInterface *iface ) { + addrindex_free_all_datasources( iface ); + + g_free( ADDRITEM_ID(iface) ); + g_free( ADDRITEM_NAME(iface) ); + g_free( iface->name ); + g_free( iface->listTag ); + g_free( iface->itemTag ); + + ADDRITEM_TYPE(iface) = ITEMTYPE_NONE; + ADDRITEM_ID(iface) = NULL; + ADDRITEM_NAME(iface) = NULL; + ADDRITEM_PARENT(iface) = NULL; + ADDRITEM_SUBTYPE(iface) = 0; + iface->type = ADDR_IF_NONE; + iface->name = NULL; + iface->listTag = NULL; + iface->itemTag = NULL; + iface->legacyFlag = FALSE; + iface->useInterface = FALSE; + iface->haveLibrary = FALSE; + + g_list_free( iface->listSource ); + iface->listSource = NULL; +} + +/* +* Create new object. +*/ +AddressIndex *addrindex_create_index() { + AddressIndex *addrIndex = g_new0( AddressIndex, 1 ); + + ADDRITEM_TYPE(addrIndex) = ITEMTYPE_INDEX; + ADDRITEM_ID(addrIndex) = NULL; + ADDRITEM_NAME(addrIndex) = g_strdup( "Address Index" ); + ADDRITEM_PARENT(addrIndex) = NULL; + ADDRITEM_SUBTYPE(addrIndex) = 0; + addrIndex->filePath = NULL; + addrIndex->fileName = NULL; + addrIndex->retVal = MGU_SUCCESS; + addrIndex->needsConversion = FALSE; + addrIndex->wasConverted = FALSE; + addrIndex->conversionError = FALSE; + addrIndex->interfaceList = NULL; + addrIndex->lastType = ADDR_IF_NONE; + addrIndex->dirtyFlag = FALSE; + addrindex_build_if_list( addrIndex ); + return addrIndex; +} + +/* +* Specify file to be used. +*/ +void addrindex_set_file_path( AddressIndex *addrIndex, const gchar *value ) { + g_return_if_fail( addrIndex != NULL ); + addrIndex->filePath = mgu_replace_string( addrIndex->filePath, value ); +} +void addrindex_set_file_name( AddressIndex *addrIndex, const gchar *value ) { + g_return_if_fail( addrIndex != NULL ); + addrIndex->fileName = mgu_replace_string( addrIndex->fileName, value ); +} +void addrindex_set_dirty( AddressIndex *addrIndex, const gboolean value ) { + g_return_if_fail( addrIndex != NULL ); + addrIndex->dirtyFlag = value; +} + +/* +* Return list of interfaces. +*/ +GList *addrindex_get_interface_list( AddressIndex *addrIndex ) { + g_return_val_if_fail( addrIndex != NULL, NULL ); + return addrIndex->interfaceList; +} + +/* +* Free up object. +*/ +void addrindex_free_index( AddressIndex *addrIndex ) { + GList *node; + + g_return_if_fail( addrIndex != NULL ); + + g_free( ADDRITEM_ID(addrIndex) ); + g_free( ADDRITEM_NAME(addrIndex) ); + g_free( addrIndex->filePath ); + g_free( addrIndex->fileName ); + ADDRITEM_TYPE(addrIndex) = ITEMTYPE_NONE; + ADDRITEM_ID(addrIndex) = NULL; + ADDRITEM_NAME(addrIndex) = NULL; + ADDRITEM_PARENT(addrIndex) = NULL; + ADDRITEM_SUBTYPE(addrIndex) = 0; + addrIndex->filePath = NULL; + addrIndex->fileName = NULL; + addrIndex->retVal = MGU_SUCCESS; + addrIndex->needsConversion = FALSE; + addrIndex->wasConverted = FALSE; + addrIndex->conversionError = FALSE; + addrIndex->lastType = ADDR_IF_NONE; + addrIndex->dirtyFlag = FALSE; + node = addrIndex->interfaceList; + while( node ) { + AddressInterface *iface = node->data; + addrindex_free_interface( iface ); + node = g_list_next( node ); + } + g_list_free( addrIndex->interfaceList ); + addrIndex->interfaceList = NULL; + g_free( addrIndex ); +} + +/* +* Print address index. +*/ +void addrindex_print_index( AddressIndex *addrIndex, FILE *stream ) { + g_return_if_fail( addrIndex != NULL ); + fprintf( stream, "AddressIndex:\n" ); + fprintf( stream, "\tfile path: '%s'\n", addrIndex->filePath ); + fprintf( stream, "\tfile name: '%s'\n", addrIndex->fileName ); + fprintf( stream, "\t status: %d : '%s'\n", addrIndex->retVal, mgu_error2string( addrIndex->retVal ) ); + fprintf( stream, "\tconverted: '%s'\n", addrIndex->wasConverted ? "yes" : "no" ); + fprintf( stream, "\tcvt error: '%s'\n", addrIndex->conversionError ? "yes" : "no" ); + fprintf( stream, "\t---\n" ); +} + +/* +* Retrieve specified interface from index. +*/ +AddressInterface *addrindex_get_interface( AddressIndex *addrIndex, AddressIfType ifType ) { + AddressInterface *retVal = NULL; + GList *node; + + g_return_val_if_fail( addrIndex != NULL, NULL ); + + node = addrIndex->interfaceList; + while( node ) { + AddressInterface *iface = node->data; + node = g_list_next( node ); + if( iface->type == ifType ) { + retVal = iface; + break; + } + } + return retVal; +} + +AddressDataSource *addrindex_create_datasource() { + AddressDataSource *ds = NULL; + ds = g_new0( AddressDataSource, 1 ); + ADDRITEM_TYPE(ds) = ITEMTYPE_DATASOURCE; + ADDRITEM_ID(ds) = NULL; + ADDRITEM_NAME(ds) = NULL; + ADDRITEM_PARENT(ds) = NULL; + ADDRITEM_SUBTYPE(ds) = 0; + ds->type = ADDR_IF_NONE; + ds->rawDataSource = NULL; + ds->iface = NULL; + return ds; +} + +/* +* Add data source to index. +* Enter: addrIndex Address index object. +* ifType Interface type to add. +* dataSource Actual data source to add. +* Return: TRUE if data source was added. +* Note: The raw data object (for example, AddressBookFile or VCardFile object) should be +* supplied as the dataSource argument. +*/ +AddressDataSource *addrindex_index_add_datasource( AddressIndex *addrIndex, AddressIfType ifType, gpointer dataSource ) { + AddressInterface *iface; + AddressDataSource *ds = NULL; + + g_return_val_if_fail( addrIndex != NULL, NULL ); + g_return_val_if_fail( dataSource != NULL, NULL ); + + iface = addrindex_get_interface( addrIndex, ifType ); + if( iface ) { + ds = addrindex_create_datasource(); + ADDRITEM_PARENT(ds) = ADDRITEM_OBJECT(iface); + ds->type = ifType; + ds->rawDataSource = dataSource; + ds->iface = iface; + iface->listSource = g_list_append( iface->listSource, ds ); + addrIndex->dirtyFlag = TRUE; + } + return ds; +} + +/* +* Remove data source from index. +* Enter: addrIndex Address index object. +* dataSource Data source to remove. +* Return: Data source if removed, or NULL if data source was not found in +* index. Note the this object must still be freed. +*/ +AddressDataSource *addrindex_index_remove_datasource( AddressIndex *addrIndex, AddressDataSource *dataSource ) { + AddressDataSource *retVal = FALSE; + AddressInterface *iface; + + g_return_val_if_fail( addrIndex != NULL, NULL ); + g_return_val_if_fail( dataSource != NULL, NULL ); + + iface = addrindex_get_interface( addrIndex, dataSource->type ); + if( iface ) { + iface->listSource = g_list_remove( iface->listSource, dataSource ); + addrIndex->dirtyFlag = TRUE; + dataSource->iface = NULL; + retVal = dataSource; + } + return retVal; +} + +static AddressInterface *addrindex_tag_get_interface( AddressIndex *addrIndex, gchar *tag, AddressIfType ifType ) { + AddressInterface *retVal = NULL; + GList *node = addrIndex->interfaceList; + + while( node ) { + AddressInterface *iface = node->data; + node = g_list_next( node ); + if( tag ) { + if( strcmp( iface->listTag, tag ) == 0 ) { + retVal = iface; + break; + } + } + else { + if( iface->type == ifType ) { + retVal = iface; + break; + } + } + } + return retVal; +} + +static AddressInterface *addrindex_tag_get_datasource( AddressIndex *addrIndex, AddressIfType ifType, gchar *tag ) { + AddressInterface *retVal = NULL; + GList *node = addrIndex->interfaceList; + + while( node ) { + AddressInterface *iface = node->data; + node = g_list_next( node ); + if( iface->type == ifType && iface->itemTag ) { + if( strcmp( iface->itemTag, tag ) == 0 ) { + retVal = iface; + break; + } + } + } + return retVal; +} + +/* ********************************************************************** +* Interface XML parsing functions. +* *********************************************************************** +*/ +#if 0 +static void show_attribs( GList *attr ) { + while( attr ) { + gchar *name = ((XMLAttr *)attr->data)->name; + gchar *value = ((XMLAttr *)attr->data)->value; + printf( "\tattr value : %s :%s:\n", name, value ); + attr = g_list_next( attr ); + } + printf( "\t---\n" ); +} +#endif + +static void addrindex_write_elem_s( FILE *fp, gint lvl, gchar *name ) { + gint i; + for( i = 0; i < lvl; i++ ) fputs( " ", fp ); + fputs( "<", fp ); + fputs( name, fp ); +} + +static void addrindex_write_elem_e( FILE *fp, gint lvl, gchar *name ) { + gint i; + for( i = 0; i < lvl; i++ ) fputs( " ", fp ); + fputs( "\n", fp ); +} + +static void addrindex_write_attr( FILE *fp, gchar *name, gchar *value ) { + fputs( " ", fp ); + fputs( name, fp ); + fputs( "=\"", fp ); + xml_file_put_escape_str( fp, value ); + fputs( "\"", fp ); +} + +/* +* Return list of name-value pairs. +*/ +static GList *addrindex_read_attributes( XMLFile *file ) { + GList *list = NULL; + AddressIfAttrib *nv; + GList *attr; + gchar *name; + gchar *value; + + attr = xml_get_current_tag_attr( file ); + while( attr ) { + name = ((XMLAttr *)attr->data)->name; + value = ((XMLAttr *)attr->data)->value; + nv = g_new0( AddressIfAttrib, 1 ); + nv->name = g_strdup( name ); + nv->value = g_strdup( value ); + list = g_list_append( list, nv ); + attr = g_list_next( attr ); + } + return list; +} + +/* +* Output name-value pairs. +*/ +static void addrindex_write_attributes( FILE *fp, gchar *tag, GList *list, gint lvl ) { + GList *node; + AddressIfAttrib *nv; + if( list ) { + addrindex_write_elem_s( fp, lvl, tag ); + node = list; + while( node ) { + nv = node->data; + addrindex_write_attr( fp, nv->name, nv->value ); + node = g_list_next( node ); + } + fputs(" />\n", fp); + } +} + +#if 0 +static void addrindex_print_attributes( GList *list, FILE *stream ) { + GList *node = list; + while( node ) { + AddressIfAttrib *nv = node->data; + fprintf( stream, "%s : %s\n", nv->name, nv->value ); + node = g_list_next( node ); + } +} +#endif + +static AddressDataSource *addrindex_parse_book( XMLFile *file ) { + AddressDataSource *ds = g_new0( AddressDataSource, 1 ); + AddressBookFile *abf; + GList *attr; + + abf = addrbook_create_book(); + attr = xml_get_current_tag_attr( file ); + while( attr ) { + gchar *name = ((XMLAttr *)attr->data)->name; + gchar *value = ((XMLAttr *)attr->data)->value; + if( strcmp( name, ATTAG_BOOK_NAME ) == 0 ) { + addrbook_set_name( abf, value ); + } + else if( strcmp( name, ATTAG_BOOK_FILE ) == 0) { + addrbook_set_file( abf, value ); + } + attr = g_list_next( attr ); + } + ds->rawDataSource = abf; + return ds; +} + +static void addrindex_write_book( FILE *fp, AddressDataSource *ds, gint lvl ) { + AddressBookFile *abf = ds->rawDataSource; + if( abf ) { + addrindex_write_elem_s( fp, lvl, TAG_DS_ADDRESS_BOOK ); + addrindex_write_attr( fp, ATTAG_BOOK_NAME, abf->name ); + addrindex_write_attr( fp, ATTAG_BOOK_FILE, abf->fileName ); + fputs( " />\n", fp ); + } +} + +static AddressDataSource *addrindex_parse_vcard( XMLFile *file ) { + AddressDataSource *ds = g_new0( AddressDataSource, 1 ); + VCardFile *vcf; + GList *attr; + + vcf = vcard_create(); + attr = xml_get_current_tag_attr( file ); + while( attr ) { + gchar *name = ((XMLAttr *)attr->data)->name; + gchar *value = ((XMLAttr *)attr->data)->value; + if( strcmp( name, ATTAG_VCARD_NAME ) == 0 ) { + vcard_set_name( vcf, value ); + } + else if( strcmp( name, ATTAG_VCARD_FILE ) == 0) { + vcard_set_file( vcf, value ); + } + attr = g_list_next( attr ); + } + ds->rawDataSource = vcf; + return ds; +} + +static void addrindex_write_vcard( FILE *fp, AddressDataSource *ds, gint lvl ) { + VCardFile *vcf = ds->rawDataSource; + if( vcf ) { + addrindex_write_elem_s( fp, lvl, TAG_DS_VCARD ); + addrindex_write_attr( fp, ATTAG_VCARD_NAME, vcf->name ); + addrindex_write_attr( fp, ATTAG_VCARD_FILE, vcf->path ); + fputs( " />\n", fp ); + } +} + +#ifdef USE_JPILOT +static AddressDataSource *addrindex_parse_jpilot( XMLFile *file ) { + AddressDataSource *ds = g_new0( AddressDataSource, 1 ); + JPilotFile *jpf; + GList *attr; + + jpf = jpilot_create(); + attr = xml_get_current_tag_attr( file ); + while( attr ) { + gchar *name = ((XMLAttr *)attr->data)->name; + gchar *value = ((XMLAttr *)attr->data)->value; + if( strcmp( name, ATTAG_JPILOT_NAME ) == 0 ) { + jpilot_set_name( jpf, value ); + } + else if( strcmp( name, ATTAG_JPILOT_FILE ) == 0 ) { + jpilot_set_file( jpf, value ); + } + else if( strcmp( name, ATTAG_JPILOT_CUSTOM_1 ) == 0 ) { + jpilot_add_custom_label( jpf, value ); + } + else if( strcmp( name, ATTAG_JPILOT_CUSTOM_2 ) == 0 ) { + jpilot_add_custom_label( jpf, value ); + } + else if( strcmp( name, ATTAG_JPILOT_CUSTOM_3 ) == 0 ) { + jpilot_add_custom_label( jpf, value ); + } + else if( strcmp( name, ATTAG_JPILOT_CUSTOM_4 ) == 0 ) { + jpilot_add_custom_label( jpf, value ); + } + attr = g_list_next( attr ); + } + ds->rawDataSource = jpf; + return ds; +} + +static void addrindex_write_jpilot( FILE *fp,AddressDataSource *ds, gint lvl ) { + JPilotFile *jpf = ds->rawDataSource; + if( jpf ) { + gint ind; + GList *node; + GList *customLbl = jpilot_get_custom_labels( jpf ); + addrindex_write_elem_s( fp, lvl, TAG_DS_JPILOT ); + addrindex_write_attr( fp, ATTAG_JPILOT_NAME, jpf->name ); + addrindex_write_attr( fp, ATTAG_JPILOT_FILE, jpf->path ); + node = customLbl; + ind = 1; + while( node ) { + gchar name[256]; + sprintf( name, "%s%d", ATTAG_JPILOT_CUSTOM, ind ); + addrindex_write_attr( fp, name, node->data ); + ind++; + node = g_list_next( node ); + } + fputs( " />\n", fp ); + } +} +#else +/* Just read/write name-value pairs */ +static AddressDataSource *addrindex_parse_jpilot( XMLFile *file ) { + AddressDataSource *ds = g_new0( AddressDataSource, 1 ); + GList *list = addrindex_read_attributes( file ); + ds->rawDataSource = list; + return ds; +} + +static void addrindex_write_jpilot( FILE *fp, AddressDataSource *ds, gint lvl ) { + GList *list = ds->rawDataSource; + if( list ) { + addrindex_write_attributes( fp, TAG_DS_JPILOT, list, lvl ); + } +} +#endif + +#ifdef USE_LDAP +static AddressDataSource *addrindex_parse_ldap( XMLFile *file ) { + AddressDataSource *ds = g_new0( AddressDataSource, 1 ); + SyldapServer *server; + GList *attr; + + server = syldap_create(); + attr = xml_get_current_tag_attr( file ); + while( attr ) { + gchar *name = ((XMLAttr *)attr->data)->name; + gchar *value = ((XMLAttr *)attr->data)->value; + gint ivalue = atoi( value ); + if( strcmp( name, ATTAG_LDAP_NAME ) == 0 ) { + syldap_set_name( server, value ); + } + else if( strcmp( name, ATTAG_LDAP_HOST ) == 0 ) { + syldap_set_host( server, value ); + } + else if( strcmp( name, ATTAG_LDAP_PORT ) == 0 ) { + syldap_set_port( server, ivalue ); + } + else if( strcmp( name, ATTAG_LDAP_BASE_DN ) == 0 ) { + syldap_set_base_dn( server, value ); + } + else if( strcmp( name, ATTAG_LDAP_BIND_DN ) == 0 ) { + syldap_set_bind_dn( server, value ); + } + else if( strcmp( name, ATTAG_LDAP_BIND_PASS ) == 0 ) { + syldap_set_bind_password( server, value ); + } + else if( strcmp( name, ATTAG_LDAP_CRITERIA ) == 0 ) { + syldap_set_search_criteria( server, value ); + } + else if( strcmp( name, ATTAG_LDAP_MAX_ENTRY ) == 0 ) { + syldap_set_max_entries( server, ivalue ); + } + else if( strcmp( name, ATTAG_LDAP_TIMEOUT ) == 0 ) { + syldap_set_timeout( server, ivalue ); + } + attr = g_list_next( attr ); + } + + ds->rawDataSource = server; + return ds; +} + +static void addrindex_write_ldap( FILE *fp, AddressDataSource *ds, gint lvl ) { + SyldapServer *server = ds->rawDataSource; + if( server ) { + gchar value[256]; + + addrindex_write_elem_s( fp, lvl, TAG_DS_LDAP ); + addrindex_write_attr( fp, ATTAG_LDAP_NAME, server->name ); + addrindex_write_attr( fp, ATTAG_LDAP_HOST, server->hostName ); + + sprintf( value, "%d", server->port ); + addrindex_write_attr( fp, ATTAG_LDAP_PORT, value ); + + addrindex_write_attr( fp, ATTAG_LDAP_BASE_DN, server->baseDN ); + addrindex_write_attr( fp, ATTAG_LDAP_BIND_DN, server->bindDN ); + addrindex_write_attr( fp, ATTAG_LDAP_BIND_PASS, server->bindPass ); + addrindex_write_attr( fp, ATTAG_LDAP_CRITERIA, server->searchCriteria ); + + sprintf( value, "%d", server->maxEntries ); + addrindex_write_attr( fp, ATTAG_LDAP_MAX_ENTRY, value ); + sprintf( value, "%d", server->timeOut ); + addrindex_write_attr( fp, ATTAG_LDAP_TIMEOUT, value ); + + fputs(" />\n", fp); + } +} +#else +/* Just read/write name-value pairs */ +static AddressDataSource *addrindex_parse_ldap( XMLFile *file ) { + AddressDataSource *ds = g_new0( AddressDataSource, 1 ); + GList *list = addrindex_read_attributes( file ); + ds->rawDataSource = list; + return ds; +} + +static void addrindex_write_ldap( FILE *fp, AddressDataSource *ds, gint lvl ) { + GList *list = ds->rawDataSource; + if( list ) { + addrindex_write_attributes( fp, TAG_DS_LDAP, list, lvl ); + } +} +#endif + +/* ********************************************************************** +* Address index I/O functions. +* *********************************************************************** +*/ +static void addrindex_read_index( AddressIndex *addrIndex, XMLFile *file ) { + guint prev_level; + /* gchar *element; */ + /* GList *attr; */ + XMLTag *xtag; + AddressInterface *iface = NULL, *dsIFace = NULL; + AddressDataSource *ds; + + for (;;) { + prev_level = file->level; + xml_parse_next_tag( file ); + if( file->level < prev_level ) return; + + xtag = xml_get_current_tag( file ); + /* printf( "tag : %s\n", xtag->tag ); */ + + iface = addrindex_tag_get_interface( addrIndex, xtag->tag, ADDR_IF_NONE ); + if( iface ) { + addrIndex->lastType = iface->type; + if( iface->legacyFlag ) addrIndex->needsConversion = TRUE; + /* printf( "found : %s\n", iface->name ); */ + } + else { + dsIFace = addrindex_tag_get_datasource( addrIndex, addrIndex->lastType, xtag->tag ); + if( dsIFace ) { + /* Add data source to list */ + /* printf( "\tdata source: %s\n", dsIFace->name ); */ + ds = NULL; + if( addrIndex->lastType == ADDR_IF_BOOK ) { + ds = addrindex_parse_book( file ); + if( ds->rawDataSource ) { + addrbook_set_path( ds->rawDataSource, addrIndex->filePath ); + /* addrbook_print_book( ds->rawDataSource, stdout ); */ + } + } + else if( addrIndex->lastType == ADDR_IF_VCARD ) { + ds = addrindex_parse_vcard( file ); + /* if( ds->rawDataSource ) { */ + /* vcard_print_file( ds->rawDataSource, stdout ); */ + /* } */ + } + else if( addrIndex->lastType == ADDR_IF_JPILOT ) { + ds = addrindex_parse_jpilot( file ); + /* + if( ds->rawDataSource ) { + jpilot_print_file( ds->rawDataSource, stdout ); + // addrindex_print_attributes( ds->rawDataSource, stdout ); + } + */ + } + else if( addrIndex->lastType == ADDR_IF_LDAP ) { + ds = addrindex_parse_ldap( file ); + /* + if( ds->rawDataSource ) { + syldap_print_data( ds->rawDataSource, stdout ); + // addrindex_print_attributes( ds->rawDataSource, stdout ); + } + */ + } + if( ds ) { + ds->type = addrIndex->lastType; + ds->iface = dsIFace; + dsIFace->listSource = g_list_append( dsIFace->listSource, ds ); + } + /* printf( "=============================\n\n" ); */ + } + } + /* + element = xml_get_element( file ); + attr = xml_get_current_tag_attr( file ); + if( _interfaceLast_ && ! _interfaceLast_->legacyFlag ) { + show_attribs( attr ); + printf( "\ttag value : %s :\n", element ); + } + */ + addrindex_read_index( addrIndex, file ); + } +} + +static gint addrindex_read_file( AddressIndex *addrIndex ) { + XMLFile *file = NULL; + gchar *fileSpec = NULL; + + g_return_val_if_fail( addrIndex != NULL, -1 ); + + fileSpec = g_strconcat( addrIndex->filePath, G_DIR_SEPARATOR_S, addrIndex->fileName, NULL ); + addrIndex->retVal = MGU_NO_FILE; + file = xml_open_file( fileSpec ); + g_free( fileSpec ); + + if( file == NULL ) { + /* fprintf( stdout, " file '%s' does not exist.\n", addrIndex->fileName ); */ + return addrIndex->retVal; + } + + addrIndex->retVal = MGU_BAD_FORMAT; + if( xml_get_dtd( file ) == 0 ) { + if( xml_parse_next_tag( file ) == 0 ) { + if( xml_compare_tag( file, TAG_ADDRESS_INDEX ) ) { + addrindex_read_index( addrIndex, file ); + addrIndex->retVal = MGU_SUCCESS; + } + } + } + xml_close_file( file ); + + return addrIndex->retVal; +} + +static void addrindex_write_index( AddressIndex *addrIndex, FILE *fp ) { + GList *nodeIF, *nodeDS; + gint lvlList = 1; + gint lvlItem = 1 + lvlList; + + nodeIF = addrIndex->interfaceList; + while( nodeIF ) { + AddressInterface *iface = nodeIF->data; + if( ! iface->legacyFlag ) { + nodeDS = iface->listSource; + addrindex_write_elem_s( fp, lvlList, iface->listTag ); + fputs( ">\n", fp ); + while( nodeDS ) { + AddressDataSource *ds = nodeDS->data; + if( ds ) { + if( iface->type == ADDR_IF_BOOK ) { + addrindex_write_book( fp, ds, lvlItem ); + } + if( iface->type == ADDR_IF_VCARD ) { + addrindex_write_vcard( fp, ds, lvlItem ); + } + if( iface->type == ADDR_IF_JPILOT ) { + addrindex_write_jpilot( fp, ds, lvlItem ); + } + if( iface->type == ADDR_IF_LDAP ) { + addrindex_write_ldap( fp, ds, lvlItem ); + } + } + nodeDS = g_list_next( nodeDS ); + } + addrindex_write_elem_e( fp, lvlList, iface->listTag ); + } + nodeIF = g_list_next( nodeIF ); + } +} + +/* +* Write data to specified file. +* Enter: addrIndex Address index object. +* newFile New file name. +* return: Status code, from addrIndex->retVal. +* Note: File will be created in directory specified by addrIndex. +*/ +gint addrindex_write_to( AddressIndex *addrIndex, const gchar *newFile ) { + FILE *fp; + gchar *fileSpec; +#ifndef DEV_STANDALONE + PrefFile *pfile; +#endif + + g_return_val_if_fail( addrIndex != NULL, -1 ); + + fileSpec = g_strconcat( addrIndex->filePath, G_DIR_SEPARATOR_S, newFile, NULL ); + addrIndex->retVal = MGU_OPEN_FILE; +#ifdef DEV_STANDALONE + fp = g_fopen( fileSpec, "wb" ); + g_free( fileSpec ); + if( fp ) { + fputs( "\n", fp ); +#else + pfile = prefs_file_open( fileSpec ); + g_free( fileSpec ); + if( pfile ) { + fp = pfile->fp; + fprintf( fp, "\n", CS_INTERNAL ); +#endif + addrindex_write_elem_s( fp, 0, TAG_ADDRESS_INDEX ); + fputs( ">\n", fp ); + + addrindex_write_index( addrIndex, fp ); + addrindex_write_elem_e( fp, 0, TAG_ADDRESS_INDEX ); + + addrIndex->retVal = MGU_SUCCESS; +#ifdef DEV_STANDALONE + fclose( fp ); +#else + if( prefs_file_close( pfile ) < 0 ) { + addrIndex->retVal = MGU_ERROR_WRITE; + } +#endif + } + + fileSpec = NULL; + return addrIndex->retVal; +} + +/* +* Save address index data to original file. +* return: Status code, from addrIndex->retVal. +*/ +gint addrindex_save_data( AddressIndex *addrIndex ) { + g_return_val_if_fail( addrIndex != NULL, -1 ); + + addrIndex->retVal = MGU_NO_FILE; + if( addrIndex->fileName == NULL || *addrIndex->fileName == '\0' ) return addrIndex->retVal; + if( addrIndex->filePath == NULL || *addrIndex->filePath == '\0' ) return addrIndex->retVal; + + addrindex_write_to( addrIndex, addrIndex->fileName ); + if( addrIndex->retVal == MGU_SUCCESS ) { + addrIndex->dirtyFlag = FALSE; + } + return addrIndex->retVal; +} + +/* +* Save all address book files which may have changed. +* Return: Status code, set if there was a problem saving data. +*/ +gint addrindex_save_all_books( AddressIndex *addrIndex ) { + gint retVal = MGU_SUCCESS; + GList *nodeIf, *nodeDS; + + nodeIf = addrIndex->interfaceList; + while( nodeIf ) { + AddressInterface *iface = nodeIf->data; + if( iface->type == ADDR_IF_BOOK ) { + nodeDS = iface->listSource; + while( nodeDS ) { + AddressDataSource *ds = nodeDS->data; + AddressBookFile *abf = ds->rawDataSource; + if( abf->dirtyFlag ) { + if( abf->readFlag ) { + addrbook_save_data( abf ); + if( abf->retVal != MGU_SUCCESS ) { + retVal = abf->retVal; + } + } + } + nodeDS = g_list_next( nodeDS ); + } + break; + } + nodeIf = g_list_next( nodeIf ); + } + return retVal; +} + + +/* ********************************************************************** +* Address book conversion to new format. +* *********************************************************************** +*/ + +#define ELTAG_IF_OLD_FOLDER "folder" +#define ELTAG_IF_OLD_GROUP "group" +#define ELTAG_IF_OLD_ITEM "item" +#define ELTAG_IF_OLD_NAME "name" +#define ELTAG_IF_OLD_ADDRESS "address" +#define ELTAG_IF_OLD_REMARKS "remarks" +#define ATTAG_IF_OLD_NAME "name" + +#define TEMPNODE_ROOT 0 +#define TEMPNODE_FOLDER 1 +#define TEMPNODE_GROUP 2 +#define TEMPNODE_ADDRESS 3 + +typedef struct _AddressCvt_Node AddressCvtNode; +struct _AddressCvt_Node { + gint type; + gchar *name; + gchar *address; + gchar *remarks; + GList *list; +}; + +/* +* Parse current address item. +*/ +static AddressCvtNode *addrindex_parse_item( XMLFile *file ) { + gchar *element; + guint level; + AddressCvtNode *nn; + + nn = g_new0( AddressCvtNode, 1 ); + nn->type = TEMPNODE_ADDRESS; + nn->list = NULL; + + level = file->level; + + for (;;) { + xml_parse_next_tag(file); + if (file->level < level) return nn; + + element = xml_get_element( file ); + if( xml_compare_tag( file, ELTAG_IF_OLD_NAME ) ) { + nn->name = g_strdup( element ); + } + if( xml_compare_tag( file, ELTAG_IF_OLD_ADDRESS ) ) { + nn->address = g_strdup( element ); + } + if( xml_compare_tag( file, ELTAG_IF_OLD_REMARKS ) ) { + nn->remarks = g_strdup( element ); + } + xml_parse_next_tag(file); + } +} + +/* +* Create a temporary node below specified node. +*/ +static AddressCvtNode *addrindex_add_object( AddressCvtNode *node, gint type, gchar *name, gchar *addr, char *rem ) { + AddressCvtNode *nn; + nn = g_new0( AddressCvtNode, 1 ); + nn->type = type; + nn->name = g_strdup( name ); + nn->remarks = g_strdup( rem ); + node->list = g_list_append( node->list, nn ); + return nn; +} + +/* +* Process current temporary node. +*/ +static void addrindex_add_obj( XMLFile *file, AddressCvtNode *node ) { + GList *attr; + guint prev_level; + AddressCvtNode *newNode = NULL; + gchar *name; + gchar *value; + + for (;;) { + prev_level = file->level; + xml_parse_next_tag( file ); + if (file->level < prev_level) return; + name = NULL; + value = NULL; + + if( xml_compare_tag( file, ELTAG_IF_OLD_GROUP ) ) { + attr = xml_get_current_tag_attr(file); + if (attr) { + name = ((XMLAttr *)attr->data)->name; + if( strcmp( name, ATTAG_IF_OLD_NAME ) == 0 ) { + value = ((XMLAttr *)attr->data)->value; + } + } + newNode = addrindex_add_object( node, TEMPNODE_GROUP, value, "", "" ); + addrindex_add_obj( file, newNode ); + + } + else if( xml_compare_tag( file, ELTAG_IF_OLD_FOLDER ) ) { + attr = xml_get_current_tag_attr(file); + if (attr) { + name = ((XMLAttr *)attr->data)->name; + if( strcmp( name, ATTAG_IF_OLD_NAME ) == 0 ) { + value = ((XMLAttr *)attr->data)->value; + } + } + newNode = addrindex_add_object( node, TEMPNODE_FOLDER, value, "", "" ); + addrindex_add_obj( file, newNode ); + } + else if( xml_compare_tag( file, ELTAG_IF_OLD_ITEM ) ) { + newNode = addrindex_parse_item( file ); + node->list = g_list_append( node->list, newNode ); + } + else { + /* printf( "invalid: !!! \n" ); */ + attr = xml_get_current_tag_attr( file ); + } + } +} + +/* +* Consume all nodes below current tag. +*/ +static void addrindex_consume_tree( XMLFile *file ) { + guint prev_level; + gchar *element; + GList *attr; + XMLTag *xtag; + + for (;;) { + prev_level = file->level; + xml_parse_next_tag( file ); + if (file->level < prev_level) return; + + xtag = xml_get_current_tag( file ); + /* printf( "tag : %s\n", xtag->tag ); */ + element = xml_get_element( file ); + attr = xml_get_current_tag_attr( file ); + /* show_attribs( attr ); */ + /* printf( "\ttag value : %s :\n", element ); */ + addrindex_consume_tree( file ); + } +} + +/* +* Print temporary tree. +*/ +static void addrindex_print_node( AddressCvtNode *node, FILE *stream ) { + GList *list; + + fprintf( stream, "Node:\ttype :%d:\n", node->type ); + fprintf( stream, "\tname :%s:\n", node->name ); + fprintf( stream, "\taddr :%s:\n", node->address ); + fprintf( stream, "\trems :%s:\n", node->remarks ); + if( node->list ) { + fprintf( stream, "\t--list----\n" ); + } + list = node->list; + while( list ) { + AddressCvtNode *lNode = list->data; + list = g_list_next( list ); + addrindex_print_node( lNode, stream ); + } + fprintf( stream, "\t==list-%d==\n", node->type ); +} + +/* +* Free up temporary tree. +*/ +static void addrindex_free_node( AddressCvtNode *node ) { + GList *list = node->list; + + while( list ) { + AddressCvtNode *lNode = list->data; + list = g_list_next( list ); + addrindex_free_node( lNode ); + } + node->type = TEMPNODE_ROOT; + g_free( node->name ); + g_free( node->address ); + g_free( node->remarks ); + g_list_free( node->list ); + g_free( node ); +} + +/* +* Process address book for specified node. +*/ +static void addrindex_process_node( + AddressBookFile *abf, AddressCvtNode *node, ItemFolder *parent, + ItemGroup *parentGrp, ItemFolder *folderGrp ) +{ + GList *list; + ItemFolder *itemFolder = NULL; + ItemGroup *itemGParent = parentGrp; + ItemFolder *itemGFolder = folderGrp; + AddressCache *cache = abf->addressCache; + + if( node->type == TEMPNODE_ROOT ) { + itemFolder = parent; + } + else if( node->type == TEMPNODE_FOLDER ) { + itemFolder = addritem_create_item_folder(); + addritem_folder_set_name( itemFolder, node->name ); + addrcache_id_folder( cache, itemFolder ); + addrcache_folder_add_folder( cache, parent, itemFolder ); + itemGFolder = NULL; + } + else if( node->type == TEMPNODE_GROUP ) { + ItemGroup *itemGroup; + gchar *fName; + + /* Create a folder for group */ + fName = g_strdup_printf( "Cvt - %s", node->name ); + itemGFolder = addritem_create_item_folder(); + addritem_folder_set_name( itemGFolder, fName ); + addrcache_id_folder( cache, itemGFolder ); + addrcache_folder_add_folder( cache, parent, itemGFolder ); + g_free( fName ); + + /* Add group into folder */ + itemGroup = addritem_create_item_group(); + addritem_group_set_name( itemGroup, node->name ); + addrcache_id_group( cache, itemGroup ); + addrcache_folder_add_group( cache, itemGFolder, itemGroup ); + itemGParent = itemGroup; + } + else if( node->type == TEMPNODE_ADDRESS ) { + ItemPerson *itemPerson; + ItemEMail *itemEMail; + + /* Create person and email objects */ + itemPerson = addritem_create_item_person(); + addritem_person_set_common_name( itemPerson, node->name ); + addrcache_id_person( cache, itemPerson ); + itemEMail = addritem_create_item_email(); + addritem_email_set_address( itemEMail, node->address ); + addritem_email_set_remarks( itemEMail, node->remarks ); + addrcache_id_email( cache, itemEMail ); + addrcache_person_add_email( cache, itemPerson, itemEMail ); + + /* Add person into appropriate folder */ + if( itemGFolder ) { + addrcache_folder_add_person( cache, itemGFolder, itemPerson ); + } + else { + addrcache_folder_add_person( cache, parent, itemPerson ); + } + + /* Add email address only into group */ + if( parentGrp ) { + addrcache_group_add_email( cache, parentGrp, itemEMail ); + } + } + + list = node->list; + while( list ) { + AddressCvtNode *lNode = list->data; + list = g_list_next( list ); + addrindex_process_node( abf, lNode, itemFolder, itemGParent, itemGFolder ); + } +} + +/* +* Process address book to specified file number. +*/ +static gboolean addrindex_process_book( AddressIndex *addrIndex, XMLFile *file, gchar *displayName ) { + gboolean retVal = FALSE; + AddressBookFile *abf = NULL; + AddressCvtNode *rootNode = NULL; + gchar *newFile = NULL; + GList *fileList = NULL; + gint fileNum = 0; + + /* Setup root node */ + rootNode = g_new0( AddressCvtNode, 1 ); + rootNode->type = TEMPNODE_ROOT; + rootNode->name = g_strdup( "root" ); + rootNode->list = NULL; + addrindex_add_obj( file, rootNode ); + /* addrindex_print_node( rootNode, stdout ); */ + + /* Create new address book */ + abf = addrbook_create_book(); + addrbook_set_name( abf, displayName ); + addrbook_set_path( abf, addrIndex->filePath ); + + /* Determine next available file number */ + fileList = addrbook_get_bookfile_list( abf ); + if( fileList ) { + fileNum = 1 + abf->maxValue; + } + g_list_free( fileList ); + fileList = NULL; + + newFile = addrbook_gen_new_file_name( fileNum ); + if( newFile ) { + addrbook_set_file( abf, newFile ); + } + + addrindex_process_node( abf, rootNode, abf->addressCache->rootFolder, NULL, NULL ); + + /* addrbook_dump_book( abf, stdout ); */ + addrbook_save_data( abf ); + addrIndex->retVal = abf->retVal; + if( abf->retVal == MGU_SUCCESS ) retVal = TRUE; + + addrbook_free_book( abf ); + abf = NULL; + addrindex_free_node( rootNode ); + rootNode = NULL; + + /* Create entries in address index */ + if( retVal ) { + abf = addrbook_create_book(); + addrbook_set_name( abf, displayName ); + addrbook_set_path( abf, addrIndex->filePath ); + addrbook_set_file( abf, newFile ); + addrindex_index_add_datasource( addrIndex, ADDR_IF_BOOK, abf ); + } + + return retVal; +} + +/* +* Process tree converting data. +*/ +static void addrindex_convert_tree( AddressIndex *addrIndex, XMLFile *file ) { + guint prev_level; + gchar *element; + GList *attr; + XMLTag *xtag; + + /* Process file */ + for (;;) { + prev_level = file->level; + xml_parse_next_tag( file ); + if (file->level < prev_level) return; + + xtag = xml_get_current_tag( file ); + /* printf( "tag : %d : %s\n", prev_level, xtag->tag ); */ + if( strcmp( xtag->tag, TAG_IF_OLD_COMMON ) == 0 ) { + if( addrindex_process_book( addrIndex, file, DISP_OLD_COMMON ) ) { + addrIndex->needsConversion = FALSE; + addrIndex->wasConverted = TRUE; + continue; + } + return; + } + if( strcmp( xtag->tag, TAG_IF_OLD_PERSONAL ) == 0 ) { + if( addrindex_process_book( addrIndex, file, DISP_OLD_PERSONAL ) ) { + addrIndex->needsConversion = FALSE; + addrIndex->wasConverted = TRUE; + continue; + } + return; + } + element = xml_get_element( file ); + attr = xml_get_current_tag_attr( file ); + /* show_attribs( attr ); */ + /* printf( "\ttag value : %s :\n", element ); */ + addrindex_consume_tree( file ); + } +} + +static gint addrindex_convert_data( AddressIndex *addrIndex ) { + XMLFile *file = NULL; + gchar *fileSpec; + + fileSpec = g_strconcat( addrIndex->filePath, G_DIR_SEPARATOR_S, addrIndex->fileName, NULL ); + addrIndex->retVal = MGU_NO_FILE; + file = xml_open_file( fileSpec ); + g_free( fileSpec ); + + if( file == NULL ) { + /* fprintf( stdout, " file '%s' does not exist.\n", addrIndex->fileName ); */ + return addrIndex->retVal; + } + + addrIndex->retVal = MGU_BAD_FORMAT; + if( xml_get_dtd( file ) == 0 ) { + if( xml_parse_next_tag( file ) == 0 ) { + if( xml_compare_tag( file, TAG_ADDRESS_INDEX ) ) { + addrindex_convert_tree( addrIndex, file ); + } + } + } + xml_close_file( file ); + return addrIndex->retVal; +} + +/* +* Create a new address book file. +*/ +static gboolean addrindex_create_new_book( AddressIndex *addrIndex, gchar *displayName ) { + gboolean retVal = FALSE; + AddressBookFile *abf = NULL; + gchar *newFile = NULL; + GList *fileList = NULL; + gint fileNum = 0; + + /* Create new address book */ + abf = addrbook_create_book(); + addrbook_set_name( abf, displayName ); + addrbook_set_path( abf, addrIndex->filePath ); + + /* Determine next available file number */ + fileList = addrbook_get_bookfile_list( abf ); + if( fileList ) { + fileNum = 1 + abf->maxValue; + } + g_list_free( fileList ); + fileList = NULL; + + newFile = addrbook_gen_new_file_name( fileNum ); + if( newFile ) { + addrbook_set_file( abf, newFile ); + } + + addrbook_save_data( abf ); + addrIndex->retVal = abf->retVal; + if( abf->retVal == MGU_SUCCESS ) retVal = TRUE; + addrbook_free_book( abf ); + abf = NULL; + + /* Create entries in address index */ + if( retVal ) { + abf = addrbook_create_book(); + addrbook_set_name( abf, displayName ); + addrbook_set_path( abf, addrIndex->filePath ); + addrbook_set_file( abf, newFile ); + addrindex_index_add_datasource( addrIndex, ADDR_IF_BOOK, abf ); + } + + return retVal; +} + +/* +* Read data for address index performing a conversion if necesary. +* Enter: addrIndex Address index object. +* return: Status code, from addrIndex->retVal. +* Note: New address book files will be created in directory specified by +* addrIndex. Three files will be created, for the following: +* "Common addresses" +* "Personal addresses" +* "Auto-registered" - a new address book. +*/ +gint addrindex_read_data( AddressIndex *addrIndex ) { + g_return_val_if_fail( addrIndex != NULL, -1 ); + + addrIndex->conversionError = FALSE; + addrindex_read_file( addrIndex ); + if( addrIndex->retVal == MGU_SUCCESS ) { + if( addrIndex->needsConversion ) { + if( addrindex_convert_data( addrIndex ) == MGU_SUCCESS ) { + addrIndex->conversionError = TRUE; + } + else { + addrIndex->conversionError = TRUE; + } + } + addrIndex->dirtyFlag = TRUE; + } + return addrIndex->retVal; +} + +/* +* Create new address books for a new address index. +* Enter: addrIndex Address index object. +* return: Status code, from addrIndex->retVal. +* Note: New address book files will be created in directory specified by +* addrIndex. Three files will be created, for the following: +* "Common addresses" +* "Personal addresses" +* "Auto-registered" - a new address book. +*/ +gint addrindex_create_new_books( AddressIndex *addrIndex ) { + gboolean flg; + + g_return_val_if_fail( addrIndex != NULL, -1 ); + + flg = addrindex_create_new_book( addrIndex, DISP_NEW_COMMON ); + if( flg ) { + flg = addrindex_create_new_book( addrIndex, DISP_NEW_PERSONAL ); + flg = addrindex_create_new_book( addrIndex, ADDR_DS_AUTOREG ); + addrIndex->dirtyFlag = TRUE; + } + return addrIndex->retVal; +} + +gint addrindex_create_extra_books( AddressIndex *addrIndex ) { + GList *node_ds; + AddressInterface *iface = NULL; + AddressDataSource *ds = NULL; + const gchar *ds_name; + + g_return_val_if_fail(addrIndex != NULL, -1); + + iface = addrindex_get_interface(addrIndex, ADDR_IF_BOOK); + if (!iface) + return -1; + + for (node_ds = iface->listSource; node_ds != NULL; + node_ds = node_ds->next) { + ds = node_ds->data; + ds_name = addrindex_ds_get_name(ds); + if (!ds_name) + continue; + if (!strcmp(ds_name, ADDR_DS_AUTOREG)) { + debug_print("%s found\n", ADDR_DS_AUTOREG); + return 0; + } + } + + debug_print("%s not found, creating new one\n", ADDR_DS_AUTOREG); + if (addrindex_create_new_book(addrIndex, ADDR_DS_AUTOREG)) { + addrIndex->dirtyFlag = TRUE; + } + + return addrIndex->retVal; +} + +/* ********************************************************************** +* New interface stuff. +* *********************************************************************** +*/ + +/* + * Return modified flag for specified data source. + */ +gboolean addrindex_ds_get_modify_flag( AddressDataSource *ds ) { + gboolean retVal = FALSE; + AddressInterface *iface; + + if( ds == NULL ) return retVal; + iface = ds->iface; + if( iface == NULL ) return retVal; + if( iface->getModifyFlag ) { + retVal = ( iface->getModifyFlag ) ( ds->rawDataSource ); + } + return retVal; +} + +/* + * Return accessed flag for specified data source. + */ +gboolean addrindex_ds_get_access_flag( AddressDataSource *ds ) { + gboolean retVal = FALSE; + AddressInterface *iface; + + if( ds == NULL ) return retVal; + iface = ds->iface; + if( iface == NULL ) return retVal; + if( iface->getAccessFlag ) { + retVal = ( iface->getAccessFlag ) ( ds->rawDataSource ); + } + return retVal; +} + +/* + * Return data read flag for specified data source. + */ +gboolean addrindex_ds_get_read_flag( AddressDataSource *ds ) { + gboolean retVal = TRUE; + AddressInterface *iface; + + if( ds == NULL ) return retVal; + iface = ds->iface; + if( iface == NULL ) return retVal; + if( iface->getReadFlag ) { + retVal = ( iface->getReadFlag ) ( ds->rawDataSource ); + } + return retVal; +} + +/* + * Return status code for specified data source. + */ +gint addrindex_ds_get_status_code( AddressDataSource *ds ) { + gint retVal = MGU_SUCCESS; + AddressInterface *iface; + + if( ds == NULL ) return retVal; + iface = ds->iface; + if( iface == NULL ) return retVal; + if( iface->getStatusCode ) { + retVal = ( iface->getStatusCode ) ( ds->rawDataSource ); + } + return retVal; +} + +/* + * Return data read flag for specified data source. + */ +gint addrindex_ds_read_data( AddressDataSource *ds ) { + gint retVal = MGU_SUCCESS; + AddressInterface *iface; + + if( ds == NULL ) return retVal; + iface = ds->iface; + if( iface == NULL ) return retVal; + if( iface->getReadData ) { + retVal = ( iface->getReadData ) ( ds->rawDataSource ); + } + return retVal; +} + +/* + * Return data read flag for specified data source. + */ +ItemFolder *addrindex_ds_get_root_folder( AddressDataSource *ds ) { + ItemFolder *retVal = NULL; + AddressInterface *iface; + + if( ds == NULL ) return retVal; + iface = ds->iface; + if( iface == NULL ) return retVal; + if( iface->getRootFolder ) { + retVal = ( iface->getRootFolder ) ( ds->rawDataSource ); + } + return retVal; +} + +/* + * Return list of folders for specified data source. + */ +GList *addrindex_ds_get_list_folder( AddressDataSource *ds ) { + GList *retVal = FALSE; + AddressInterface *iface; + + if( ds == NULL ) return retVal; + iface = ds->iface; + if( iface == NULL ) return retVal; + if( iface->getListFolder ) { + retVal = ( iface->getListFolder ) ( ds->rawDataSource ); + } + return retVal; +} + +/* + * Return list of persons in root folder for specified data source. + */ +GList *addrindex_ds_get_list_person( AddressDataSource *ds ) { + GList *retVal = FALSE; + AddressInterface *iface; + + if( ds == NULL ) return retVal; + iface = ds->iface; + if( iface == NULL ) return retVal; + if( iface->getListPerson ) { + retVal = ( iface->getListPerson ) ( ds->rawDataSource ); + } + return retVal; +} + +/* + * Return name for specified data source. + */ +gchar *addrindex_ds_get_name( AddressDataSource *ds ) { + gchar *retVal = FALSE; + AddressInterface *iface; + + if( ds == NULL ) return retVal; + iface = ds->iface; + if( iface == NULL ) return retVal; + if( iface->getName ) { + retVal = ( iface->getName ) ( ds->rawDataSource ); + } + return retVal; +} + +/* + * Set the access flag inside the data source. + */ +void addrindex_ds_set_access_flag( AddressDataSource *ds, gboolean *value ) { + AddressInterface *iface; + + if( ds == NULL ) return; + iface = ds->iface; + if( iface == NULL ) return; + if( iface->setAccessFlag ) { + ( iface->setAccessFlag ) ( ds->rawDataSource, value ); + } +} + +/* + * Return read only flag for specified data source. + */ +gboolean addrindex_ds_get_readonly( AddressDataSource *ds ) { + AddressInterface *iface; + if( ds == NULL ) return TRUE; + iface = ds->iface; + if( iface == NULL ) return TRUE; + return iface->readOnly; +} + +/* + * Return list of all persons for specified data source. + */ +GList *addrindex_ds_get_all_persons( AddressDataSource *ds ) { + GList *retVal = NULL; + AddressInterface *iface; + + if( ds == NULL ) return retVal; + iface = ds->iface; + if( iface == NULL ) return retVal; + if( iface->getAllPersons ) { + retVal = ( iface->getAllPersons ) ( ds->rawDataSource ); + } + return retVal; +} + +/* + * Return list of all groups for specified data source. + */ +GList *addrindex_ds_get_all_groups( AddressDataSource *ds ) { + GList *retVal = NULL; + AddressInterface *iface; + + if( ds == NULL ) return retVal; + iface = ds->iface; + if( iface == NULL ) return retVal; + if( iface->getAllGroups ) { + retVal = ( iface->getAllGroups ) ( ds->rawDataSource ); + } + return retVal; +} + +/* +* End of Source. +*/ diff --git a/src/addrindex.h b/src/addrindex.h new file mode 100644 index 0000000..092ba37 --- /dev/null +++ b/src/addrindex.h @@ -0,0 +1,133 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * General functions for accessing address index file. + */ + +#ifndef __ADDRINDEX_H__ +#define __ADDRINDEX_H__ + +#include +#include +#include "addritem.h" + +#define ADDRESSBOOK_MAX_IFACE 4 +#define ADDRESSBOOK_INDEX_FILE "addrbook--index.xml" +#define ADDRESSBOOK_OLD_FILE "addressbook.xml" + +#define ADDR_DS_AUTOREG "@Auto-registered" + +typedef enum { + ADDR_IF_NONE, + ADDR_IF_BOOK, + ADDR_IF_VCARD, + ADDR_IF_JPILOT, + ADDR_IF_LDAP, + ADDR_IF_COMMON, + ADDR_IF_PERSONAL +} AddressIfType; + +typedef struct _AddressIndex AddressIndex; +struct _AddressIndex { + AddrItemObject obj; + gchar *filePath; + gchar *fileName; + gint retVal; + gboolean needsConversion; + gboolean wasConverted; + gboolean conversionError; + AddressIfType lastType; + gboolean dirtyFlag; + GList *interfaceList; +}; + +typedef struct _AddressInterface AddressInterface; +struct _AddressInterface { + AddrItemObject obj; + AddressIfType type; + gchar *name; + gchar *listTag; + gchar *itemTag; + gboolean legacyFlag; + gboolean useInterface; + gboolean haveLibrary; + gboolean readOnly; + GList *listSource; + gboolean (*getModifyFlag)( void * ); + gboolean (*getAccessFlag)( void * ); + gboolean (*getReadFlag)( void * ); + gint (*getStatusCode)( void * ); + gint (*getReadData)( void * ); + ItemFolder *(*getRootFolder)( void * ); + GList *(*getListFolder)( void * ); + GList *(*getListPerson)( void * ); + GList *(*getAllPersons)( void * ); + GList *(*getAllGroups)( void * ); + gchar *(*getName)( void * ); + void (*setAccessFlag)( void *, void * ); +}; + +typedef struct _AddressDataSource AddressDataSource; +struct _AddressDataSource { + AddrItemObject obj; + AddressIfType type; + AddressInterface *iface; + gpointer rawDataSource; +}; + +AddressIndex *addrindex_create_index (); +void addrindex_set_file_path ( AddressIndex *addrIndex, const gchar *value ); +void addrindex_set_file_name ( AddressIndex *addrIndex, const gchar *value ); +void addrindex_set_dirty ( AddressIndex *addrIndex, const gboolean value ); +GList *addrindex_get_interface_list ( AddressIndex *addrIndex ); +void addrindex_free_index ( AddressIndex *addrIndex ); +void addrindex_print_index ( AddressIndex *addrIndex, FILE *stream ); + +AddressInterface *addrindex_get_interface ( AddressIndex *addrIndex, AddressIfType ifType ); +AddressDataSource *addrindex_index_add_datasource ( AddressIndex *addrIndex, AddressIfType ifType, gpointer dataSource ); +AddressDataSource *addrindex_index_remove_datasource ( AddressIndex *addrIndex, AddressDataSource *dataSource ); +void addrindex_free_datasource ( AddressIndex *addrIndex, AddressDataSource *ds ); + +gint addrindex_read_data ( AddressIndex *addrIndex ); +gint addrindex_write_to ( AddressIndex *addrIndex, const gchar *newFile ); +gint addrindex_save_data ( AddressIndex *addrIndex ); +gint addrindex_create_new_books ( AddressIndex *addrIndex ); +gint addrindex_save_all_books ( AddressIndex *addrIndex ); +gint addrindex_create_extra_books ( AddressIndex *addrIndex ); + +gboolean addrindex_ds_get_modify_flag ( AddressDataSource *ds ); +gboolean addrindex_ds_get_access_flag ( AddressDataSource *ds ); +gboolean addrindex_ds_get_read_flag ( AddressDataSource *ds ); +gint addrindex_ds_get_status_code ( AddressDataSource *ds ); +gint addrindex_ds_read_data ( AddressDataSource *ds ); +ItemFolder *addrindex_ds_get_root_folder( AddressDataSource *ds ); +GList *addrindex_ds_get_list_folder ( AddressDataSource *ds ); +GList *addrindex_ds_get_list_person ( AddressDataSource *ds ); +gchar *addrindex_ds_get_name ( AddressDataSource *ds ); +void addrindex_ds_set_access_flag ( AddressDataSource *ds, gboolean *value ); +gboolean addrindex_ds_get_readonly ( AddressDataSource *ds ); +GList *addrindex_ds_get_all_persons ( AddressDataSource *ds ); +GList *addrindex_ds_get_all_groups ( AddressDataSource *ds ); + +#endif /* __ADDRINDEX_H__ */ + +/* +* End of Source. +*/ diff --git a/src/addritem.c b/src/addritem.c new file mode 100644 index 0000000..0fdfb01 --- /dev/null +++ b/src/addritem.c @@ -0,0 +1,989 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * General primitive address item objects. + */ + +#include +#include +#include + +#include "addritem.h" +#include "mgutils.h" + +/* +* Create new email address item. +*/ +ItemEMail *addritem_create_item_email( void ) { + ItemEMail *item; + item = g_new0( ItemEMail, 1 ); + ADDRITEM_TYPE(item) = ITEMTYPE_EMAIL; + ADDRITEM_ID(item) = NULL; + ADDRITEM_NAME(item) = NULL; + ADDRITEM_PARENT(item) = NULL; + ADDRITEM_SUBTYPE(item) = 0; + item->address = NULL; + item->remarks = NULL; + return item; +} + +/* +* Create copy of specified email address item. +*/ +ItemEMail *addritem_copy_item_email( ItemEMail *item ) { + ItemEMail *itemNew = NULL; + if( item ) { + itemNew = addritem_create_item_email(); + ADDRITEM_TYPE(itemNew) = ADDRITEM_TYPE(item); + ADDRITEM_ID(itemNew) = g_strdup( ADDRITEM_ID(item) ); + ADDRITEM_NAME(itemNew) = g_strdup( ADDRITEM_NAME(item) ); + ADDRITEM_PARENT(itemNew) = ADDRITEM_PARENT(item); + itemNew->address = g_strdup( item->address ); + itemNew->remarks = g_strdup( item->remarks ); + } + return itemNew; +} + +void addritem_email_set_id( ItemEMail *email, const gchar *value ) { + ADDRITEM_ID(email) = mgu_replace_string( ADDRITEM_ID(email), value ); +} +void addritem_email_set_alias( ItemEMail *email, const gchar *value ) { + ADDRITEM_NAME(email) = mgu_replace_string( ADDRITEM_NAME(email), value ); +} +void addritem_email_set_address( ItemEMail *email, const gchar *value ) { + email->address = mgu_replace_string( email->address, value ); +} +void addritem_email_set_remarks( ItemEMail *email, const gchar *value ) { + email->remarks = mgu_replace_string( email->remarks, value ); +} + +/* +* Free address item email. +*/ +void addritem_free_item_email( ItemEMail *item ) { + g_return_if_fail( item != NULL ); + + /* Free internal stuff */ + g_free( ADDRITEM_ID(item) ); + g_free( ADDRITEM_NAME(item) ); + g_free( item->address ); + g_free( item->remarks ); + + ADDRITEM_OBJECT(item)->type = ITEMTYPE_NONE; + ADDRITEM_ID(item) = NULL; + ADDRITEM_NAME(item) = NULL; + ADDRITEM_PARENT(item) = NULL; + ADDRITEM_SUBTYPE(item) = 0; + item->address = NULL; + item->remarks = NULL; + g_free( item ); +} + +/* +* Create new attribute. +*/ +UserAttribute *addritem_create_attribute( void ) { + UserAttribute *item; + item = g_new0( UserAttribute, 1 ); + item->uid = NULL; + item->name = NULL; + item->value = NULL; + return item; +} + +/* +* Create copy of specified attribute. +*/ +UserAttribute *addritem_copy_attribute( UserAttribute *item ) { + UserAttribute *itemNew = NULL; + if( item ) { + itemNew = addritem_create_attribute(); + itemNew->uid = g_strdup( item->uid ); + itemNew->name = g_strdup( item->name ); + itemNew->value = g_strdup( item->value ); + } + return itemNew; +} + +void addritem_attrib_set_id( UserAttribute *item, const gchar *value ) { + g_return_if_fail( item != NULL ); + item->uid = mgu_replace_string( item->uid, value ); +} +void addritem_attrib_set_name( UserAttribute *item, const gchar *value ) { + g_return_if_fail( item != NULL ); + item->name = mgu_replace_string( item->name, value ); +} +void addritem_attrib_set_value( UserAttribute *item, const gchar *value ) { + g_return_if_fail( item != NULL ); + item->value = mgu_replace_string( item->value, value ); +} + +/* +* Free user attribute. +*/ +void addritem_free_attribute( UserAttribute *item ) { + g_return_if_fail( item != NULL ); + g_free( item->uid ); + g_free( item->name ); + g_free( item->value ); + item->uid = NULL; + item->name = NULL; + item->value = NULL; + g_free( item ); +} + +/* +* Create new address book person. +*/ +ItemPerson *addritem_create_item_person( void ) { + ItemPerson *person; + person = g_new0( ItemPerson, 1 ); + ADDRITEM_TYPE(person) = ITEMTYPE_PERSON; + ADDRITEM_ID(person) = NULL; + ADDRITEM_NAME(person) = NULL; + ADDRITEM_PARENT(person) = NULL; + ADDRITEM_SUBTYPE(person) = 0; + person->firstName = NULL; + person->lastName = NULL; + person->nickName = NULL; + person->listEMail = NULL; + person->listAttrib = NULL; + person->externalID = NULL; + person->isOpened = FALSE; + return person; +} + +void addritem_person_set_id( ItemPerson *person, const gchar *value ) { + ADDRITEM_ID(person) = mgu_replace_string( ADDRITEM_ID(person), value ); +} +void addritem_person_set_first_name( ItemPerson *person, const gchar *value ) { + person->firstName = mgu_replace_string( person->firstName, value ); +} +void addritem_person_set_last_name( ItemPerson *person, const gchar *value ) { + person->lastName = mgu_replace_string( person->lastName, value ); +} +void addritem_person_set_nick_name( ItemPerson *person, const gchar *value ) { + person->nickName = mgu_replace_string( person->nickName, value ); +} +void addritem_person_set_common_name( ItemPerson *person, const gchar *value ) { + ADDRITEM_NAME(person) = mgu_replace_string( ADDRITEM_NAME(person), value ); +} +void addritem_person_set_external_id( ItemPerson *person, const gchar *value ) { + person->externalID = mgu_replace_string( person->externalID, value ); +} +void addritem_person_set_opened( ItemPerson *person, const gboolean value ) { + person->isOpened = value; +} + +/* +* Free linked list of item addresses. +*/ +void addritem_free_list_email( GList *list ) { + GList *node = list; + while( node ) { + addritem_free_item_email( node->data ); + node->data = NULL; + node = g_list_next( node ); + } + g_list_free( list ); +} + +/* +* Free linked list of attributes. +*/ +void addritem_free_list_attribute( GList *list ) { + GList *node = list; + while( node ) { + addritem_free_attribute( node->data ); + node->data = NULL; + node = g_list_next( node ); + } + g_list_free( list ); +} + +/* +* Free address person. +*/ +void addritem_free_item_person( ItemPerson *person ) { + g_return_if_fail( person != NULL ); + + /* Free internal stuff */ + g_free( ADDRITEM_ID(person) ); + g_free( ADDRITEM_NAME(person) ); + g_free( person->firstName ); + g_free( person->lastName ); + g_free( person->nickName ); + g_free( person->externalID ); + addritem_free_list_email( person->listEMail ); + addritem_free_list_attribute( person->listAttrib ); + + ADDRITEM_OBJECT(person)->type = ITEMTYPE_NONE; + ADDRITEM_ID(person) = NULL; + ADDRITEM_NAME(person) = NULL; + ADDRITEM_PARENT(person) = NULL; + ADDRITEM_SUBTYPE(person) = 0; + person->firstName = NULL; + person->lastName = NULL; + person->nickName = NULL; + person->externalID = NULL; + person->listEMail = NULL; + person->listAttrib = NULL; + + g_free( person ); +} + +/* +* Print address item. +*/ +void addritem_print_item_email( ItemEMail *item, FILE *stream ) { + g_return_if_fail( item != NULL ); + fprintf( stream, "\t\tt/id: %d : '%s'\n", ADDRITEM_TYPE(item), ADDRITEM_ID(item) ); + fprintf( stream, "\t\tsubty: %d\n", ADDRITEM_SUBTYPE(item) ); + fprintf( stream, "\t\talis: '%s'\n", ADDRITEM_NAME(item) ); + fprintf( stream, "\t\taddr: '%s'\n", item->address ); + fprintf( stream, "\t\trems: '%s'\n", item->remarks ); + fprintf( stream, "\t\t---\n" ); +} + +/* +* Print user attribute. +*/ +void addritem_print_attribute( UserAttribute *item, FILE *stream ) { + g_return_if_fail( item != NULL ); + fprintf( stream, "\t\tuid : '%s'\n", item->uid ); + fprintf( stream, "\t\tname : '%s'\n", item->name ); + fprintf( stream, "\t\tvalue: '%s'\n", item->value ); + fprintf( stream, "\t\t---\n" ); +} + +/* +* Print person item. +*/ +void addritem_print_item_person( ItemPerson *person, FILE *stream ) { + GList *node; + g_return_if_fail( person != NULL ); + fprintf( stream, "Person:\n" ); + fprintf( stream, "\tt/uid: %d : '%s'\n", ADDRITEM_TYPE(person), ADDRITEM_ID(person) ); + fprintf( stream, "\tsubty: %d\n", ADDRITEM_SUBTYPE(person) ); + fprintf( stream, "\tcommn: '%s'\n", ADDRITEM_NAME(person) ); + fprintf( stream, "\tfirst: '%s'\n", person->firstName ); + fprintf( stream, "\tlast : '%s'\n", person->lastName ); + fprintf( stream, "\tnick : '%s'\n", person->nickName ); + fprintf( stream, "\textID: '%s'\n", person->externalID ); + fprintf( stream, "\teMail:\n" ); + fprintf( stream, "\t---\n" ); + node = person->listEMail; + while( node ) { + addritem_print_item_email( node->data, stream ); + node = g_list_next( node ); + } + fprintf( stream, "\tuAttr:\n" ); + fprintf( stream, "\t---\n" ); + node = person->listAttrib; + while( node ) { + addritem_print_attribute( node->data, stream ); + node = g_list_next( node ); + } + fprintf( stream, "\t===\n" ); +} + +/* +* Add EMail address to person. +* return: TRUE if item added. +*/ +gboolean addritem_person_add_email( ItemPerson *person, ItemEMail *email ) { + GList *node; + + g_return_val_if_fail( person != NULL, FALSE ); + g_return_val_if_fail( email != NULL, FALSE ); + + node = person->listEMail; + while( node ) { + if( node->data == email ) return FALSE; + node = g_list_next( node ); + } + person->listEMail = g_list_append( person->listEMail, email ); + ADDRITEM_PARENT(email) = ADDRITEM_OBJECT(person); + return TRUE; +} + +/* +* Return email object with specified ID. +* param: person Person object. +* eid EMail ID. +* return: EMail object, or NULL if not found. +*/ +ItemEMail *addritem_person_get_email( ItemPerson *person, const gchar *eid ) { + ItemEMail *email = NULL; + GList *node; + + g_return_val_if_fail( person != NULL, NULL ); + if( eid == NULL || *eid == '\0' ) return NULL; + + /* Look for email */ + node = person->listEMail; + while( node ) { + AddrItemObject *objE = node->data; + gchar *ide = ADDRITEM_ID(objE); + if( ide ) { + if( strcmp( ide, eid ) == 0 ) { + email = ( ItemEMail * ) objE; + } + } + node = g_list_next( node ); + } + return email; +} + +/* +* Remove email address for specified person. +* param: person Person object. +* eid EMail ID. +* return: EMail object, or NULL if not found. Note that object should still be freed. +*/ +ItemEMail *addritem_person_remove_email_id( ItemPerson *person, const gchar *eid ) { + ItemEMail *email = NULL; + GList *node; + + g_return_val_if_fail( person != NULL, NULL ); + if( eid == NULL || *eid == '\0' ) return NULL; + + /* Look for email */ + node = person->listEMail; + while( node ) { + AddrItemObject *objE = node->data; + gchar *ide = ADDRITEM_ID(objE); + if( ide ) { + if( strcmp( ide, eid ) == 0 ) { + email = ( ItemEMail * ) objE; + } + } + node = g_list_next( node ); + } + + if( email ) { + /* Remove email from person's address list */ + if( person->listEMail ) { + person->listEMail = g_list_remove( person->listEMail, email ); + } + /* Unlink reference to person. */ + ADDRITEM_PARENT(email) = NULL; + } + return email; +} + +/* +* Remove email address for specified. +* param: person Person. +* email EMail to remove. +* return: EMail object, or NULL if not found. Note that object should still be freed. +*/ +ItemEMail *addritem_person_remove_email( ItemPerson *person, ItemEMail *email ) { + gboolean found = FALSE; + GList *node; + + g_return_val_if_fail( person != NULL, NULL ); + if( email == NULL ) return NULL; + + /* Look for email */ + node = person->listEMail; + while( node ) { + if( node-> data == email ) { + found = TRUE; + break; + } + node = g_list_next( node ); + } + + if( found ) { + /* Remove email from person's address list */ + if( person->listEMail ) { + person->listEMail = g_list_remove( person->listEMail, email ); + } + /* Unlink reference to person. */ + ADDRITEM_PARENT(email) = NULL; + return email; + } + return NULL; +} + +/* +* Add user attribute to person. +* return: TRUE if item added. +*/ +void addritem_person_add_attribute( ItemPerson *person, UserAttribute *attrib ) { + g_return_if_fail( person != NULL ); + person->listAttrib = g_list_append( person->listAttrib, attrib ); +} + +/* +* Return attribute with specified ID. +* param: person Person object. +* aid Attribute ID. +* return: UserAttribute object, or NULL if not found. Note that object should still be freed. +*/ +UserAttribute *addritem_person_get_attribute( ItemPerson *person, const gchar *aid ) { + UserAttribute *attrib = NULL; + GList *node; + + g_return_val_if_fail( person != NULL, NULL ); + if( aid == NULL || *aid == '\0' ) return NULL; + + /* Look for attribute */ + node = person->listAttrib; + while( node ) { + UserAttribute *attr = node->data; + gchar *ida = attr->uid; + if( ida ) { + if( strcmp( ida, aid ) == 0 ) { + attrib = attr; + } + } + node = g_list_next( node ); + } + return attrib; +} + +/* +* Remove attribute from person. +* param: person Person object. +* aid Attribute ID. +* return: UserAttribute object, or NULL if not found. Note that object should still be freed. +*/ +UserAttribute *addritem_person_remove_attrib_id( ItemPerson *person, const gchar *aid ) { + UserAttribute *attrib = NULL; + GList *node; + + g_return_val_if_fail( person != NULL, NULL ); + if( aid == NULL || *aid == '\0' ) return NULL; + + /* Look for attribute */ + node = person->listAttrib; + while( node ) { + UserAttribute *attr = node->data; + gchar *ida = attr->uid; + if( ida ) { + if( strcmp( ida, aid ) == 0 ) { + attrib = attr; + } + } + node = g_list_next( node ); + } + + /* Remove email from person's address list */ + if( person->listAttrib ) { + person->listAttrib = g_list_remove( person->listAttrib, attrib ); + } + return attrib; +} + +/* +* Remove attribute from person. +* param: person Person. +* attrib Attribute to remove. +* return: UserAttribute object. Note that object should still be freed. +*/ +UserAttribute *addritem_person_remove_attribute( ItemPerson *person, UserAttribute *attrib ) { + gboolean found = FALSE; + GList *node; + + g_return_val_if_fail( person != NULL, NULL ); + if( attrib == NULL ) return NULL; + + /* Look for attribute */ + node = person->listAttrib; + while( node ) { + if( node-> data == attrib ) { + found = TRUE; + break; + } + node = g_list_next( node ); + } + + if( found ) { + /* Remove attribute */ + if( person->listAttrib ) { + person->listAttrib = g_list_remove( person->listAttrib, attrib ); + } + } + return attrib; +} + +/* +* Create new address book group. +*/ +ItemGroup *addritem_create_item_group( void ) { + ItemGroup *group; + + group = g_new0( ItemGroup, 1 ); + ADDRITEM_TYPE(group) = ITEMTYPE_GROUP; + ADDRITEM_ID(group) = NULL; + ADDRITEM_NAME(group) = NULL; + ADDRITEM_PARENT(group) = NULL; + ADDRITEM_SUBTYPE(group) = 0; + group->remarks = NULL; + group->listEMail = NULL; + return group; +} + +/* +* Specify name to be used. +*/ +void addritem_group_set_id( ItemGroup *group, const gchar *value ) { + ADDRITEM_ID(group) = mgu_replace_string( ADDRITEM_ID(group), value ); +} +void addritem_group_set_name( ItemGroup *group, const gchar *value ) { + ADDRITEM_NAME(group) = mgu_replace_string( ADDRITEM_NAME(group), value ); +} +void addritem_group_set_remarks( ItemGroup *group, const gchar *value ) { + group->remarks = mgu_replace_string( group->remarks, value ); +} + +/* +* Free address group. +*/ +void addritem_free_item_group( ItemGroup *group ) { + g_return_if_fail( group != NULL ); + + /* Free internal stuff */ + g_free( ADDRITEM_ID(group) ); + g_free( ADDRITEM_NAME(group) ); + g_free( group->remarks ); + mgu_clear_list( group->listEMail ); + g_list_free( group->listEMail ); + + ADDRITEM_TYPE(group) = ITEMTYPE_NONE; + ADDRITEM_ID(group) = NULL; + ADDRITEM_NAME(group) = NULL; + ADDRITEM_PARENT(group) = NULL; + ADDRITEM_SUBTYPE(group) = 0; + group->remarks = NULL; + group->listEMail = NULL; + + g_free( group ); +} + +/* +* Add EMail address to group. +* return: TRUE if item added. +*/ +gboolean addritem_group_add_email( ItemGroup *group, ItemEMail *email ) { + GList *node; + + g_return_val_if_fail( group != NULL, FALSE ); + g_return_val_if_fail( email != NULL, FALSE ); + + node = group->listEMail; + while( node ) { + if( node->data == email ) return FALSE; + node = g_list_next( node ); + } + group->listEMail = g_list_append( group->listEMail, email ); + return TRUE; +} + +/* +* Remove email address for specified group. +* param: group Group from which to remove address. +* email EMail to remove +* return: EMail object, or NULL if email not found in group. Note that this object is +* referenced (linked) to a group and should *NOT* be freed. This object should only be +* freed after removing from a person. +*/ +ItemEMail *addritem_group_remove_email( ItemGroup *group, ItemEMail *email ) { + if( group && email ) { + GList *node = group->listEMail; + while( node ) { + if( node->data == email ) { + group->listEMail = g_list_remove( group->listEMail, email ); + return email; + } + node = g_list_next( node ); + } + } + return NULL; +} + +/* +* Remove email address for specified group and ID. +* param: group Group from which to remove address. +* eid EMail ID. +* return: EMail object, or NULL if email not found in group. Note that this object is +* referenced (linked) to a group and should *NOT* be freed. This object should only be +* freed after removing from a person. +*/ +ItemEMail *addritem_group_remove_email_id( ItemGroup *group, const gchar *eid ) { + if( group ) { + GList *node = group->listEMail; + while( node ) { + ItemEMail *email = ( ItemEMail * ) node->data; + if( strcmp( ADDRITEM_ID( email ), eid ) == 0 ) { + group->listEMail = g_list_remove( group->listEMail, email ); + return email; + } + node = g_list_next( node ); + } + } + return NULL; +} + +/* +* Print address group item. +*/ +void addritem_print_item_group( ItemGroup *group, FILE *stream ) { + GList *node; + ItemPerson *person; + ItemEMail *item; + g_return_if_fail( group != NULL ); + fprintf( stream, "Group:\n" ); + fprintf( stream, "\tt/u: %d : '%s'\n", ADDRITEM_TYPE(group), ADDRITEM_ID(group) ); + fprintf( stream, "\tsub: %d\n", ADDRITEM_SUBTYPE(group) ); + fprintf( stream, "\tgrp: '%s'\n", ADDRITEM_NAME(group) ); + fprintf( stream, "\trem: '%s'\n", group->remarks ); + fprintf( stream, "\t---\n" ); + node = group->listEMail; + while( node ) { + item = node->data; + person = ( ItemPerson * ) ADDRITEM_PARENT(item); + if( person ) { + fprintf( stream, "\t\tpid : '%s'\n", ADDRITEM_ID(person) ); + fprintf( stream, "\t\tcomn: '%s'\n", ADDRITEM_NAME(person) ); + } + else { + fprintf( stream, "\t\tpid : ???\n" ); + fprintf( stream, "\t\tcomn: ???\n" ); + } + addritem_print_item_email( item, stream ); + node = g_list_next( node ); + } + fprintf( stream, "\t***\n" ); +} + +/* +* Create new address folder. +*/ +ItemFolder *addritem_create_item_folder( void ) { + ItemFolder *folder; + folder = g_new0( ItemFolder, 1 ); + ADDRITEM_TYPE(folder) = ITEMTYPE_FOLDER; + ADDRITEM_ID(folder) = NULL; + ADDRITEM_NAME(folder) = NULL; + ADDRITEM_PARENT(folder) = NULL; + ADDRITEM_SUBTYPE(folder) = 0; + folder->remarks = NULL; + folder->isRoot = FALSE; + folder->listItems = NULL; + folder->listFolder = NULL; + folder->listPerson = NULL; + folder->listGroup = NULL; + folder->userData = NULL; + return folder; +} + +/* +* Specify name to be used. +*/ +void addritem_folder_set_id( ItemFolder *folder, const gchar *value ) { + ADDRITEM_ID(folder) = mgu_replace_string( ADDRITEM_ID(folder), value ); +} +void addritem_folder_set_name( ItemFolder *folder, const gchar *value ) { + ADDRITEM_NAME(folder) = mgu_replace_string( ADDRITEM_NAME(folder), value ); +} +void addritem_folder_set_remarks( ItemFolder *folder, const gchar *value ) { + folder->remarks = mgu_replace_string( folder->remarks, value ); +} + +/* +* Free address folder. Note: this does not free up the lists of children +* (folders, groups and person). This should be done prior to calling this +* function. +*/ +void addritem_free_item_folder( ItemFolder *folder ) { + g_return_if_fail( folder != NULL ); + + /* Free internal stuff */ + g_free( ADDRITEM_ID(folder) ); + g_free( ADDRITEM_NAME(folder) ); + g_free( folder->remarks ); + mgu_clear_list( folder->listItems ); + g_list_free( folder->listItems ); + + ADDRITEM_TYPE(folder) = ITEMTYPE_NONE; + ADDRITEM_ID(folder) = NULL; + ADDRITEM_NAME(folder) = NULL; + ADDRITEM_PARENT(folder) = NULL; + ADDRITEM_SUBTYPE(folder) = 0; + folder->isRoot = FALSE; + folder->remarks = NULL; + folder->listItems = NULL; + folder->listFolder = NULL; + folder->listGroup = NULL; + folder->listPerson = NULL; + + g_free( folder->userData ); + folder->userData = NULL; + + g_free( folder ); +} + +/* +* Free up folders recursively. Note: this does not free up the lists of children +* (folders, groups and person). This should be done prior to calling this +* function. +*/ +void addritem_free_item_folder_recurse( ItemFolder *parent ) { + GList *node = parent->listFolder; + + while( node ) { + ItemFolder *folder = node->data; + addritem_free_item_folder_recurse( folder ); + node = g_list_next( node ); + } + g_list_free( parent->listPerson ); + g_list_free( parent->listGroup ); + g_list_free( parent->listFolder ); + parent->listPerson = NULL; + parent->listGroup = NULL; + parent->listFolder = NULL; +} + +/* +* Free up list of person in specified folder. +*/ +void addritem_folder_free_person( ItemFolder *folder ) { + GList *node; + + g_return_if_fail( folder != NULL ); + + /* Free up folder of persons. */ + node = folder->listPerson; + while( node ) { + ItemPerson *person = node->data; + addritem_free_item_person( person ); + person = NULL; + node = g_list_next( node ); + } +} + +/* +* Add person into folder. +* return: TRUE if person added. +*/ +gboolean addritem_folder_add_person( ItemFolder *folder, ItemPerson *item ) { + g_return_val_if_fail( folder != NULL, FALSE ); + g_return_val_if_fail( item != NULL, FALSE ); + + folder->listPerson = g_list_append( folder->listPerson, item ); + ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder); + return TRUE; +} + +/* +* Add folder into folder. +* return: TRUE if folder added. +*/ +gboolean addritem_folder_add_folder( ItemFolder *folder, ItemFolder *item ) { + g_return_val_if_fail( folder != NULL, FALSE ); + g_return_val_if_fail( item != NULL, FALSE ); + + folder->listFolder = g_list_append( folder->listFolder, item ); + ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder); + return TRUE; +} + +/* +* Add group into folder. +* return: TRUE if folder added. +*/ +gboolean addritem_folder_add_group( ItemFolder *folder, ItemGroup *item ) { + g_return_val_if_fail( folder != NULL, FALSE ); + g_return_val_if_fail( item != NULL, FALSE ); + + folder->listGroup = g_list_append( folder->listGroup, item ); + ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder); + return TRUE; +} + +/* +* Print address folder item. +*/ +void addritem_print_item_folder( ItemFolder *folder, FILE *stream ) { + GList *node; + /* ItemPerson *person; */ + ItemFolder *parent; + + g_return_if_fail( folder != NULL ); + + fprintf( stream, "Folder:\n" ); + fprintf( stream, "\tt/u: %d : '%s'\n", ADDRITEM_TYPE(folder), ADDRITEM_ID(folder) ); + fprintf( stream, "\tsub: %d\n", ADDRITEM_SUBTYPE(folder) ); + fprintf( stream, "\tnam: '%s'\n", ADDRITEM_NAME(folder) ); + fprintf( stream, "\trem: '%s'\n", folder->remarks ); + fprintf( stream, "\t---\n" ); + parent = ( ItemFolder * ) ADDRITEM_PARENT(folder); + if( parent ) { + fprintf( stream, "\tpar: %s : %s\n", ADDRITEM_ID(parent), ADDRITEM_NAME(parent) ); + } + else { + fprintf( stream, "\tpar: NULL\n" ); + } + node = folder->listFolder; + while( node ) { + AddrItemObject *aio = node->data; + if( aio ) { + if( aio->type == ITEMTYPE_FOLDER ) { + ItemFolder *item = ( ItemFolder * ) aio; + addritem_print_item_folder( item, stream ); + } + } + else { + fprintf( stream, "\t\tpid : ???\n" ); + } + + node = g_list_next( node ); + } + + node = folder->listPerson; + while( node ) { + AddrItemObject *aio = node->data; + if( aio ) { + if( aio->type == ITEMTYPE_PERSON ) { + ItemPerson *item = ( ItemPerson * ) aio; + addritem_print_item_person( item, stream ); + } + } + else { + fprintf( stream, "\t\tpid : ???\n" ); + } + + node = g_list_next( node ); + } + + node = folder->listGroup; + while( node ) { + AddrItemObject *aio = node->data; + if( aio ) { + if( aio->type == ITEMTYPE_GROUP ) { + ItemGroup *item = ( ItemGroup * ) aio; + addritem_print_item_group( item, stream ); + } + } + else { + fprintf( stream, "\t\tpid : ???\n" ); + } + node = g_list_next( node ); + } + fprintf( stream, "\t###\n" ); +} + +/* +* Return link list of persons for specified folder. Note that the list contains +* references to items and should be g_free() when done. Do *NOT* attempt to use the +* addritem_free_xxx() functions... this will destroy the addressbook data! +* Return: List of items, or NULL if none. +*/ +GList *addritem_folder_get_person_list( ItemFolder *folder ) { + GList *list = NULL; + GList *node = NULL; + + g_return_val_if_fail( folder != NULL, NULL ); + + node = folder->listPerson; + while( node ) { + ItemPerson *person = node->data; + list = g_list_append( list, person ); + node = g_list_next( node ); + } + return list; +} + +/* +* Return link list of groups for specified folder. Note that the list contains +* references to items and should be g_free() when done. Do *NOT* attempt to use the +* addritem_free_xxx() functions... this will destroy the addressbook data! +* Return: List of items, or NULL if none. +*/ +GList *addritem_folder_get_group_list( ItemFolder *folder ) { + GList *list = NULL; + GList *node = NULL; + + g_return_val_if_fail( folder != NULL, NULL ); + + node = folder->listGroup; + while( node ) { + ItemGroup *group = node->data; + list = g_list_append( list, group ); + node = g_list_next( node ); + } + return list; +} + +/* +* Move person's email item. +* param: person Person. +* itemMove Item to move. +* itemTarget Target item before which to move item. +*/ + +ItemEMail *addritem_move_email_before( ItemPerson *person, ItemEMail *itemMove, ItemEMail *itemTarget ) { + gint posT, posM; + + g_return_val_if_fail( person != NULL, NULL ); + + if( itemTarget == NULL ) return NULL; + if( itemMove == NULL ) return NULL; + if( itemMove == itemTarget ) return itemMove; + + posT = g_list_index( person->listEMail, itemTarget ); + if( posT < 0 ) return NULL; + posM = g_list_index( person->listEMail, itemMove ); + if( posM < 0 ) return NULL; + person->listEMail = g_list_remove( person->listEMail, itemMove ); + person->listEMail = g_list_insert( person->listEMail, itemMove, posT ); + return itemMove; +} + +/* +* Move person's email item. +* param: person Person. +* itemMove Item to move. +* itemTarget Target item after which to move item. +*/ +ItemEMail *addritem_move_email_after( ItemPerson *person, ItemEMail *itemMove, ItemEMail *itemTarget ) { + gint posT, posM; + + g_return_val_if_fail( person != NULL, NULL ); + + if( itemTarget == NULL ) return NULL; + if( itemMove == NULL ) return NULL; + if( itemMove == itemTarget ) return itemMove; + + posT = g_list_index( person->listEMail, itemTarget ); + if( posT < 0 ) return NULL; + posM = g_list_index( person->listEMail, itemMove ); + if( posM < 0 ) return NULL; + person->listEMail = g_list_remove( person->listEMail, itemMove ); + person->listEMail = g_list_insert( person->listEMail, itemMove, 1+posT ); + return itemMove; +} + +/* +* End of Source. +*/ diff --git a/src/addritem.h b/src/addritem.h new file mode 100644 index 0000000..80cbdaa --- /dev/null +++ b/src/addritem.h @@ -0,0 +1,174 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2001 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Address item data. + */ + +#ifndef __ADDRITEM_H__ +#define __ADDRITEM_H__ + +#include +#include + +#define ADDRITEM_OBJECT(obj) ((AddrItemObject *)obj) +#define ADDRITEM_TYPE(obj) (ADDRITEM_OBJECT(obj)->type) +#define ADDRITEM_NAME(obj) (ADDRITEM_OBJECT(obj)->name) +#define ADDRITEM_ID(obj) (ADDRITEM_OBJECT(obj)->uid) +#define ADDRITEM_PARENT(obj) (ADDRITEM_OBJECT(obj)->parent) +#define ADDRITEM_SUBTYPE(obj) (ADDRITEM_OBJECT(obj)->subType) + +typedef enum { + ITEMTYPE_NONE, + ITEMTYPE_PERSON, + ITEMTYPE_EMAIL, + ITEMTYPE_FOLDER, + ITEMTYPE_GROUP, + ITEMTYPE_INDEX, + ITEMTYPE_INTERFACE, + ITEMTYPE_DATASOURCE +} ItemObjectType; + +typedef struct _AddrItemObject AddrItemObject; +struct _AddrItemObject { + ItemObjectType type; + gchar *name; + gchar *uid; + AddrItemObject *parent; + gint subType; +}; + +typedef struct _ItemPerson ItemPerson; +struct _ItemPerson { + AddrItemObject obj; + gchar *firstName; + gchar *lastName; + gchar *nickName; + gchar *externalID; + GList *listEMail; + GList *listAttrib; + gboolean isOpened; +}; + +typedef struct _ItemEMail ItemEMail; +struct _ItemEMail { + AddrItemObject obj; + gchar *address; + gchar *remarks; +}; + +typedef struct _UserAttribute UserAttribute; +struct _UserAttribute { + gchar *uid; + gchar *name; + gchar *value; +}; + +typedef struct _ItemFolder ItemFolder; +struct _ItemFolder { + AddrItemObject obj; + gchar *remarks; + gboolean isRoot; + GList *listItems; + GList *listFolder; + GList *listPerson; + GList *listGroup; + gpointer userData; +}; + +typedef struct _ItemGroup ItemGroup; +struct _ItemGroup { + AddrItemObject obj; + gchar *remarks; + GList *listEMail; +}; + +/* Function prototypes */ +ItemEMail *addritem_create_item_email ( void ); +ItemEMail *addritem_copy_item_email ( ItemEMail *item ); +void addritem_email_set_id ( ItemEMail *email, const gchar *value ); +void addritem_email_set_alias ( ItemEMail *email, const gchar *value ); +void addritem_email_set_address ( ItemEMail *email, const gchar *value ); +void addritem_email_set_remarks ( ItemEMail *email, const gchar *value ); +void addritem_free_item_email ( ItemEMail *item ); + +UserAttribute *addritem_create_attribute( void ); +UserAttribute *addritem_copy_attribute ( UserAttribute *item ); +void addritem_attrib_set_id ( UserAttribute *item, const gchar *value ); +void addritem_attrib_set_name ( UserAttribute *item, const gchar *value ); +void addritem_attrib_set_value ( UserAttribute *item, const gchar *value ); +void addritem_free_attribute ( UserAttribute *item ); + +ItemPerson *addritem_create_item_person ( void ); +void addritem_person_set_id ( ItemPerson *person, const gchar *value ); +void addritem_person_set_first_name ( ItemPerson *person, const gchar *value ); +void addritem_person_set_last_name ( ItemPerson *person, const gchar *value ); +void addritem_person_set_nick_name ( ItemPerson *person, const gchar *value ); +void addritem_person_set_common_name ( ItemPerson *person, const gchar *value ); +void addritem_person_set_external_id ( ItemPerson *person, const gchar *value ); +void addritem_person_set_opened ( ItemPerson *person, const gboolean value ); +void addritem_free_item_person ( ItemPerson *person ); +void addritem_free_list_email ( GList *list ); +void addritem_free_list_attribute ( GList *list ); + +ItemGroup *addritem_create_item_group ( void ); +void addritem_free_item_group ( ItemGroup *group ); +void addritem_print ( ItemGroup *group, FILE *stream ); +void addritem_group_set_id ( ItemGroup *group, const gchar *value ); +void addritem_group_set_name ( ItemGroup *group, const gchar *value ); +void addritem_group_set_remarks ( ItemGroup *group, const gchar *value ); + +void addritem_print_item_email ( ItemEMail *item, FILE *stream ); +void addritem_print_attribute ( UserAttribute *item, FILE *stream ); +void addritem_print_item_person ( ItemPerson *person, FILE *stream ); +void addritem_print_item_group ( ItemGroup *group, FILE *stream ); +void addritem_print_item_folder ( ItemFolder *folder, FILE *stream ); + +gboolean addritem_person_add_email ( ItemPerson *person, ItemEMail *email ); +ItemEMail *addritem_person_get_email ( ItemPerson *person, const gchar *eid ); +ItemEMail *addritem_person_remove_email_id ( ItemPerson *person, const gchar *eid ); +ItemEMail *addritem_person_remove_email ( ItemPerson *person, ItemEMail *email ); + +void addritem_person_add_attribute ( ItemPerson *person, UserAttribute *attrib ); +UserAttribute *addritem_person_get_attribute ( ItemPerson *person, const gchar *aid ); +UserAttribute *addritem_person_remove_attrib_id ( ItemPerson *person, const gchar *aid ); +UserAttribute *addritem_person_remove_attribute ( ItemPerson *person, UserAttribute *attrib ); + +ItemFolder *addritem_create_item_folder ( void ); +void addritem_folder_set_id ( ItemFolder *folder, const gchar *value ); +void addritem_folder_set_name ( ItemFolder *folder, const gchar *value ); +void addritem_folder_set_remarks ( ItemFolder *folder, const gchar *value ); +void addritem_free_item_folder ( ItemFolder *folder ); +void addritem_free_item_folder_recurse ( ItemFolder *parent ); + +gboolean addritem_group_add_email ( ItemGroup *group, ItemEMail *email ); +ItemEMail *addritem_group_remove_email ( ItemGroup *group, ItemEMail *email ); +ItemEMail *addritem_group_remove_email_id ( ItemGroup *group, const gchar *eid ); + +gboolean addritem_folder_add_person ( ItemFolder *folder, ItemPerson *item ); +gboolean addritem_folder_add_folder ( ItemFolder *folder, ItemFolder *item ); +gboolean addritem_folder_add_group ( ItemFolder *folder, ItemGroup *item ); +void addritem_folder_free_person ( ItemFolder *folder ); +GList *addritem_folder_get_person_list ( ItemFolder *folder ); +GList *addritem_folder_get_group_list ( ItemFolder *folder ); + +ItemEMail *addritem_move_email_before ( ItemPerson *person, ItemEMail *itemMove, ItemEMail *itemTarget ); +ItemEMail *addritem_move_email_after ( ItemPerson *person, ItemEMail *itemMove, ItemEMail *itemTarget ); + +#endif /* __ADDRITEM_H__ */ diff --git a/src/alertpanel.c b/src/alertpanel.c new file mode 100644 index 0000000..723125e --- /dev/null +++ b/src/alertpanel.c @@ -0,0 +1,414 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include + +#include "alertpanel.h" +#include "mainwindow.h" +#include "manage_window.h" +#include "utils.h" +#include "gtkutils.h" +#include "inc.h" +#include "prefs_common.h" + +#define ALERT_PANEL_WIDTH 380 +#define TITLE_HEIGHT 72 +#define MESSAGE_HEIGHT 62 + +static gboolean alertpanel_is_open = FALSE; +static AlertValue value; + +static GtkWidget *dialog; + +static void alertpanel_show (void); +static void alertpanel_create (const gchar *title, + const gchar *message, + AlertType type, + AlertValue default_value, + gboolean can_disable, + const gchar *button1_label, + const gchar *button2_label, + const gchar *button3_label); + +static void alertpanel_button_toggled (GtkToggleButton *button, + gpointer data); +static void alertpanel_button_clicked (GtkWidget *widget, + gpointer data); +static gint alertpanel_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean alertpanel_close (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gint alertpanel_focus_out (GtkWidget *widget, + GdkEventFocus *event, + gpointer data); + +AlertValue alertpanel_full(const gchar *title, const gchar *message, + AlertType type, AlertValue default_value, + gboolean can_disable, + const gchar *button1_label, + const gchar *button2_label, + const gchar *button3_label) +{ + if (alertpanel_is_open) + return -1; + else + alertpanel_is_open = TRUE; + + alertpanel_create(title, message, type, default_value, can_disable, + button1_label, button2_label, button3_label); + alertpanel_show(); + + debug_print("return value = %d\n", value); + return value; +} + +AlertValue alertpanel(const gchar *title, + const gchar *message, + const gchar *button1_label, + const gchar *button2_label, + const gchar *button3_label) +{ + return alertpanel_full(title, message, ALERT_QUESTION, G_ALERTDEFAULT, + FALSE, + button1_label, button2_label, button3_label); +} + +void alertpanel_message(const gchar *title, const gchar *message, + AlertType type) +{ + if (alertpanel_is_open) + return; + else + alertpanel_is_open = TRUE; + + alertpanel_create(title, message, type, G_ALERTDEFAULT, FALSE, + NULL, NULL, NULL); + alertpanel_show(); +} + +AlertValue alertpanel_message_with_disable(const gchar *title, + const gchar *message, + AlertType type) +{ + if (alertpanel_is_open) + return 0; + else + alertpanel_is_open = TRUE; + + alertpanel_create(title, message, type, G_ALERTDEFAULT, TRUE, + NULL, NULL, NULL); + alertpanel_show(); + + return value; +} + +void alertpanel_notice(const gchar *format, ...) +{ + va_list args; + gchar buf[256]; + + va_start(args, format); + g_vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + strretchomp(buf); + + alertpanel_message(_("Notice"), buf, ALERT_NOTICE); +} + +void alertpanel_warning(const gchar *format, ...) +{ + va_list args; + gchar buf[256]; + + va_start(args, format); + g_vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + strretchomp(buf); + + alertpanel_message(_("Warning"), buf, ALERT_WARNING); +} + +void alertpanel_error(const gchar *format, ...) +{ + va_list args; + gchar buf[256]; + + va_start(args, format); + g_vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + strretchomp(buf); + + alertpanel_message(_("Error"), buf, ALERT_ERROR); +} + +static void alertpanel_show(void) +{ + gint x, y, w, h, sx, sy; + value = G_ALERTWAIT; + + inc_lock(); + + sx = gdk_screen_width(); + sy = gdk_screen_height(); + gdk_window_get_origin(dialog->window, &x, &y); + w = dialog->allocation.width; + h = dialog->allocation.height; + if (x < 0 || y < 0 || x + w > sx || y + h > sy) { + debug_print("sx, sy, x, y, w, h = %d, %d, %d, %d, %d, %d\n", + sx, sy, x, y, w, h); + debug_print("alert dialog position out of range\n"); + gtk_window_set_position(GTK_WINDOW(dialog), + GTK_WIN_POS_CENTER_ALWAYS); + } + + while ((value & G_ALERT_VALUE_MASK) == G_ALERTWAIT) + gtk_main_iteration(); + + gtk_widget_destroy(dialog); + GTK_EVENTS_FLUSH(); + + alertpanel_is_open = FALSE; + inc_unlock(); +} + +static void alertpanel_create(const gchar *title, + const gchar *message, + AlertType type, + AlertValue default_value, + gboolean can_disable, + const gchar *button1_label, + const gchar *button2_label, + const gchar *button3_label) +{ + static PangoFontDescription *font_desc; + GtkWidget *image; + GtkWidget *label; + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *disable_chkbtn; + GtkWidget *confirm_area; + GtkWidget *button1; + GtkWidget *button2; + GtkWidget *button3; + const gchar *label2; + const gchar *label3; + gint spacing; + + debug_print(_("Creating alert panel dialog...\n")); + + dialog = gtk_dialog_new(); + gtk_window_set_title(GTK_WINDOW(dialog), title); + gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, FALSE); + gtk_window_set_position(GTK_WINDOW(dialog), + GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + manage_window_set_transient(GTK_WINDOW(dialog)); + gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE); + gtk_widget_realize(dialog); + g_signal_connect(G_OBJECT(dialog), "delete_event", + G_CALLBACK(alertpanel_deleted), + (gpointer)G_ALERTCANCEL); + g_signal_connect(G_OBJECT(dialog), "key_press_event", + G_CALLBACK(alertpanel_close), + (gpointer)G_ALERTCANCEL); + g_signal_connect(G_OBJECT(dialog), "focus_out_event", + G_CALLBACK(alertpanel_focus_out), NULL); + + /* for title icon, label and message */ + spacing = 12 * gtkut_get_dpi_multiplier(); + hbox = gtk_hbox_new(FALSE, spacing); + gtk_container_set_border_width(GTK_CONTAINER(hbox), spacing); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), + hbox, FALSE, FALSE, 0); + + /* title icon */ + switch (type) { + case ALERT_QUESTION: + image = gtk_image_new_from_stock + (GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG); + break; + case ALERT_WARNING: + image = gtk_image_new_from_stock + (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG); + break; + case ALERT_ERROR: + image = gtk_image_new_from_stock + (GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_DIALOG); + break; + case ALERT_NOTICE: + default: + image = gtk_image_new_from_stock + (GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG); + break; + } + gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.0); + gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); + + /* for title and message */ + vbox = gtk_vbox_new(FALSE, spacing); + gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0); + + label = gtk_label_new(title); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + if (!font_desc) { + gint size; + + size = pango_font_description_get_size + (label->style->font_desc); + font_desc = pango_font_description_new(); + pango_font_description_set_weight + (font_desc, PANGO_WEIGHT_BOLD); + pango_font_description_set_size + (font_desc, size * PANGO_SCALE_LARGE); + } + if (font_desc) + gtk_widget_modify_font(label, font_desc); + + /* message label */ + label = gtk_label_new(message); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + GTK_WIDGET_UNSET_FLAGS(label, GTK_CAN_FOCUS); +#ifdef G_OS_WIN32 + { + GtkStyle *style; + style = gtk_widget_get_style(dialog); + gtk_widget_modify_base(label, GTK_STATE_ACTIVE, + &style->base[GTK_STATE_SELECTED]); + gtk_widget_modify_text(label, GTK_STATE_ACTIVE, + &style->text[GTK_STATE_SELECTED]); + } +#endif + + if (can_disable) { + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, + FALSE, FALSE, 0); + + disable_chkbtn = gtk_check_button_new_with_label + (_("Show this message next time")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(disable_chkbtn), + TRUE); + gtk_box_pack_start(GTK_BOX(hbox), disable_chkbtn, + FALSE, FALSE, spacing); + g_signal_connect(G_OBJECT(disable_chkbtn), "toggled", + G_CALLBACK(alertpanel_button_toggled), + GUINT_TO_POINTER(G_ALERTDISABLE)); + } + + /* for button(s) */ + if (!button1_label) + button1_label = GTK_STOCK_OK; + label2 = button2_label; + label3 = button3_label; + if (label2 && *label2 == '+') label2++; + if (label3 && *label3 == '+') label3++; + + gtkut_stock_button_set_create(&confirm_area, + &button1, button1_label, + button2_label ? &button2 : NULL, label2, + button3_label ? &button3 : NULL, label3); + + gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area), + confirm_area, FALSE, FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5 * gtkut_get_dpi_multiplier()); + gtk_widget_grab_default(button1); + gtk_widget_grab_focus(button1); + if (button2_label && + (default_value == G_ALERTALTERNATE || *button2_label == '+')) { + gtk_widget_grab_default(button2); + gtk_widget_grab_focus(button2); + } + if (button3_label && + (default_value == G_ALERTOTHER || *button3_label == '+')) { + gtk_widget_grab_default(button3); + gtk_widget_grab_focus(button3); + } + + g_signal_connect(G_OBJECT(button1), "clicked", + G_CALLBACK(alertpanel_button_clicked), + GUINT_TO_POINTER(G_ALERTDEFAULT)); + if (button2_label) + g_signal_connect(G_OBJECT(button2), "clicked", + G_CALLBACK(alertpanel_button_clicked), + GUINT_TO_POINTER(G_ALERTALTERNATE)); + if (button3_label) + g_signal_connect(G_OBJECT(button3), "clicked", + G_CALLBACK(alertpanel_button_clicked), + GUINT_TO_POINTER(G_ALERTOTHER)); + + gtk_widget_show_all(dialog); +} + +static void alertpanel_button_toggled(GtkToggleButton *button, + gpointer data) +{ + if (gtk_toggle_button_get_active(button)) + value &= ~GPOINTER_TO_UINT(data); + else + value |= GPOINTER_TO_UINT(data); +} + +static void alertpanel_button_clicked(GtkWidget *widget, gpointer data) +{ + value = (value & ~G_ALERT_VALUE_MASK) | (AlertValue)data; +} + +static gint alertpanel_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + value = (value & ~G_ALERT_VALUE_MASK) | (AlertValue)data; + return TRUE; +} + +static gboolean alertpanel_close(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + if (event->type == GDK_KEY_PRESS) + if (((GdkEventKey *)event)->keyval != GDK_Escape) + return FALSE; + + value = (value & ~G_ALERT_VALUE_MASK) | (AlertValue)data; + return FALSE; +} + + +static gint alertpanel_focus_out(GtkWidget *widget, GdkEventFocus *event, + gpointer data) +{ +#ifdef G_OS_WIN32 + gtk_window_present(GTK_WINDOW(widget)); +#endif + return FALSE; +} diff --git a/src/alertpanel.h b/src/alertpanel.h new file mode 100644 index 0000000..ac8e2eb --- /dev/null +++ b/src/alertpanel.h @@ -0,0 +1,76 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __ALERTPANEL_H__ +#define __ALERTPANEL_H__ + +#include + +typedef enum +{ + G_ALERTDEFAULT, + G_ALERTALTERNATE, + G_ALERTOTHER, + G_ALERTCANCEL, + G_ALERTWAIT, + + G_ALERTDISABLE = 1 << 16 +} AlertValue; + +#define G_ALERT_VALUE_MASK 0x0000ffff + +typedef enum +{ + ALERT_NOTICE, + ALERT_QUESTION, + ALERT_WARNING, + ALERT_ERROR +} AlertType; + +AlertValue alertpanel_full (const gchar *title, + const gchar *message, + AlertType type, + AlertValue default_value, + gboolean can_disable, + const gchar *button1_label, + const gchar *button2_label, + const gchar *button3_label); + +AlertValue alertpanel (const gchar *title, + const gchar *message, + const gchar *button1_label, + const gchar *button2_label, + const gchar *button3_label); + +void alertpanel_message (const gchar *title, + const gchar *message, + AlertType type); + +AlertValue alertpanel_message_with_disable (const gchar *title, + const gchar *message, + AlertType type); + +void alertpanel_notice (const gchar *format, + ...) G_GNUC_PRINTF(1, 2); +void alertpanel_warning (const gchar *format, + ...) G_GNUC_PRINTF(1, 2); +void alertpanel_error (const gchar *format, + ...) G_GNUC_PRINTF(1, 2); + +#endif /* __ALERTPANEL_H__ */ diff --git a/src/colorlabel.c b/src/colorlabel.c new file mode 100644 index 0000000..1a35ad2 --- /dev/null +++ b/src/colorlabel.c @@ -0,0 +1,435 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001-2010 Hiroyuki Yamamoto & The Sylpheed Claws Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* (alfons) - based on a contribution by Satoshi Nagayasu; revised for colorful + * menu and more Sylpheed integration. The idea to put the code in a separate + * file is just that it make it easier to allow "user changeable" label colors. + */ + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "colorlabel.h" +#include "gtkutils.h" +#include "utils.h" +#include "prefs.h" + +static gchar *labels[] = { + N_("Orange"), + N_("Red") , + N_("Pink"), + N_("Sky blue"), + N_("Blue"), + N_("Green"), + N_("Brown") +}; + +typedef enum LabelColorChangeFlags_ { + LCCF_COLOR = 1 << 0, + LCCF_LABEL = 1 << 1, + LCCF_ALL = LCCF_COLOR | LCCF_LABEL +} LabelColorChangeFlags; + +/* XXX: if you add colors, make sure you also check the procmsg.h. + * color indices are stored as 3 bits; that explains the max. of 7 colors */ +static struct +{ + LabelColorChangeFlags changed; + GdkColor color; + + /* XXX: note that the label member is supposed to be dynamically + * allocated and fffreed */ + gchar *label; + GtkWidget *widget; + GtkWidget *label_widget; +} label_colors[] = { + { LCCF_ALL, { 0, 0xffff, (0x99 << 8), 0x0 }, NULL, NULL, NULL }, + { LCCF_ALL, { 0, 0xffff, 0, 0 }, NULL, NULL, NULL }, + { LCCF_ALL, { 0, 0xffff, (0x66 << 8), 0xffff }, NULL, NULL, NULL }, + { LCCF_ALL, { 0, 0x0, (0xcc << 8), 0xffff }, NULL, NULL, NULL }, + { LCCF_ALL, { 0, 0x0, 0x0, 0xffff }, NULL, NULL, NULL }, + { LCCF_ALL, { 0, 0x0, 0x99 << 8, 0x0 }, NULL, NULL, NULL }, + { LCCF_ALL, { 0, 0x66 << 8, 0x33 << 8, 0x33 << 8 }, NULL, NULL, NULL } +}; + +#define LABEL_COLOR_WIDTH 28 +#define LABEL_COLOR_HEIGHT 16 + +#define LABEL_COLORS_ELEMS (sizeof label_colors / sizeof label_colors[0]) + +#define G_RETURN_VAL_IF_INVALID_COLOR(color, val) \ + g_return_val_if_fail((color) >= 0 && (color) < LABEL_COLORS_ELEMS, (val)) + +static void colorlabel_recreate (gint); +static void colorlabel_recreate_label (gint); + +gint colorlabel_get_color_count(void) +{ + return LABEL_COLORS_ELEMS; +} + +GdkColor colorlabel_get_color(gint color_index) +{ + GdkColor invalid = { 0 }; + + G_RETURN_VAL_IF_INVALID_COLOR(color_index, invalid); + + return label_colors[color_index].color; +} + +const gchar *colorlabel_get_color_text(gint color_index) +{ + G_RETURN_VAL_IF_INVALID_COLOR(color_index, NULL); + + return label_colors[color_index].label ? + label_colors[color_index].label : gettext(labels[color_index]); +} + +const gchar *colorlabel_get_custom_color_text(gint color_index) +{ + G_RETURN_VAL_IF_INVALID_COLOR(color_index, NULL); + + return label_colors[color_index].label; +} + +void colorlabel_set_color_text(gint color_index, const gchar *label) +{ + if (label_colors[color_index].label) + g_free(label_colors[color_index].label); + + label_colors[color_index].label = g_strdup(label); + label_colors[color_index].changed |= LCCF_LABEL; +} + +static gboolean colorlabel_drawing_area_expose_event_cb + (GtkWidget *widget, GdkEventExpose *expose, gpointer data) +{ + GdkDrawable *drawable = widget->window; + gulong c = (gulong) GPOINTER_TO_INT(data); + GdkColor color; + GdkGC *gc; + + color.red = ((c >> 16UL) & 0xFFUL) << 8UL; + color.green = ((c >> 8UL) & 0xFFUL) << 8UL; + color.blue = ((c) & 0xFFUL) << 8UL; + + gdk_colormap_alloc_color(gtk_widget_get_colormap(widget), &color, FALSE, TRUE); + + gc = gdk_gc_new(drawable); + + gdk_gc_set_foreground(gc, &color); + gdk_draw_rectangle(drawable, gc, + TRUE, 0, 0, widget->allocation.width - 1, + widget->allocation.height - 1); + gdk_draw_rectangle(drawable, widget->style->black_gc, + FALSE, 0, 0, widget->allocation.width - 1, + widget->allocation.height - 1); + + gdk_gc_unref(gc); + + return FALSE; +} + +GtkWidget *colorlabel_create_color_widget(GdkColor color) +{ + GtkWidget *widget; + + widget = gtk_drawing_area_new(); + gtk_drawing_area_size(GTK_DRAWING_AREA(widget), + LABEL_COLOR_WIDTH - 2, LABEL_COLOR_HEIGHT - 4); + +#define CL(x) (((gulong) (x) >> (gulong) 8) & 0xFFUL) +#define CR(r, g, b) ((CL(r) << (gulong) 16) | \ + (CL(g) << (gulong) 8) | \ + (CL(b))) + + g_signal_connect(G_OBJECT(widget), "expose_event", + G_CALLBACK(colorlabel_drawing_area_expose_event_cb), + GINT_TO_POINTER + ((gint)CR(color.red, color.green, color.blue))); + + return widget; +} + +/* XXX: this function to check if menus with colors and labels should + * be recreated */ +gboolean colorlabel_changed(void) +{ + gint n; + + for (n = 0; n < LABEL_COLORS_ELEMS; n++) { + if (label_colors[n].changed) + return TRUE; + } + + return FALSE; +} + +/* XXX: colorlabel_recreate_XXX are there to make sure everything + * is initialized ok, without having to call a global _xxx_init_ + * function */ +static void colorlabel_recreate_color(gint color) +{ + GtkWidget *widget; + + if (!(label_colors[color].changed & LCCF_COLOR)) + return; + + widget = colorlabel_create_color_widget(label_colors[color].color); + g_return_if_fail(widget); + + if (label_colors[color].widget) + gtk_widget_destroy(label_colors[color].widget); + + label_colors[color].widget = widget; + label_colors[color].changed &= ~LCCF_COLOR; +} + +static void colorlabel_recreate_label(gint color) +{ + const gchar *text; + + if (!(label_colors[color].changed & LCCF_LABEL)) + return; + + text = colorlabel_get_color_text(color); + + if (label_colors[color].label_widget) + gtk_label_set_text(GTK_LABEL(label_colors[color].label_widget), + text); + else + label_colors[color].label_widget = gtk_label_new(text); + + label_colors[color].changed &= ~LCCF_LABEL; +} + +/* XXX: call this function everytime when you're doing important + * stuff with the label_colors[] array */ +static void colorlabel_recreate(gint color) +{ + colorlabel_recreate_label(color); + colorlabel_recreate_color(color); +} + +static void colorlabel_recreate_all(void) +{ + gint n; + + for (n = 0; n < LABEL_COLORS_ELEMS; n++) + colorlabel_recreate(n); +} + +/* colorlabel_create_check_color_menu_item() - creates a color + * menu item with a check box */ +GtkWidget *colorlabel_create_check_color_menu_item(gint color_index) +{ + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *item; + + G_RETURN_VAL_IF_INVALID_COLOR(color_index, NULL); + + item = gtk_check_menu_item_new(); + + colorlabel_recreate(color_index); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_widget_show(hbox); + gtk_container_add(GTK_CONTAINER(item), hbox); + + vbox = gtk_vbox_new(TRUE, 0); + gtk_widget_show(vbox); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 1); + + gtk_container_add(GTK_CONTAINER(vbox), + label_colors[color_index].widget); + gtk_widget_show(label_colors[color_index].widget); + + gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), label_colors[color_index].label_widget, FALSE, FALSE, 4); + gtk_widget_show(label_colors[color_index].label_widget); + + return item; +} + +/* colorlabel_create_color_menu() - creates a color menu without + * checkitems, probably for use in combo items */ +GtkWidget *colorlabel_create_color_menu(void) +{ + GtkWidget *label; + GtkWidget *item; + GtkWidget *menu; + gint i; + + colorlabel_recreate_all(); + + /* create the menu items. each item has its color code attached */ + menu = gtk_menu_new(); + g_object_set_data(G_OBJECT(menu), "label_color_menu", menu); + +#if 0 + item = gtk_menu_item_new_with_label(_("None")); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + g_object_set_data(G_OBJECT(item), "color", GUINT_TO_POINTER(0)); + gtk_widget_show(item); +#endif + + /* and the color items */ + for (i = 0; i < LABEL_COLORS_ELEMS; i++) { + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *widget; + + item = gtk_menu_item_new(); + g_object_set_data(G_OBJECT(item), "color", + GUINT_TO_POINTER(i + 1)); + + label = gtk_label_new(colorlabel_get_color_text(i)); + + gtk_widget_show(label); + hbox = gtk_hbox_new(FALSE, 0); + gtk_widget_show(hbox); + gtk_container_add(GTK_CONTAINER(item), hbox); + + vbox = gtk_vbox_new(TRUE, 0); + gtk_widget_show(vbox); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 1); + + widget = colorlabel_create_color_widget(label_colors[i].color); + gtk_widget_show(widget); + gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 4); + + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + } + + gtk_widget_show(menu); + + return menu; +} + +guint colorlabel_get_color_menu_active_item(GtkWidget *menu) +{ + GtkWidget *menuitem; + guint color; + + g_return_val_if_fail + (g_object_get_data(G_OBJECT(menu), "label_color_menu"), 0); + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + color = GPOINTER_TO_UINT + (g_object_get_data(G_OBJECT(menuitem), "color")); + return color; +} + +void colorlabel_update_menu(void) +{ + gint i; + + for (i = 0; i < LABEL_COLORS_ELEMS; i++) { + if (label_colors[i].widget && label_colors[i].changed) { + colorlabel_recreate(i); + } + } +} + +gint colorlabel_read_config(void) +{ + gchar *path; + FILE *fp; + gint i; + gchar buf[PREFSBUFSIZE]; + + path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "colorlabelrc", + NULL); + if ((fp = g_fopen(path, "rb")) == NULL) { + g_free(path); + return -1; + } + + for (i = 0; i < LABEL_COLORS_ELEMS; i++) { + if (fgets(buf, sizeof(buf), fp) == NULL) + break; + g_strstrip(buf); + if (buf[0] != '\0') { + colorlabel_set_color_text(i, buf); + } + } + + fclose(fp); + g_free(path); + + return 0; +} + +gint colorlabel_write_config(void) +{ + gchar *path; + PrefFile *pfile; + gint i; + gint ret; + const gchar *text; + + path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "colorlabelrc", + NULL); + if ((pfile = prefs_file_open(path)) == NULL) { + g_warning("failed to write colorlabelrc"); + g_free(path); + return -1; + } + + for (i = 0; i < LABEL_COLORS_ELEMS; i++) { + text = colorlabel_get_custom_color_text(i); + ret = 0; + if (text) + ret = fputs(text, pfile->fp); + + if (ret == EOF || fputc('\n', pfile->fp) == EOF) { + FILE_OP_ERROR(path, "fputs || fputc"); + prefs_file_close_revert(pfile); + g_free(path); + return -1; + } + } + + if (prefs_file_close(pfile) < 0) { + g_warning("failed to write colorlabelrc"); + g_free(path); + return -1; + } + + g_free(path); + + return 0; +} diff --git a/src/colorlabel.h b/src/colorlabel.h new file mode 100644 index 0000000..c521a66 --- /dev/null +++ b/src/colorlabel.h @@ -0,0 +1,42 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001-2010 Hiroyuki Yamamoto & The Sylpheed Claws Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if !defined(COLORLABEL_H__) +#define COLORLABEL_H__ + +#include + +gint colorlabel_get_color_count (void); +GdkColor colorlabel_get_color (gint color_index); +const gchar *colorlabel_get_color_text (gint color_index); +const gchar *colorlabel_get_custom_color_text (gint color_index); +void colorlabel_set_color_text (gint color_index, + const gchar *label); +gboolean colorlabel_changed (void); +GtkWidget *colorlabel_create_color_widget (GdkColor color); +GtkWidget *colorlabel_create_check_color_menu_item + (gint color_index); +GtkWidget *colorlabel_create_color_menu (void); +guint colorlabel_get_color_menu_active_item (GtkWidget *menu); +void colorlabel_update_menu (void); + +gint colorlabel_read_config (void); +gint colorlabel_write_config (void); + +#endif /* COLORLABEL_H__ */ diff --git a/src/compose.c b/src/compose.c new file mode 100644 index 0000000..0e5a032 --- /dev/null +++ b/src/compose.c @@ -0,0 +1,8612 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2017 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#ifndef PANGO_ENABLE_ENGINE +# define PANGO_ENABLE_ENGINE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if USE_GTKSPELL +# include +# include +#if USE_ENCHANT +# include +#else +# include +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_SYS_WAIT_H +# include +#endif +#include +#include +#ifdef G_OS_WIN32 +# include +#endif + +#include "main.h" +#include "mainwindow.h" +#include "compose.h" +#include "addressbook.h" +#include "folderview.h" +#include "procmsg.h" +#include "menu.h" +#include "stock_pixmap.h" +#include "send_message.h" +#include "imap.h" +#include "news.h" +#include "customheader.h" +#include "prefs_common.h" +#include "prefs_common_dialog.h" +#include "prefs_account.h" +#include "prefs_toolbar.h" +#include "action.h" +#include "account.h" +#include "account_dialog.h" +#include "filesel.h" +#include "procheader.h" +#include "procmime.h" +#include "statusbar.h" +#include "about.h" +#include "base64.h" +#include "quoted-printable.h" +#include "codeconv.h" +#include "utils.h" +#include "gtkutils.h" +#include "socket.h" +#include "alertpanel.h" +#include "manage_window.h" +#include "gtkshruler.h" +#include "folder.h" +#include "filter.h" +#include "addr_compl.h" +#include "quote_fmt.h" +#include "template.h" +#include "undo.h" +#include "plugin.h" +#include "md5.h" +#include "inc.h" + +#if USE_GPGME +# include "rfc2015.h" +#endif + +enum +{ + COL_MIMETYPE, + COL_SIZE, + COL_NAME, + COL_ATTACH_INFO, + N_ATTACH_COLS +}; + +typedef enum +{ + COMPOSE_ACTION_MOVE_BEGINNING_OF_LINE, + COMPOSE_ACTION_MOVE_FORWARD_CHARACTER, + COMPOSE_ACTION_MOVE_BACKWARD_CHARACTER, + COMPOSE_ACTION_MOVE_FORWARD_WORD, + COMPOSE_ACTION_MOVE_BACKWARD_WORD, + COMPOSE_ACTION_MOVE_END_OF_LINE, + COMPOSE_ACTION_MOVE_NEXT_LINE, + COMPOSE_ACTION_MOVE_PREVIOUS_LINE, + COMPOSE_ACTION_DELETE_FORWARD_CHARACTER, + COMPOSE_ACTION_DELETE_BACKWARD_CHARACTER, + COMPOSE_ACTION_DELETE_FORWARD_WORD, + COMPOSE_ACTION_DELETE_BACKWARD_WORD, + COMPOSE_ACTION_DELETE_LINE, + COMPOSE_ACTION_DELETE_LINE_N, + COMPOSE_ACTION_DELETE_TO_LINE_END +} ComposeAction; + +#define B64_LINE_SIZE 57 +#define B64_BUFFSIZE 77 + +#define MAX_REFERENCES_LEN 999 + +#define TEXTVIEW_MARGIN 6 + +static GdkColor quote_color = {0, 0, 0, 0xbfff}; + +static GList *compose_list = NULL; + +static Compose *compose_create (PrefsAccount *account, + ComposeMode mode); +static Compose *compose_find_window_by_target (MsgInfo *msginfo); +static gboolean compose_window_exist (gint x, + gint y); +static void compose_connect_changed_callbacks (Compose *compose); + +static GtkWidget *compose_toolbar_create (Compose *compose); +static GtkWidget *compose_toolbar_create_from_list + (Compose *compose, + GList *item_list); +static void compose_set_toolbar_button_visibility + (Compose *compose); + +static GtkWidget *compose_account_option_menu_create + (Compose *compose, + GtkWidget *hbox); +static GtkWidget *compose_signature_menu_create(Compose *compose, + GtkWidget *hbox); +static void compose_set_out_encoding (Compose *compose); +static void compose_set_template_menu (Compose *compose); +static void compose_template_apply (Compose *compose, + Template *tmpl, + gboolean replace); +static void compose_destroy (Compose *compose); + +static void compose_entry_show (Compose *compose, + ComposeEntryType type); +static GtkEntry *compose_get_entry (Compose *compose, + ComposeEntryType type); +static void compose_entries_set (Compose *compose, + const gchar *mailto); +static void compose_entries_set_from_item (Compose *compose, + FolderItem *item, + ComposeMode mode); +static gint compose_parse_header (Compose *compose, + MsgInfo *msginfo); +static gchar *compose_parse_references (const gchar *ref, + const gchar *msgid); +static gint compose_parse_source_msg (Compose *compose, + MsgInfo *msginfo); + +static gchar *compose_quote_fmt (Compose *compose, + MsgInfo *msginfo, + const gchar *fmt, + const gchar *qmark, + const gchar *body); + +static void compose_reply_set_entry (Compose *compose, + MsgInfo *msginfo, + ComposeMode mode); +static void compose_reedit_set_entry (Compose *compose, + MsgInfo *msginfo); + +static void compose_insert_sig (Compose *compose, + gboolean append, + gboolean replace, + gboolean scroll); +static void compose_enable_sig (Compose *compose); +static gchar *compose_get_signature_str (Compose *compose); + +static void compose_insert_file (Compose *compose, + const gchar *file, + gboolean scroll); + +static void compose_attach_parts (Compose *compose, + MsgInfo *msginfo); + +static void compose_wrap_paragraph (Compose *compose, + GtkTextIter *par_iter); +static void compose_wrap_all (Compose *compose); +static void compose_wrap_all_full (Compose *compose, + gboolean autowrap); + +static void compose_set_title (Compose *compose); +static void compose_select_account (Compose *compose, + PrefsAccount *account, + gboolean init); +static void compose_update_signature_menu (Compose *compose); + +static gboolean compose_check_for_valid_recipient + (Compose *compose); +static gboolean compose_check_entries (Compose *compose); +static gboolean compose_check_attachments (Compose *compose); +static gboolean compose_check_recipients (Compose *compose); +static gboolean compose_check_activities (Compose *compose); + +static void compose_add_new_recipients_to_addressbook + (Compose *compose); + +static gint compose_send_real (Compose *compose); +static gint compose_write_to_file (Compose *compose, + const gchar *file, + gboolean is_draft); +static gint compose_write_body_to_file (Compose *compose, + const gchar *file); +static gint compose_redirect_write_to_file (Compose *compose, + const gchar *file); +static gint compose_remove_reedit_target (Compose *compose); +static gint compose_queue (Compose *compose, + const gchar *file); +static gint compose_write_attach (Compose *compose, + FILE *fp, + const gchar *charset); +static gint compose_write_headers (Compose *compose, + FILE *fp, + const gchar *charset, + const gchar *body_charset, + EncodingType encoding, + gboolean is_draft); +static gint compose_redirect_write_headers (Compose *compose, + FILE *fp); + +static void compose_convert_header (Compose *compose, + gchar *dest, + gint len, + const gchar *src, + gint header_len, + gboolean addr_field, + const gchar *encoding); +static gchar *compose_convert_filename (Compose *compose, + const gchar *src, + const gchar *param_name, + const gchar *encoding); +static void compose_generate_msgid (Compose *compose, + gchar *buf, + gint len); + +static void compose_attach_info_free (AttachInfo *ainfo); +static void compose_attach_remove_selected (Compose *compose); + +static void compose_attach_property (Compose *compose); +static void compose_attach_property_create (gboolean *cancelled); +static void attach_property_ok (GtkWidget *widget, + gboolean *cancelled); +static void attach_property_cancel (GtkWidget *widget, + gboolean *cancelled); +static gint attach_property_delete_event (GtkWidget *widget, + GdkEventAny *event, + gboolean *cancelled); +static gboolean attach_property_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gboolean *cancelled); + +static void compose_attach_open (Compose *compose); + +static void compose_exec_ext_editor (Compose *compose); +static gboolean compose_ext_editor_kill (Compose *compose); +static void compose_ext_editor_child_exit (GPid pid, + gint status, + gpointer data); +static void compose_set_ext_editor_sensitive (Compose *compose, + gboolean sensitive); + +static void compose_undo_state_changed (UndoMain *undostruct, + gint undo_state, + gint redo_state, + gpointer data); + +static gint calc_cursor_xpos (GtkTextView *text, + gint extra, + gint char_width); + +/* callback functions */ + +static gboolean compose_edit_size_alloc (GtkEditable *widget, + GtkAllocation *allocation, + GtkSHRuler *shruler); + +static void toolbar_send_cb (GtkWidget *widget, + gpointer data); +static void toolbar_send_later_cb (GtkWidget *widget, + gpointer data); +static void toolbar_draft_cb (GtkWidget *widget, + gpointer data); +static void toolbar_insert_cb (GtkWidget *widget, + gpointer data); +static void toolbar_attach_cb (GtkWidget *widget, + gpointer data); +static void toolbar_sig_cb (GtkWidget *widget, + gpointer data); +static void toolbar_ext_editor_cb (GtkWidget *widget, + gpointer data); +static void toolbar_linewrap_cb (GtkWidget *widget, + gpointer data); +static void toolbar_address_cb (GtkWidget *widget, + gpointer data); +static void toolbar_prefs_common_cb (GtkWidget *widget, + gpointer data); +static void toolbar_prefs_account_cb (GtkWidget *widget, + gpointer data); + +static gboolean toolbar_button_pressed (GtkWidget *widget, + GdkEventButton *event, + gpointer data); + +static void account_activated (GtkMenuItem *menuitem, + gpointer data); +static void sig_combo_changed (GtkComboBox *combo, + gpointer data); + +static void attach_selection_changed (GtkTreeSelection *selection, + gpointer data); + +static gboolean attach_button_pressed (GtkWidget *widget, + GdkEventButton *event, + gpointer data); +static gboolean attach_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); + +static void compose_send_cb (gpointer data, + guint action, + GtkWidget *widget); +static void compose_send_later_cb (gpointer data, + guint action, + GtkWidget *widget); + +static void compose_draft_cb (gpointer data, + guint action, + GtkWidget *widget); + +static void compose_attach_open_cb (gpointer data, + guint action, + GtkWidget *widget); +static void compose_attach_cb (gpointer data, + guint action, + GtkWidget *widget); +static void compose_insert_file_cb (gpointer data, + guint action, + GtkWidget *widget); +static void compose_insert_sig_cb (gpointer data, + guint action, + GtkWidget *widget); + +static void compose_close_cb (gpointer data, + guint action, + GtkWidget *widget); + +static void compose_set_encoding_cb (gpointer data, + guint action, + GtkWidget *widget); + +static void compose_address_cb (gpointer data, + guint action, + GtkWidget *widget); +static void compose_template_activate_cb(GtkWidget *widget, + gpointer data); + +static void compose_ext_editor_cb (gpointer data, + guint action, + GtkWidget *widget); + +static gint compose_delete_cb (GtkWidget *widget, + GdkEventAny *event, + gpointer data); + +static gint compose_window_state_cb (GtkWidget *widget, + GdkEventWindowState *event, + gpointer data); + +static void compose_undo_cb (Compose *compose); +static void compose_redo_cb (Compose *compose); +static void compose_cut_cb (Compose *compose); +static void compose_copy_cb (Compose *compose); +static void compose_paste_cb (Compose *compose); +static void compose_paste_as_quote_cb (Compose *compose); +static void compose_allsel_cb (Compose *compose); + +static void compose_grab_focus_cb (GtkWidget *widget, + Compose *compose); + +#if USE_GPGME +static void compose_signing_toggled (GtkWidget *widget, + Compose *compose); +static void compose_encrypt_toggled (GtkWidget *widget, + Compose *compose); +#endif + +#if 0 +static void compose_attach_toggled (GtkWidget *widget, + Compose *compose); +#endif + +static void compose_buffer_changed_cb (GtkTextBuffer *textbuf, + Compose *compose); +static void compose_changed_cb (GtkEditable *editable, + Compose *compose); + +static void compose_wrap_cb (gpointer data, + guint action, + GtkWidget *widget); +static void compose_toggle_autowrap_cb (gpointer data, + guint action, + GtkWidget *widget); + +static void compose_toggle_to_cb (gpointer data, + guint action, + GtkWidget *widget); +static void compose_toggle_cc_cb (gpointer data, + guint action, + GtkWidget *widget); +static void compose_toggle_bcc_cb (gpointer data, + guint action, + GtkWidget *widget); +static void compose_toggle_replyto_cb (gpointer data, + guint action, + GtkWidget *widget); +static void compose_toggle_followupto_cb(gpointer data, + guint action, + GtkWidget *widget); +static void compose_toggle_ruler_cb (gpointer data, + guint action, + GtkWidget *widget); +static void compose_toggle_attach_cb (gpointer data, + guint action, + GtkWidget *widget); +static void compose_customize_toolbar_cb(gpointer data, + guint action, + GtkWidget *widget); + +static void compose_toggle_mdn_cb (gpointer data, + guint action, + GtkWidget *widget); + +#if USE_GPGME +static void compose_toggle_sign_cb (gpointer data, + guint action, + GtkWidget *widget); +static void compose_toggle_encrypt_cb (gpointer data, + guint action, + GtkWidget *widget); +#endif + +#if USE_GTKSPELL +static void compose_set_spell_lang_menu (Compose *compose); +static void compose_change_spell_lang_menu + (Compose *compose, + const gchar *lang); +static void compose_toggle_spell_cb (gpointer data, + guint action, + GtkWidget *widget); +static void compose_set_spell_lang_cb (GtkWidget *widget, + gpointer data); +#endif + +static void compose_attach_drag_received_cb (GtkWidget *widget, + GdkDragContext *drag_context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + gpointer user_data); +static void compose_insert_drag_received_cb (GtkWidget *widget, + GdkDragContext *drag_context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + gpointer user_data); + +static void to_activated (GtkWidget *widget, + Compose *compose); +static void newsgroups_activated (GtkWidget *widget, + Compose *compose); +static void cc_activated (GtkWidget *widget, + Compose *compose); +static void bcc_activated (GtkWidget *widget, + Compose *compose); +static void replyto_activated (GtkWidget *widget, + Compose *compose); +static void followupto_activated (GtkWidget *widget, + Compose *compose); +static void subject_activated (GtkWidget *widget, + Compose *compose); + +static void text_inserted (GtkTextBuffer *buffer, + GtkTextIter *iter, + const gchar *text, + gint len, + Compose *compose); + +static gboolean autosave_timeout (gpointer data); + + +static GtkItemFactoryEntry compose_popup_entries[] = +{ + {N_("/_Open"), NULL, compose_attach_open_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Add..."), NULL, compose_attach_cb, 0, NULL}, + {N_("/_Remove"), NULL, compose_attach_remove_selected, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Properties..."), NULL, compose_attach_property, 0, NULL} +}; + +static GtkItemFactoryEntry compose_entries[] = +{ + {N_("/_File"), NULL, NULL, 0, ""}, + {N_("/_File/_Send"), "E", + compose_send_cb, 0, NULL}, + {N_("/_File/Send _later"), "S", + compose_send_later_cb, 0, NULL}, + {N_("/_File/---"), NULL, NULL, 0, ""}, + {N_("/_File/Save to _draft folder"), + "D", compose_draft_cb, 0, NULL}, + {N_("/_File/Save and _keep editing"), + "S", compose_draft_cb, 1, NULL}, + {N_("/_File/---"), NULL, NULL, 0, ""}, + {N_("/_File/_Attach file"), "M", compose_attach_cb, 0, NULL}, + {N_("/_File/_Insert file"), "I", compose_insert_file_cb, 0, NULL}, + {N_("/_File/---"), NULL, NULL, 0, ""}, + {N_("/_File/Insert si_gnature"), "G", compose_insert_sig_cb, 0, NULL}, + {N_("/_File/A_ppend signature"), "G", compose_insert_sig_cb, 1, NULL}, + {N_("/_File/---"), NULL, NULL, 0, ""}, + {N_("/_File/_Close"), "W", compose_close_cb, 0, NULL}, + + {N_("/_Edit"), NULL, NULL, 0, ""}, + {N_("/_Edit/_Undo"), "Z", compose_undo_cb, 0, NULL}, + {N_("/_Edit/_Redo"), "Y", compose_redo_cb, 0, NULL}, + {N_("/_Edit/---"), NULL, NULL, 0, ""}, + {N_("/_Edit/Cu_t"), "X", compose_cut_cb, 0, NULL}, + {N_("/_Edit/_Copy"), "C", compose_copy_cb, 0, NULL}, + {N_("/_Edit/_Paste"), "V", compose_paste_cb, 0, NULL}, + {N_("/_Edit/Paste as _quotation"), + NULL, compose_paste_as_quote_cb, 0, NULL}, + {N_("/_Edit/Select _all"), "A", compose_allsel_cb, 0, NULL}, + {N_("/_Edit/---"), NULL, NULL, 0, ""}, + {N_("/_Edit/_Wrap current paragraph"), + "L", compose_wrap_cb, 0, NULL}, + {N_("/_Edit/Wrap all long _lines"), + "L", compose_wrap_cb, 1, NULL}, + {N_("/_Edit/Aut_o wrapping"), "L", compose_toggle_autowrap_cb, 0, ""}, + {N_("/_View"), NULL, NULL, 0, ""}, + {N_("/_View/_To"), NULL, compose_toggle_to_cb , 0, ""}, + {N_("/_View/_Cc"), NULL, compose_toggle_cc_cb , 0, ""}, + {N_("/_View/_Bcc"), NULL, compose_toggle_bcc_cb , 0, ""}, + {N_("/_View/_Reply-To"), NULL, compose_toggle_replyto_cb, 0, ""}, + {N_("/_View/---"), NULL, NULL, 0, ""}, + {N_("/_View/_Followup-To"), NULL, compose_toggle_followupto_cb, 0, ""}, + {N_("/_View/---"), NULL, NULL, 0, ""}, + {N_("/_View/R_uler"), NULL, compose_toggle_ruler_cb, 0, ""}, + {N_("/_View/---"), NULL, NULL, 0, ""}, + {N_("/_View/_Attachment"), NULL, compose_toggle_attach_cb, 0, ""}, + {N_("/_View/---"), NULL, NULL, 0, ""}, + {N_("/_View/Cu_stomize toolbar..."), + NULL, compose_customize_toolbar_cb, 0, NULL}, + {N_("/_View/---"), NULL, NULL, 0, ""}, + +#define ENC_ACTION(action) \ + NULL, compose_set_encoding_cb, action, \ + "/View/Character encoding/Automatic" + + {N_("/_View/Character _encoding"), NULL, NULL, 0, ""}, + {N_("/_View/Character _encoding/_Automatic"), + NULL, compose_set_encoding_cb, C_AUTO, ""}, + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""}, + + {N_("/_View/Character _encoding/7bit ascii (US-ASC_II)"), + ENC_ACTION(C_US_ASCII)}, + {N_("/_View/Character _encoding/Unicode (_UTF-8)"), + ENC_ACTION(C_UTF_8)}, + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""}, + + {N_("/_View/Character _encoding/Western European (ISO-8859-_1)"), + ENC_ACTION(C_ISO_8859_1)}, + {N_("/_View/Character _encoding/Western European (ISO-8859-15)"), + ENC_ACTION(C_ISO_8859_15)}, + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""}, + + {N_("/_View/Character _encoding/Central European (ISO-8859-_2)"), + ENC_ACTION(C_ISO_8859_2)}, + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""}, + + {N_("/_View/Character _encoding/_Baltic (ISO-8859-13)"), + ENC_ACTION(C_ISO_8859_13)}, + {N_("/_View/Character _encoding/Baltic (ISO-8859-_4)"), + ENC_ACTION(C_ISO_8859_4)}, + {N_("/_View/Character _encoding/Baltic (Windows-1257)"), + ENC_ACTION(C_WINDOWS_1257)}, + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""}, + + {N_("/_View/Character _encoding/Greek (ISO-8859-_7)"), + ENC_ACTION(C_ISO_8859_7)}, + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""}, + + {N_("/_View/Character _encoding/Arabic (ISO-8859-_6)"), + ENC_ACTION(C_ISO_8859_6)}, + {N_("/_View/Character _encoding/Arabic (Windows-1256)"), + ENC_ACTION(C_WINDOWS_1256)}, + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""}, + + {N_("/_View/Character _encoding/Hebrew (ISO-8859-_8)"), + ENC_ACTION(C_ISO_8859_8)}, + {N_("/_View/Character _encoding/Hebrew (Windows-1255)"), + ENC_ACTION(C_WINDOWS_1255)}, + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""}, + + {N_("/_View/Character _encoding/Turkish (ISO-8859-_9)"), + ENC_ACTION(C_ISO_8859_9)}, + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""}, + + {N_("/_View/Character _encoding/Cyrillic (ISO-8859-_5)"), + ENC_ACTION(C_ISO_8859_5)}, + {N_("/_View/Character _encoding/Cyrillic (KOI8-_R)"), + ENC_ACTION(C_KOI8_R)}, + {N_("/_View/Character _encoding/Cyrillic (KOI8-U)"), + ENC_ACTION(C_KOI8_U)}, + {N_("/_View/Character _encoding/Cyrillic (Windows-1251)"), + ENC_ACTION(C_WINDOWS_1251)}, + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""}, + + {N_("/_View/Character _encoding/Japanese (ISO-2022-_JP)"), + ENC_ACTION(C_ISO_2022_JP)}, + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""}, + + {N_("/_View/Character _encoding/Simplified Chinese (_GB2312)"), + ENC_ACTION(C_GB2312)}, + {N_("/_View/Character _encoding/Simplified Chinese (GBK)"), + ENC_ACTION(C_GBK)}, + {N_("/_View/Character _encoding/Traditional Chinese (_Big5)"), + ENC_ACTION(C_BIG5)}, + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""}, + + {N_("/_View/Character _encoding/Korean (EUC-_KR)"), + ENC_ACTION(C_EUC_KR)}, + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""}, + + {N_("/_View/Character _encoding/Thai (TIS-620)"), + ENC_ACTION(C_TIS_620)}, + {N_("/_View/Character _encoding/Thai (Windows-874)"), + ENC_ACTION(C_WINDOWS_874)}, + + {N_("/_Tools"), NULL, NULL, 0, ""}, + {N_("/_Tools/_Address book"), "A", compose_address_cb , 0, NULL}, + {N_("/_Tools/_Template"), NULL, NULL, 0, ""}, +#ifndef G_OS_WIN32 + {N_("/_Tools/Actio_ns"), NULL, NULL, 0, ""}, +#endif + {N_("/_Tools/---"), NULL, NULL, 0, ""}, + {N_("/_Tools/Edit with e_xternal editor"), + "X", compose_ext_editor_cb, 0, NULL}, + {N_("/_Tools/---"), NULL, NULL, 0, ""}, + {N_("/_Tools/Request _disposition notification"), NULL, compose_toggle_mdn_cb , 0, ""}, + +#if USE_GPGME + {N_("/_Tools/---"), NULL, NULL, 0, ""}, + {N_("/_Tools/PGP Si_gn"), NULL, compose_toggle_sign_cb , 0, ""}, + {N_("/_Tools/PGP _Encrypt"), NULL, compose_toggle_encrypt_cb, 0, ""}, +#endif /* USE_GPGME */ + +#if USE_GTKSPELL + {N_("/_Tools/---"), NULL, NULL, 0, ""}, + {N_("/_Tools/_Check spell"), NULL, compose_toggle_spell_cb, 0, ""}, + {N_("/_Tools/_Set spell language"), NULL, NULL, 0, ""}, +#endif /* USE_GTKSPELL */ + + {N_("/_Help"), NULL, NULL, 0, ""}, + {N_("/_Help/_About"), NULL, about_show, 0, NULL} +}; + +enum +{ + DRAG_TYPE_RFC822, + DRAG_TYPE_URI_LIST, + + N_DRAG_TYPES +}; + +static GtkTargetEntry compose_drag_types[] = +{ + {"message/rfc822", GTK_TARGET_SAME_APP, DRAG_TYPE_RFC822}, + {"text/uri-list", 0, DRAG_TYPE_URI_LIST} +}; + + +Compose *compose_new(PrefsAccount *account, FolderItem *item, + const gchar *mailto, GPtrArray *attach_files) +{ + Compose *compose; + GtkTextView *text; + GtkTextBuffer *buffer; + GtkTextIter iter; + + if (!account) account = cur_account; + g_return_val_if_fail(account != NULL, NULL); + + compose = compose_create(account, COMPOSE_NEW); + + undo_block(compose->undostruct); + + if (prefs_common.auto_sig) + compose_insert_sig(compose, TRUE, FALSE, FALSE); + + text = GTK_TEXT_VIEW(compose->text); + buffer = gtk_text_view_get_buffer(text); + gtk_text_buffer_get_start_iter(buffer, &iter); + gtk_text_buffer_place_cursor(buffer, &iter); + + if (account->protocol != A_NNTP) { + if (mailto && *mailto != '\0') { + compose_entries_set(compose, mailto); + gtk_widget_grab_focus(compose->subject_entry); + } else if (item) { + compose_entries_set_from_item + (compose, item, COMPOSE_NEW); + if (item->auto_to) + gtk_widget_grab_focus(compose->subject_entry); + else + gtk_widget_grab_focus(compose->to_entry); + } else + gtk_widget_grab_focus(compose->to_entry); + } else { + if (mailto && *mailto != '\0') { + compose_entry_append(compose, mailto, + COMPOSE_ENTRY_NEWSGROUPS); + gtk_widget_grab_focus(compose->subject_entry); + } else + gtk_widget_grab_focus(compose->newsgroups_entry); + } + + if (attach_files) { + gint i; + gchar *file; + + for (i = 0; i < attach_files->len; i++) { + gchar *utf8file; + + file = g_ptr_array_index(attach_files, i); + utf8file = conv_filename_to_utf8(file); + compose_attach_append(compose, file, utf8file, NULL); + g_free(utf8file); + } + } + + undo_unblock(compose->undostruct); + + compose_connect_changed_callbacks(compose); + compose_set_title(compose); + + syl_plugin_signal_emit("compose-created", compose); + + if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0) + compose->autosave_tag = + g_timeout_add_full(G_PRIORITY_LOW, + prefs_common.autosave_itv * 60 * 1000, + autosave_timeout, compose, NULL); + if (prefs_common.auto_exteditor) + compose_exec_ext_editor(compose); + + return compose; +} + +Compose *compose_reply(MsgInfo *msginfo, FolderItem *item, ComposeMode mode, + const gchar *body) +{ + Compose *compose; + PrefsAccount *account = NULL; + MsgInfo *replyinfo; + GtkTextBuffer *buffer; + GtkTextIter iter; + gboolean quote = FALSE; + + g_return_val_if_fail(msginfo != NULL, NULL); + + if (COMPOSE_QUOTE_MODE(mode) == COMPOSE_WITH_QUOTE) + quote = TRUE; + + if (msginfo->folder) + account = account_find_from_item_property(msginfo->folder); + if (!account && msginfo->to && prefs_common.reply_account_autosel) { + gchar *to; + to = g_strdup(msginfo->to); + extract_address(to); + account = account_find_from_address(to); + g_free(to); + } + if (!account && msginfo->folder && msginfo->folder->folder) + account = msginfo->folder->folder->account; + if (!account) + account = cur_account; + g_return_val_if_fail(account != NULL, NULL); + + compose = compose_create(account, COMPOSE_REPLY); + + replyinfo = procmsg_msginfo_get_full_info(msginfo); + if (!replyinfo) + replyinfo = procmsg_msginfo_copy(msginfo); + if (msginfo->folder) { + gchar *id; + id = folder_item_get_identifier(msginfo->folder); + if (id) { + compose->reply_target = g_strdup_printf + ("%s/%u", id, msginfo->msgnum); + g_free(id); + } + } + + if (compose_parse_header(compose, msginfo) < 0) + return compose; + + undo_block(compose->undostruct); + + compose_reply_set_entry(compose, msginfo, mode); + if (item) + compose_entries_set_from_item(compose, item, COMPOSE_REPLY); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text)); + + if (account->sig_before_quote && prefs_common.auto_sig) { + GtkTextMark *mark; + compose_insert_sig(compose, TRUE, FALSE, FALSE); + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } + + if (quote) { + compose_quote_fmt(compose, replyinfo, + prefs_common.quotefmt, + prefs_common.quotemark, body); + } + + if (!account->sig_before_quote && prefs_common.auto_sig) + compose_insert_sig(compose, TRUE, FALSE, FALSE); + + if (quote && prefs_common.linewrap_quote) + compose_wrap_all(compose); + + gtk_text_buffer_get_start_iter(buffer, &iter); + gtk_text_buffer_place_cursor(buffer, &iter); + + gtk_widget_grab_focus(compose->text); + + undo_unblock(compose->undostruct); + + compose_connect_changed_callbacks(compose); + compose_set_title(compose); + +#if USE_GPGME + if (rfc2015_is_available() && account->encrypt_reply && + MSG_IS_ENCRYPTED(replyinfo->flags)) { + GtkItemFactory *ifactory; + + ifactory = gtk_item_factory_from_widget(compose->menubar); + menu_set_active(ifactory, "/Tools/PGP Encrypt", TRUE); + } +#endif + + procmsg_msginfo_free(replyinfo); + + syl_plugin_signal_emit("compose-created", compose); + + if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0) + compose->autosave_tag = + g_timeout_add_full(G_PRIORITY_LOW, + prefs_common.autosave_itv * 60 * 1000, + autosave_timeout, compose, NULL); + if (prefs_common.auto_exteditor) + compose_exec_ext_editor(compose); + + return compose; +} + +Compose *compose_forward(GSList *mlist, FolderItem *item, gboolean as_attach, + const gchar *body) +{ + Compose *compose; + PrefsAccount *account = NULL; + GtkTextView *text; + GtkTextBuffer *buffer; + GtkTextIter iter; + GSList *cur; + MsgInfo *msginfo; + GString *forward_targets; + + g_return_val_if_fail(mlist != NULL, NULL); + + msginfo = (MsgInfo *)mlist->data; + + if (msginfo->folder) + account = account_find_from_item(msginfo->folder); + if (!account) account = cur_account; + g_return_val_if_fail(account != NULL, NULL); + + compose = compose_create(account, COMPOSE_FORWARD); + + forward_targets = g_string_new(""); + for (cur = mlist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + if (msginfo->folder) { + gchar *id; + id = folder_item_get_identifier(msginfo->folder); + if (id) { + if (forward_targets->len > 0) + g_string_append(forward_targets, + "\n "); + g_string_append_printf(forward_targets, "%s/%u", + id, msginfo->msgnum); + g_free(id); + } + } + } + if (forward_targets->len > 0) + compose->forward_targets = g_string_free(forward_targets, + FALSE); + else + g_string_free(forward_targets, TRUE); + + undo_block(compose->undostruct); + + compose_entry_set(compose, "Fw: ", COMPOSE_ENTRY_SUBJECT); + if (mlist->next == NULL && msginfo->subject && *msginfo->subject) + compose_entry_append(compose, msginfo->subject, + COMPOSE_ENTRY_SUBJECT); + if (item) + compose_entries_set_from_item(compose, item, COMPOSE_FORWARD); + + text = GTK_TEXT_VIEW(compose->text); + buffer = gtk_text_view_get_buffer(text); + + if (account->sig_before_quote && prefs_common.auto_sig) { + GtkTextMark *mark; + compose_insert_sig(compose, TRUE, FALSE, FALSE); + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } + + for (cur = mlist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + + if (as_attach) { + gchar *msgfile; + + msgfile = procmsg_get_message_file_path(msginfo); + if (!is_file_exist(msgfile)) + g_warning(_("%s: file not exist\n"), msgfile); + else + compose_attach_append(compose, msgfile, msgfile, + "message/rfc822"); + + g_free(msgfile); + } else { + MsgInfo *full_msginfo; + + full_msginfo = procmsg_msginfo_get_full_info(msginfo); + if (!full_msginfo) + full_msginfo = procmsg_msginfo_copy(msginfo); + + if (cur != mlist) { + GtkTextMark *mark; + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark + (buffer, &iter, mark); + gtk_text_buffer_insert + (buffer, &iter, "\n\n", 2); + } + + compose_quote_fmt(compose, full_msginfo, + prefs_common.fw_quotefmt, + prefs_common.fw_quotemark, body); + compose_attach_parts(compose, msginfo); + + procmsg_msginfo_free(full_msginfo); + + if (body) break; + } + } + + if (!account->sig_before_quote && prefs_common.auto_sig) + compose_insert_sig(compose, TRUE, FALSE, FALSE); + + if (prefs_common.linewrap_quote) + compose_wrap_all(compose); + + gtk_text_buffer_get_start_iter(buffer, &iter); + gtk_text_buffer_place_cursor(buffer, &iter); + + undo_unblock(compose->undostruct); + + compose_connect_changed_callbacks(compose); + compose_set_title(compose); + + if (account->protocol != A_NNTP) + gtk_widget_grab_focus(compose->to_entry); + else + gtk_widget_grab_focus(compose->newsgroups_entry); + + syl_plugin_signal_emit("compose-created", compose); + + if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0) + compose->autosave_tag = + g_timeout_add_full(G_PRIORITY_LOW, + prefs_common.autosave_itv * 60 * 1000, + autosave_timeout, compose, NULL); + if (prefs_common.auto_exteditor) + compose_exec_ext_editor(compose); + + return compose; +} + +Compose *compose_redirect(MsgInfo *msginfo, FolderItem *item) +{ + Compose *compose; + PrefsAccount *account; + GtkTextView *text; + GtkTextBuffer *buffer; + GtkTextMark *mark; + GtkTextIter iter; + FILE *fp; + gchar buf[BUFFSIZE]; + + g_return_val_if_fail(msginfo != NULL, NULL); + g_return_val_if_fail(msginfo->folder != NULL, NULL); + + account = account_find_from_item(msginfo->folder); + if (!account) account = cur_account; + g_return_val_if_fail(account != NULL, NULL); + + compose = compose_create(account, COMPOSE_REDIRECT); + compose->targetinfo = procmsg_msginfo_copy(msginfo); + + if (compose_parse_header(compose, msginfo) < 0) + return compose; + + undo_block(compose->undostruct); + + if (msginfo->subject) + compose_entry_set(compose, msginfo->subject, + COMPOSE_ENTRY_SUBJECT); + compose_entries_set_from_item(compose, item, COMPOSE_REDIRECT); + + text = GTK_TEXT_VIEW(compose->text); + buffer = gtk_text_view_get_buffer(text); + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark); + + if ((fp = procmime_get_first_text_content(msginfo, NULL)) == NULL) + g_warning(_("Can't get text part\n")); + else { + gboolean prev_autowrap = compose->autowrap; + + compose->autowrap = FALSE; + while (fgets(buf, sizeof(buf), fp) != NULL) { + strcrchomp(buf); + gtk_text_buffer_insert(buffer, &iter, buf, -1); + } + compose->autowrap = prev_autowrap; + fclose(fp); + } + compose_attach_parts(compose, msginfo); + + if (account->protocol != A_NNTP) + gtk_widget_grab_focus(compose->to_entry); + else + gtk_widget_grab_focus(compose->newsgroups_entry); + + gtk_text_view_set_editable(text, FALSE); + + undo_unblock(compose->undostruct); + + compose_connect_changed_callbacks(compose); + compose_set_title(compose); + + syl_plugin_signal_emit("compose-created", compose); + + return compose; +} + +Compose *compose_reedit(MsgInfo *msginfo) +{ + Compose *compose; + PrefsAccount *account; + GtkTextView *text; + GtkTextBuffer *buffer; + GtkTextMark *mark; + GtkTextIter iter; + FILE *fp; + gchar buf[BUFFSIZE]; + const gchar *str; + GtkWidget *focus_widget = NULL; + GtkItemFactory *ifactory; + + g_return_val_if_fail(msginfo != NULL, NULL); + g_return_val_if_fail(msginfo->folder != NULL, NULL); + + account = account_find_from_msginfo(msginfo); + if (!account) account = cur_account; + g_return_val_if_fail(account != NULL, NULL); + + if (msginfo->folder->stype == F_DRAFT || + msginfo->folder->stype == F_QUEUE) { + compose = compose_find_window_by_target(msginfo); + if (compose) { + debug_print + ("compose_reedit(): existing window found.\n"); + gtk_window_present(GTK_WINDOW(compose->window)); + return compose; + } + } + + compose = compose_create(account, COMPOSE_REEDIT); + compose->targetinfo = procmsg_msginfo_copy(msginfo); + + if (compose_parse_header(compose, msginfo) < 0) + return compose; + compose_parse_source_msg(compose, msginfo); + + undo_block(compose->undostruct); + + compose_reedit_set_entry(compose, msginfo); + + text = GTK_TEXT_VIEW(compose->text); + buffer = gtk_text_view_get_buffer(text); + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark); + + if ((fp = procmime_get_first_text_content(msginfo, NULL)) == NULL) + g_warning(_("Can't get text part\n")); + else { + gboolean prev_autowrap = compose->autowrap; + + compose->autowrap = FALSE; + while (fgets(buf, sizeof(buf), fp) != NULL) { + strcrchomp(buf); + gtk_text_buffer_insert(buffer, &iter, buf, -1); + } + compose_enable_sig(compose); + compose->autowrap = prev_autowrap; + fclose(fp); + } + compose_attach_parts(compose, msginfo); + + gtk_text_buffer_get_start_iter(buffer, &iter); + gtk_text_buffer_place_cursor(buffer, &iter); + + if (account->protocol != A_NNTP) { + str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry)); + if (!str || *str == '\0') + focus_widget = compose->to_entry; + } else { + str = gtk_entry_get_text(GTK_ENTRY(compose->newsgroups_entry)); + if (!str || *str == '\0') + focus_widget = compose->newsgroups_entry; + } + if (!focus_widget) { + str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry)); + if (!str || *str == '\0') + focus_widget = compose->subject_entry; + } + if (focus_widget) + gtk_widget_grab_focus(focus_widget); + else + gtk_widget_grab_focus(compose->text); + + undo_unblock(compose->undostruct); + + compose_connect_changed_callbacks(compose); + compose_set_title(compose); + + ifactory = gtk_item_factory_from_widget(compose->menubar); + if (compose->use_mdn) { + menu_set_active(ifactory, "/Tools/Request disposition notification", TRUE); + } + menu_set_active(ifactory, "/Edit/Auto wrapping", compose->autowrap); +#if USE_GTKSPELL + menu_set_active(ifactory, "/Tools/Check spell", compose->check_spell); + compose_change_spell_lang_menu(compose, compose->spell_lang); +#endif +#if USE_GPGME + menu_set_active(ifactory, "/Tools/PGP Sign", compose->use_signing); + menu_set_active(ifactory, "/Tools/PGP Encrypt", compose->use_encryption); +#endif + + syl_plugin_signal_emit("compose-created", compose); + + if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0) + compose->autosave_tag = + g_timeout_add_full(G_PRIORITY_LOW, + prefs_common.autosave_itv * 60 * 1000, + autosave_timeout, compose, NULL); + if (prefs_common.auto_exteditor) + compose_exec_ext_editor(compose); + + return compose; +} + +GList *compose_get_compose_list(void) +{ + return compose_list; +} + +static void compose_entry_show(Compose *compose, ComposeEntryType type) +{ + GtkItemFactory *ifactory; + + ifactory = gtk_item_factory_from_widget(compose->menubar); + + switch (type) { + case COMPOSE_ENTRY_CC: + menu_set_active(ifactory, "/View/Cc", TRUE); + break; + case COMPOSE_ENTRY_BCC: + menu_set_active(ifactory, "/View/Bcc", TRUE); + break; + case COMPOSE_ENTRY_REPLY_TO: + menu_set_active(ifactory, "/View/Reply-To", TRUE); + break; + case COMPOSE_ENTRY_FOLLOWUP_TO: + menu_set_active(ifactory, "/View/Followup-To", TRUE); + break; + case COMPOSE_ENTRY_TO: + menu_set_active(ifactory, "/View/To", TRUE); + break; + default: + break; + } +} + +static GtkEntry *compose_get_entry(Compose *compose, ComposeEntryType type) +{ + GtkEntry *entry; + + switch (type) { + case COMPOSE_ENTRY_CC: + entry = GTK_ENTRY(compose->cc_entry); + break; + case COMPOSE_ENTRY_BCC: + entry = GTK_ENTRY(compose->bcc_entry); + break; + case COMPOSE_ENTRY_REPLY_TO: + entry = GTK_ENTRY(compose->reply_entry); + break; + case COMPOSE_ENTRY_SUBJECT: + entry = GTK_ENTRY(compose->subject_entry); + break; + case COMPOSE_ENTRY_NEWSGROUPS: + entry = GTK_ENTRY(compose->newsgroups_entry); + break; + case COMPOSE_ENTRY_FOLLOWUP_TO: + entry = GTK_ENTRY(compose->followup_entry); + break; + case COMPOSE_ENTRY_TO: + default: + entry = GTK_ENTRY(compose->to_entry); + break; + } + + return entry; +} + +void compose_entry_set(Compose *compose, const gchar *text, + ComposeEntryType type) +{ + GtkEntry *entry; + + if (!text) return; + + compose_entry_show(compose, type); + entry = compose_get_entry(compose, type); + + gtk_entry_set_text(entry, text); +} + +void compose_entry_append(Compose *compose, const gchar *text, + ComposeEntryType type) +{ + GtkEntry *entry; + const gchar *str; + gint pos; + + if (!text || *text == '\0') return; + + compose_entry_show(compose, type); + entry = compose_get_entry(compose, type); + + if (type != COMPOSE_ENTRY_SUBJECT) { + str = gtk_entry_get_text(entry); + if (*str != '\0') { + pos = entry->text_length; + gtk_editable_insert_text(GTK_EDITABLE(entry), + ", ", -1, &pos); + } + } + + pos = entry->text_length; + gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &pos); +} + +gchar *compose_entry_get_text(Compose *compose, ComposeEntryType type) +{ + GtkEntry *entry; + + entry = compose_get_entry(compose, type); + return gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1); +} + +static void compose_entries_set(Compose *compose, const gchar *mailto) +{ + gchar *to = NULL; + gchar *cc = NULL; + gchar *subject = NULL; + gchar *inreplyto = NULL; + gchar *body = NULL; + + scan_mailto_url(mailto, &to, &cc, NULL, &subject, &inreplyto, &body); + + if (to) + compose_entry_set(compose, to, COMPOSE_ENTRY_TO); + if (cc) + compose_entry_set(compose, cc, COMPOSE_ENTRY_CC); + if (subject) + compose_entry_set(compose, subject, COMPOSE_ENTRY_SUBJECT); + if (inreplyto) { + if (strchr(inreplyto, '<')) + extract_parenthesis(inreplyto, '<', '>'); + remove_space(inreplyto); + compose->inreplyto = g_strdup(inreplyto); + } + if (body) { + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + GtkTextMark *mark; + GtkTextIter iter; + gboolean prev_autowrap = compose->autowrap; + + compose->autowrap = FALSE; + + buffer = gtk_text_view_get_buffer(text); + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark); + + gtk_text_buffer_insert(buffer, &iter, body, -1); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + + compose->autowrap = prev_autowrap; + if (compose->autowrap) + compose_wrap_all(compose); + } + + g_free(to); + g_free(cc); + g_free(subject); + g_free(inreplyto); + g_free(body); +} + +static void compose_entries_set_from_item(Compose *compose, FolderItem *item, + ComposeMode mode) +{ + g_return_if_fail(item != NULL); + + if (item->auto_to) { + if (mode != COMPOSE_REPLY || item->use_auto_to_on_reply) + compose_entry_set(compose, item->auto_to, + COMPOSE_ENTRY_TO); + } + if (item->auto_cc) + compose_entry_set(compose, item->auto_cc, COMPOSE_ENTRY_CC); + if (item->auto_bcc) + compose_entry_set(compose, item->auto_bcc, COMPOSE_ENTRY_BCC); + if (item->auto_replyto) + compose_entry_set(compose, item->auto_replyto, + COMPOSE_ENTRY_REPLY_TO); +} + +#undef ACTIVATE_MENU + +static gint compose_parse_header(Compose *compose, MsgInfo *msginfo) +{ + static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE}, + {"Cc:", NULL, TRUE}, + {"References:", NULL, FALSE}, + {"Bcc:", NULL, TRUE}, + {"Newsgroups:", NULL, TRUE}, + {"Followup-To:", NULL, TRUE}, + {"List-Post:", NULL, FALSE}, + {"Content-Type:",NULL, FALSE}, + {NULL, NULL, FALSE}}; + + enum + { + H_REPLY_TO = 0, + H_CC = 1, + H_REFERENCES = 2, + H_BCC = 3, + H_NEWSGROUPS = 4, + H_FOLLOWUP_TO = 5, + H_LIST_POST = 6, + H_CONTENT_TYPE = 7 + }; + + FILE *fp; + gchar *charset = NULL; + + g_return_val_if_fail(msginfo != NULL, -1); + + if ((fp = procmsg_open_message(msginfo)) == NULL) return -1; + procheader_get_header_fields(fp, hentry); + fclose(fp); + + if (hentry[H_CONTENT_TYPE].body != NULL) { + procmime_scan_content_type_str(hentry[H_CONTENT_TYPE].body, + NULL, &charset, NULL, NULL); + g_free(hentry[H_CONTENT_TYPE].body); + hentry[H_CONTENT_TYPE].body = NULL; + } + if (hentry[H_REPLY_TO].body != NULL) { + if (hentry[H_REPLY_TO].body[0] != '\0') { + compose->replyto = + conv_unmime_header(hentry[H_REPLY_TO].body, + charset); + } + g_free(hentry[H_REPLY_TO].body); + hentry[H_REPLY_TO].body = NULL; + } + if (hentry[H_CC].body != NULL) { + compose->cc = conv_unmime_header(hentry[H_CC].body, charset); + g_free(hentry[H_CC].body); + hentry[H_CC].body = NULL; + } + if (hentry[H_REFERENCES].body != NULL) { + if (compose->mode == COMPOSE_REEDIT) + compose->references = hentry[H_REFERENCES].body; + else { + compose->references = compose_parse_references + (hentry[H_REFERENCES].body, msginfo->msgid); + g_free(hentry[H_REFERENCES].body); + } + hentry[H_REFERENCES].body = NULL; + } + if (hentry[H_BCC].body != NULL) { + if (compose->mode == COMPOSE_REEDIT) + compose->bcc = + conv_unmime_header(hentry[H_BCC].body, charset); + g_free(hentry[H_BCC].body); + hentry[H_BCC].body = NULL; + } + if (hentry[H_NEWSGROUPS].body != NULL) { + compose->newsgroups = hentry[H_NEWSGROUPS].body; + hentry[H_NEWSGROUPS].body = NULL; + } + if (hentry[H_FOLLOWUP_TO].body != NULL) { + if (hentry[H_FOLLOWUP_TO].body[0] != '\0') { + compose->followup_to = + conv_unmime_header(hentry[H_FOLLOWUP_TO].body, + charset); + } + g_free(hentry[H_FOLLOWUP_TO].body); + hentry[H_FOLLOWUP_TO].body = NULL; + } + if (hentry[H_LIST_POST].body != NULL) { + gchar *to = NULL; + + extract_address(hentry[H_LIST_POST].body); + if (hentry[H_LIST_POST].body[0] != '\0') { + scan_mailto_url(hentry[H_LIST_POST].body, + &to, NULL, NULL, NULL, NULL, NULL); + if (to) { + g_free(compose->ml_post); + compose->ml_post = to; + } + } + g_free(hentry[H_LIST_POST].body); + hentry[H_LIST_POST].body = NULL; + } + + g_free(charset); + + if (compose->mode == COMPOSE_REEDIT) { + if (msginfo->inreplyto && *msginfo->inreplyto) + compose->inreplyto = g_strdup(msginfo->inreplyto); + return 0; + } + + if (msginfo->msgid && *msginfo->msgid) + compose->inreplyto = g_strdup(msginfo->msgid); + + if (!compose->references) { + if (msginfo->msgid && *msginfo->msgid) { + if (msginfo->inreplyto && *msginfo->inreplyto) + compose->references = + g_strdup_printf("<%s>\n\t<%s>", + msginfo->inreplyto, + msginfo->msgid); + else + compose->references = + g_strconcat("<", msginfo->msgid, ">", + NULL); + } else if (msginfo->inreplyto && *msginfo->inreplyto) { + compose->references = + g_strconcat("<", msginfo->inreplyto, ">", + NULL); + } + } + + return 0; +} + +static gchar *compose_parse_references(const gchar *ref, const gchar *msgid) +{ + GSList *ref_id_list, *cur; + GString *new_ref; + gchar *new_ref_str; + + ref_id_list = references_list_append(NULL, ref); + if (!ref_id_list) return NULL; + if (msgid && *msgid) + ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid)); + + for (;;) { + gint len = 0; + + for (cur = ref_id_list; cur != NULL; cur = cur->next) + /* "<" + Message-ID + ">" + CR+LF+TAB */ + len += strlen((gchar *)cur->data) + 5; + + if (len > MAX_REFERENCES_LEN) { + /* remove second message-ID */ + if (ref_id_list && ref_id_list->next && + ref_id_list->next->next) { + g_free(ref_id_list->next->data); + ref_id_list = g_slist_remove + (ref_id_list, ref_id_list->next->data); + } else { + slist_free_strings(ref_id_list); + g_slist_free(ref_id_list); + return NULL; + } + } else + break; + } + + new_ref = g_string_new(""); + for (cur = ref_id_list; cur != NULL; cur = cur->next) { + if (new_ref->len > 0) + g_string_append(new_ref, "\n\t"); + g_string_sprintfa(new_ref, "<%s>", (gchar *)cur->data); + } + + slist_free_strings(ref_id_list); + g_slist_free(ref_id_list); + + new_ref_str = new_ref->str; + g_string_free(new_ref, FALSE); + + return new_ref_str; +} + +static gint compose_parse_source_msg(Compose *compose, MsgInfo *msginfo) +{ + static HeaderEntry hentry[] = {{"X-Sylpheed-Reply:", NULL, FALSE}, + {"X-Sylpheed-Forward:", NULL, FALSE}, + {"REP:", NULL, FALSE}, + {"FWD:", NULL, FALSE}, + {"Disposition-Notification-To:", NULL, FALSE}, + {"X-Sylpheed-Compose-AutoWrap:", NULL, FALSE}, + {"X-Sylpheed-Compose-CheckSpell:", NULL, FALSE}, + {"X-Sylpheed-Compose-SpellLang:", NULL, FALSE}, + {"X-Sylpheed-Compose-UseSigning:", NULL, FALSE}, + {"X-Sylpheed-Compose-UseEncryption:", NULL, FALSE}, + {NULL, NULL, FALSE}}; + + enum + { + H_X_SYLPHEED_REPLY = 0, + H_X_SYLPHEED_FORWARD = 1, + H_REP = 2, + H_FWD = 3, + H_MDN = 4, + H_X_SYLPHEED_COMPOSE_AUTOWRAP = 5, + H_X_SYLPHEED_COMPOSE_CHECKSPELL = 6, + H_X_SYLPHEED_COMPOSE_SPELLLANG = 7, + H_X_SYLPHEED_COMPOSE_USESIGNING = 8, + H_X_SYLPHEED_COMPOSE_USEENCRYPTION = 9 + }; + + gchar *file; + FILE *fp; + gchar *str; + gchar buf[BUFFSIZE]; + gint hnum; + + g_return_val_if_fail(msginfo != NULL, -1); + + file = procmsg_get_message_file(msginfo); + + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + return -1; + } + + while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry)) + != -1) { + str = buf + strlen(hentry[hnum].name); + while (g_ascii_isspace(*str)) + ++str; + if ((hnum == H_X_SYLPHEED_REPLY || hnum == H_REP) && + !compose->reply_target) { + compose->reply_target = g_strdup(str); + } else if ((hnum == H_X_SYLPHEED_FORWARD || hnum == H_FWD) && + !compose->forward_targets) { + compose->forward_targets = g_strdup(str); + } else if (hnum == H_MDN) { + compose->use_mdn = TRUE; + } else if (hnum == H_X_SYLPHEED_COMPOSE_AUTOWRAP) { + if (g_ascii_strcasecmp(str, "TRUE") == 0) + compose->autowrap = TRUE; + else + compose->autowrap = FALSE; +#if USE_GTKSPELL + } else if (hnum == H_X_SYLPHEED_COMPOSE_CHECKSPELL) { + if (g_ascii_strcasecmp(str, "TRUE") == 0) + compose->check_spell = TRUE; + else + compose->check_spell = FALSE; + } else if (hnum == H_X_SYLPHEED_COMPOSE_SPELLLANG) { + g_free(compose->spell_lang); + compose->spell_lang = g_strdup(str); +#endif +#if USE_GPGME + } else if (hnum == H_X_SYLPHEED_COMPOSE_USESIGNING) { + if (g_ascii_strcasecmp(str, "TRUE") == 0) + compose->use_signing = TRUE; + else + compose->use_signing = FALSE; + } else if (hnum == H_X_SYLPHEED_COMPOSE_USEENCRYPTION) { + if (g_ascii_strcasecmp(str, "TRUE") == 0) + compose->use_encryption = TRUE; + else + compose->use_encryption = FALSE; +#endif + } + } + + fclose(fp); + g_free(file); + + return 0; +} + +static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo, + const gchar *fmt, const gchar *qmark, + const gchar *body) +{ + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + GtkTextMark *mark; + GtkTextIter iter; + static MsgInfo dummyinfo; + gchar *quote_str = NULL; + gchar *buf; + gchar *p, *lastp; + gint len; + gboolean prev_autowrap; + + if (!msginfo) + msginfo = &dummyinfo; + + if (qmark == NULL || *qmark == '\0') + qmark = "> "; + + quote_fmt_init(msginfo, NULL, NULL); + quote_fmt_scan_string(qmark); + quote_fmt_parse(); + + buf = quote_fmt_get_buffer(); + if (buf == NULL) + alertpanel_error(_("Quote mark format error.")); + else + quote_str = g_strdup(buf); + + if (fmt && *fmt != '\0') { + quote_fmt_init(msginfo, quote_str, body); + quote_fmt_scan_string(fmt); + quote_fmt_parse(); + + buf = quote_fmt_get_buffer(); + if (buf == NULL) { + alertpanel_error(_("Message reply/forward format error.")); + g_free(quote_str); + return NULL; + } + } else + buf = ""; + + g_free(quote_str); + + prev_autowrap = compose->autowrap; + compose->autowrap = FALSE; + + buffer = gtk_text_view_get_buffer(text); + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark); + + for (p = buf; *p != '\0'; ) { + lastp = strchr(p, '\n'); + len = lastp ? lastp - p + 1 : -1; + gtk_text_buffer_insert(buffer, &iter, p, len); + if (lastp) + p = lastp + 1; + else + break; + } + + compose->autowrap = prev_autowrap; + if (compose->autowrap) + compose_wrap_all(compose); + + return buf; +} + +static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo, + ComposeMode mode) +{ + GSList *cc_list = NULL; + GSList *cur; + GHashTable *to_table; + gboolean to_all = FALSE, to_ml = FALSE, ignore_replyto = FALSE; + gchar *from_str = NULL, *to_str = NULL, *cc_str = NULL, + *replyto_str = NULL; + gboolean address_only = prefs_common.reply_address_only; + + g_return_if_fail(compose->account != NULL); + g_return_if_fail(msginfo != NULL); + + switch (COMPOSE_MODE(mode)) { + case COMPOSE_REPLY_TO_SENDER: + ignore_replyto = TRUE; + break; + case COMPOSE_REPLY_TO_ALL: + to_all = TRUE; + break; + case COMPOSE_REPLY_TO_LIST: + to_ml = TRUE; + break; + default: + break; + } + + if (address_only) { + from_str = extract_addresses(msginfo->from); + to_str = extract_addresses(msginfo->to); + cc_str = extract_addresses(compose->cc); + replyto_str = extract_addresses(compose->replyto); + } else { + from_str = g_strdup(msginfo->from); + to_str = g_strdup(msginfo->to); + cc_str = g_strdup(compose->cc); + replyto_str = g_strdup(compose->replyto); + } + + if (compose->account->protocol != A_NNTP) { + if (to_ml && compose->ml_post) { + /* don't reply to list for confirmation request etc. */ + if ((!to_str || + !strcasestr_with_skip_quote(to_str, + compose->ml_post)) && + (!cc_str || + !strcasestr_with_skip_quote(cc_str, + compose->ml_post))) + to_ml = FALSE; + } + if (to_ml && compose->ml_post) { + compose_entry_set(compose, compose->ml_post, + COMPOSE_ENTRY_TO); + if (replyto_str && + !address_equal(replyto_str, compose->ml_post)) + compose_entry_set(compose, replyto_str, + COMPOSE_ENTRY_CC); + } else if (prefs_common.inherit_recipient_on_self_reply && + address_equal(compose->account->address, from_str)) { + compose_entry_set(compose, to_str, COMPOSE_ENTRY_TO); + if (to_all) { + compose_entry_set(compose, cc_str, + COMPOSE_ENTRY_CC); + to_all = FALSE; + } + } else { + compose_entry_set(compose, + (replyto_str && !ignore_replyto) + ? replyto_str + : from_str ? from_str : "", + COMPOSE_ENTRY_TO); + } + } else { + if (ignore_replyto) { + compose_entry_set(compose, from_str ? from_str : "", + COMPOSE_ENTRY_TO); + } else { + if (to_all) { + compose_entry_set + (compose, + (replyto_str && !ignore_replyto) + ? replyto_str + : from_str ? from_str : "", + COMPOSE_ENTRY_TO); + } + compose_entry_set(compose, + compose->followup_to ? compose->followup_to + : compose->newsgroups ? compose->newsgroups + : "", + COMPOSE_ENTRY_NEWSGROUPS); + } + } + + if (msginfo->subject && *msginfo->subject) { + gchar *buf; + gchar *p; + + buf = g_strdup(msginfo->subject); + + if (msginfo->folder && msginfo->folder->trim_compose_subject) + trim_subject(buf); + + while (!g_ascii_strncasecmp(buf, "Re:", 3)) { + p = buf + 3; + while (g_ascii_isspace(*p)) p++; + memmove(buf, p, strlen(p) + 1); + } + + compose_entry_set(compose, "Re: ", COMPOSE_ENTRY_SUBJECT); + compose_entry_append(compose, buf, COMPOSE_ENTRY_SUBJECT); + + g_free(buf); + } else + compose_entry_set(compose, "Re: ", COMPOSE_ENTRY_SUBJECT); + + if (!compose->replyto && to_ml && compose->ml_post) + goto done; + if (!to_all) + goto done; + + if (replyto_str && from_str) + cc_list = address_list_append_orig(cc_list, from_str); + cc_list = address_list_append_orig(cc_list, to_str); + cc_list = address_list_append_orig(cc_list, cc_str); + + to_table = g_hash_table_new(g_str_hash, g_str_equal); + if (replyto_str) { + gchar *replyto; + + replyto = g_strdup(replyto_str); + extract_address(replyto); + g_hash_table_insert(to_table, replyto, GINT_TO_POINTER(1)); + } else if (from_str) { + gchar *from; + + from = g_strdup(from_str); + extract_address(from); + g_hash_table_insert(to_table, from, GINT_TO_POINTER(1)); + } + if (compose->account->address) + g_hash_table_insert(to_table, + g_strdup(compose->account->address), + GINT_TO_POINTER(1)); + + /* remove duplicate addresses */ + for (cur = cc_list; cur != NULL; ) { + gchar *addr = (gchar *)cur->data; + GSList *next = cur->next; + gchar *addr_; + + addr_ = g_strdup(addr); + extract_address(addr_); + if (g_hash_table_lookup(to_table, addr_) != NULL) { + cc_list = g_slist_remove(cc_list, addr); + g_free(addr_); + g_free(addr); + } else + g_hash_table_insert(to_table, addr_, cur); + + cur = next; + } + hash_free_strings(to_table); + g_hash_table_destroy(to_table); + + if (cc_list) { + for (cur = cc_list; cur != NULL; cur = cur->next) + compose_entry_append(compose, (gchar *)cur->data, + COMPOSE_ENTRY_CC); + slist_free_strings(cc_list); + g_slist_free(cc_list); + } + +done: + g_free(from_str); + g_free(to_str); + g_free(cc_str); + g_free(replyto_str); +} + +static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo) +{ + g_return_if_fail(msginfo != NULL); + g_return_if_fail(compose->account != NULL); + + compose_entry_set(compose, msginfo->to , COMPOSE_ENTRY_TO); + compose_entry_set(compose, compose->cc , COMPOSE_ENTRY_CC); + compose_entry_set(compose, compose->bcc , COMPOSE_ENTRY_BCC); + compose_entry_set(compose, compose->replyto, COMPOSE_ENTRY_REPLY_TO); + if (compose->account->protocol == A_NNTP) { + compose_entry_set(compose, compose->newsgroups, + COMPOSE_ENTRY_NEWSGROUPS); + compose_entry_set(compose, compose->followup_to, + COMPOSE_ENTRY_FOLLOWUP_TO); + } + compose_entry_set(compose, msginfo->subject, COMPOSE_ENTRY_SUBJECT); +} + +static void compose_insert_sig(Compose *compose, gboolean append, + gboolean replace, gboolean scroll) +{ + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + GtkTextMark *mark; + GtkTextIter iter; + gchar *sig_str; + gboolean prev_autowrap; + + debug_print("compose_insert_sig: append:%d replace:%d scroll:%d\n", + append, replace, scroll); + + g_return_if_fail(compose->account != NULL); + + prev_autowrap = compose->autowrap; + compose->autowrap = FALSE; + + buffer = gtk_text_view_get_buffer(text); + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark); + + if (replace) { + GtkTextIter start_iter, end_iter; + + gtk_text_buffer_get_start_iter(buffer, &start_iter); + gtk_text_buffer_get_end_iter(buffer, &iter); + + while (gtk_text_iter_begins_tag + (&start_iter, compose->sig_tag) || + gtk_text_iter_forward_to_tag_toggle + (&start_iter, compose->sig_tag)) { + end_iter = start_iter; + if (gtk_text_iter_forward_to_tag_toggle + (&end_iter, compose->sig_tag)) { + gtk_text_buffer_delete + (buffer, &start_iter, &end_iter); + iter = start_iter; + } + } + } + + if (scroll) { + if (append) + gtk_text_buffer_get_end_iter(buffer, &iter); + else + gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark); + } + + sig_str = compose_get_signature_str(compose); + if (sig_str) { + if (!replace) + gtk_text_buffer_insert(buffer, &iter, "\n\n", 2); + else if (!gtk_text_iter_starts_line(&iter)) + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + gtk_text_buffer_insert_with_tags + (buffer, &iter, sig_str, -1, compose->sig_tag, NULL); + g_free(sig_str); + if (scroll) + gtk_text_buffer_place_cursor(buffer, &iter); + } + + compose->autowrap = prev_autowrap; + if (compose->autowrap) + compose_wrap_all(compose); + + if (scroll) + gtk_text_view_scroll_mark_onscreen(text, mark); +} + +static void compose_enable_sig(Compose *compose) +{ + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + GtkTextIter iter, start, end; + gchar *sig_str; + + g_return_if_fail(compose->account != NULL); + + buffer = gtk_text_view_get_buffer(text); + gtk_text_buffer_get_start_iter(buffer, &iter); + + sig_str = compose_get_signature_str(compose); + if (!sig_str) + return; + + if (gtkut_text_buffer_find(buffer, &iter, sig_str, TRUE, &start)) { + end = start; + gtk_text_iter_forward_chars(&end, g_utf8_strlen(sig_str, -1)); + gtk_text_buffer_apply_tag(buffer, compose->sig_tag, + &start, &end); + } + + g_free(sig_str); +} + +static gchar *compose_get_signature_str(Compose *compose) +{ + gchar *sig_path; + gchar *sig_body = NULL; + gchar *utf8_sig_body = NULL; + gchar *utf8_sig_str = NULL; + + g_return_val_if_fail(compose->account != NULL, NULL); + + if (compose->account->sig_type == SIG_DIRECT) { + gchar *sig_text; + gchar *p, *sp; + gint sig_index; + + sig_index = gtk_combo_box_get_active(GTK_COMBO_BOX(compose->sig_combo)); + if (sig_index > 0) + sig_text = compose->account->sig_texts[sig_index]; + else + sig_text = compose->account->sig_text; + if (!sig_text) + return NULL; + + sp = sig_text; + p = sig_body = g_malloc(strlen(sig_text) + 1); + while (*sp) { + if (*sp == '\\' && *(sp + 1) == 'n') { + *p++ = '\n'; + sp += 2; + } else + *p++ = *sp++; + } + *p = '\0'; + + if (prefs_common.sig_sep) { + utf8_sig_str = g_strconcat(prefs_common.sig_sep, "\n", + sig_body, NULL); + g_free(sig_body); + } else + utf8_sig_str = sig_body; + + return utf8_sig_str; + } + + if (!compose->account->sig_path) + return NULL; + + if (g_path_is_absolute(compose->account->sig_path) || + compose->account->sig_type == SIG_COMMAND) + sig_path = g_strdup(compose->account->sig_path); + else { +#ifdef G_OS_WIN32 + sig_path = g_strconcat(get_rc_dir(), +#else + sig_path = g_strconcat(get_home_dir(), +#endif + G_DIR_SEPARATOR_S, + compose->account->sig_path, NULL); + } + + if (compose->account->sig_type == SIG_FILE) { + if (!is_file_or_fifo_exist(sig_path)) { + debug_print("can't open signature file: %s\n", + sig_path); + g_free(sig_path); + return NULL; + } + } + + if (compose->account->sig_type == SIG_COMMAND) + sig_body = get_command_output(sig_path); + else { + gchar *tmp; + + tmp = file_read_to_str(sig_path); + if (!tmp) + return NULL; + sig_body = normalize_newlines(tmp); + g_free(tmp); + } + g_free(sig_path); + + if (sig_body) { + gint error = 0; + + utf8_sig_body = conv_codeset_strdup_full + (sig_body, conv_get_locale_charset_str(), + CS_INTERNAL, &error); + if (!utf8_sig_body || error != 0) { + if (g_utf8_validate(sig_body, -1, NULL) == TRUE) { + g_free(utf8_sig_body); + utf8_sig_body = conv_utf8todisp(sig_body, NULL); + } + } else { + g_free(sig_body); + sig_body = utf8_sig_body; + utf8_sig_body = conv_utf8todisp(sig_body, NULL); + } + g_free(sig_body); + } + + if (prefs_common.sig_sep) { + utf8_sig_str = g_strconcat(prefs_common.sig_sep, "\n", + utf8_sig_body, NULL); + g_free(utf8_sig_body); + } else + utf8_sig_str = utf8_sig_body; + + return utf8_sig_str; +} + +static void compose_insert_file(Compose *compose, const gchar *file, + gboolean scroll) +{ + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + GtkTextMark *mark; + GtkTextIter iter; + const gchar *cur_encoding; + gchar buf[BUFFSIZE]; + gint len; + FILE *fp; + gboolean prev_autowrap; + CharSet enc; + gchar *tmp_file = NULL; + + g_return_if_fail(file != NULL); + + enc = conv_check_file_encoding(file); + if (enc == C_UTF_16 || enc == C_UTF_16BE || enc == C_UTF_16LE) { + tmp_file = get_tmp_file(); + if (conv_copy_file(file, tmp_file, conv_get_charset_str(enc)) < 0) { + g_warning("compose_insert_file: Cannot convert UTF-16 file %s to UTF-8\n", file); + g_free(tmp_file); + tmp_file = NULL; + } + } + + if (tmp_file) { + if ((fp = g_fopen(tmp_file, "rb")) == NULL) { + FILE_OP_ERROR(tmp_file, "fopen"); + g_unlink(tmp_file); + g_free(tmp_file); + return; + } + } else { + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + return; + } + } + + prev_autowrap = compose->autowrap; + compose->autowrap = FALSE; + + buffer = gtk_text_view_get_buffer(text); + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark); + + cur_encoding = conv_get_locale_charset_str(); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + gchar *str; + gint error = 0; + + if (enc == C_UTF_8) { + str = conv_utf8todisp(buf, NULL); + } else { + str = conv_codeset_strdup_full(buf, cur_encoding, + CS_INTERNAL, &error); + if (!str || error != 0) { + if (g_utf8_validate(buf, -1, NULL) == TRUE) { + g_free(str); + str = g_strdup(buf); + } + } + if (!str) continue; + } + + /* strip if DOS/Windows file, + replace with if Macintosh file. */ + strcrchomp(str); + len = strlen(str); + if (len > 0 && str[len - 1] != '\n') { + while (--len >= 0) + if (str[len] == '\r') str[len] = '\n'; + } + + gtk_text_buffer_insert(buffer, &iter, str, -1); + g_free(str); + } + + fclose(fp); + if (tmp_file) { + g_unlink(tmp_file); + g_free(tmp_file); + } + + compose->autowrap = prev_autowrap; + if (compose->autowrap) + compose_wrap_all(compose); + + if (scroll) + gtk_text_view_scroll_mark_onscreen(text, mark); +} + +void compose_attach_append(Compose *compose, const gchar *file, + const gchar *filename, + const gchar *content_type) +{ + GtkTreeIter iter; + AttachInfo *ainfo; + FILE *fp; + off_t size; + + g_return_if_fail(file != NULL); + g_return_if_fail(*file != '\0'); + + if (!is_file_exist(file)) { + g_warning(_("File %s doesn't exist\n"), file); + return; + } + if ((size = get_file_size(file)) < 0) { + g_warning(_("Can't get file size of %s\n"), file); + return; + } + if (size == 0) { + manage_window_focus_in(compose->window, NULL, NULL); + alertpanel_notice(_("File %s is empty."), file); + return; + } + if ((fp = g_fopen(file, "rb")) == NULL) { + manage_window_focus_in(compose->window, NULL, NULL); + alertpanel_error(_("Can't read %s."), file); + return; + } + fclose(fp); + + if (!compose->use_attach) { + GtkItemFactory *ifactory; + + ifactory = gtk_item_factory_from_widget(compose->menubar); + menu_set_active(ifactory, "/View/Attachment", TRUE); + } + + ainfo = g_new0(AttachInfo, 1); + ainfo->file = g_strdup(file); + + if (content_type) { + ainfo->content_type = g_strdup(content_type); + if (!g_ascii_strcasecmp(content_type, "message/rfc822")) { + MsgInfo *msginfo; + MsgFlags flags = {0, 0}; + const gchar *name; + + if (procmime_get_encoding_for_text_file(file) == ENC_7BIT) + ainfo->encoding = ENC_7BIT; + else + ainfo->encoding = ENC_8BIT; + + msginfo = procheader_parse_file(file, flags, FALSE); + if (msginfo && msginfo->subject) + name = msginfo->subject; + else + name = g_basename(filename ? filename : file); + + ainfo->name = g_strdup_printf(_("Message: %s"), name); + + procmsg_msginfo_free(msginfo); + } else { + if (!g_ascii_strncasecmp(content_type, "text", 4)) + ainfo->encoding = procmime_get_encoding_for_text_file(file); + else + ainfo->encoding = ENC_BASE64; + ainfo->name = g_strdup + (g_basename(filename ? filename : file)); + } + } else { + ainfo->content_type = procmime_get_mime_type(file); + if (!ainfo->content_type) { + ainfo->content_type = + g_strdup("application/octet-stream"); + ainfo->encoding = ENC_BASE64; + } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4)) + ainfo->encoding = + procmime_get_encoding_for_text_file(file); + else + ainfo->encoding = ENC_BASE64; + ainfo->name = g_strdup(g_basename(filename ? filename : file)); + } + ainfo->size = size; + + gtk_list_store_append(compose->attach_store, &iter); + gtk_list_store_set(compose->attach_store, &iter, + COL_MIMETYPE, ainfo->content_type, + COL_SIZE, to_human_readable(ainfo->size), + COL_NAME, ainfo->name, + COL_ATTACH_INFO, ainfo, + -1); + + syl_plugin_signal_emit("compose-attach-changed", compose); +} + +static void compose_attach_parts(Compose *compose, MsgInfo *msginfo) +{ + MimeInfo *mimeinfo; + MimeInfo *child; + gchar *infile; + gchar *outfile; + + mimeinfo = procmime_scan_message(msginfo); + if (!mimeinfo) return; + + infile = procmsg_get_message_file_path(msginfo); + + child = mimeinfo; + while (child != NULL) { + if (child->children || child->mime_type == MIME_MULTIPART) + goto next; + if (child->mime_type != MIME_MESSAGE_RFC822 && + child->mime_type != MIME_IMAGE && + child->mime_type != MIME_AUDIO && + child->mime_type != MIME_VIDEO && + !child->filename && !child->name) + goto next; + + outfile = procmime_get_tmp_file_name(child); + if (procmime_get_part(outfile, infile, child) < 0) { + g_warning(_("Can't get the part of multipart message.")); + g_free(outfile); + goto next; + } + + compose_attach_append + (compose, outfile, + child->filename ? child->filename : child->name, + child->content_type); + + g_free(outfile); + +next: + if (child->mime_type == MIME_MESSAGE_RFC822) + child = child->next; + else + child = procmime_mimeinfo_next(child); + } + + g_free(infile); + procmime_mimeinfo_free_all(mimeinfo); +} + +#define INDENT_CHARS ">|#" + +typedef enum { + WAIT_FOR_INDENT_CHAR, + WAIT_FOR_INDENT_CHAR_OR_SPACE, +} IndentState; + +/* return indent length, we allow: + indent characters followed by indent characters or spaces/tabs, + alphabets and numbers immediately followed by indent characters, + and the repeating sequences of the above + If quote ends with multiple spaces, only the first one is included. */ +static gchar *compose_get_quote_str(GtkTextBuffer *buffer, + const GtkTextIter *start, gint *len) +{ + GtkTextIter iter = *start; + gunichar wc; + gchar ch[6]; + gint clen; + IndentState state = WAIT_FOR_INDENT_CHAR; + gboolean is_space; + gboolean is_indent; + gint alnum_count = 0; + gint space_count = 0; + gint quote_len = 0; + + while (!gtk_text_iter_ends_line(&iter)) { + wc = gtk_text_iter_get_char(&iter); + if (g_unichar_iswide(wc)) + break; + clen = g_unichar_to_utf8(wc, ch); + if (clen != 1) + break; + + is_indent = strchr(INDENT_CHARS, ch[0]) ? TRUE : FALSE; + is_space = g_unichar_isspace(wc); + + if (state == WAIT_FOR_INDENT_CHAR) { + if (!is_indent && !g_unichar_isalnum(wc)) + break; + if (is_indent) { + quote_len += alnum_count + space_count + 1; + alnum_count = space_count = 0; + state = WAIT_FOR_INDENT_CHAR_OR_SPACE; + } else + alnum_count++; + } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) { + if (!is_indent && !is_space && !g_unichar_isalnum(wc)) + break; + if (is_space) + space_count++; + else if (is_indent) { + quote_len += alnum_count + space_count + 1; + alnum_count = space_count = 0; + } else { + alnum_count++; + state = WAIT_FOR_INDENT_CHAR; + } + } + + gtk_text_iter_forward_char(&iter); + } + + if (quote_len > 0 && space_count > 0) + quote_len++; + + if (len) + *len = quote_len; + + if (quote_len > 0) { + iter = *start; + gtk_text_iter_forward_chars(&iter, quote_len); + return gtk_text_buffer_get_text(buffer, start, &iter, FALSE); + } + + return NULL; +} + +/* return TRUE if the line is itemized */ +static gboolean compose_is_itemized(GtkTextBuffer *buffer, + const GtkTextIter *start) +{ + GtkTextIter iter = *start; + gunichar wc; + gchar ch[6]; + gint clen; + + if (gtk_text_iter_ends_line(&iter)) + return FALSE; + + while (1) { + wc = gtk_text_iter_get_char(&iter); + if (!g_unichar_isspace(wc)) + break; + gtk_text_iter_forward_char(&iter); + if (gtk_text_iter_ends_line(&iter)) + return FALSE; + } + + clen = g_unichar_to_utf8(wc, ch); + + /* (1), 2), 3. etc. */ + if ((clen == 1 && ch[0] == '(') || g_unichar_isdigit(wc)) { + gboolean digit_appeared = FALSE; + + if (ch[0] == '(') + gtk_text_iter_forward_char(&iter); + + while (1) { + wc = gtk_text_iter_get_char(&iter); + clen = g_unichar_to_utf8(wc, ch); + if (g_unichar_isdigit(wc)) { + digit_appeared = TRUE; + gtk_text_iter_forward_char(&iter); + if (gtk_text_iter_ends_line(&iter)) + return FALSE; + } else if (clen == 1 && + (ch[0] == ')' || ch[0] == '.')) { + if (!digit_appeared) + return FALSE; + gtk_text_iter_forward_char(&iter); + if (gtk_text_iter_ends_line(&iter)) + return TRUE; + wc = gtk_text_iter_get_char(&iter); + if (g_unichar_isspace(wc)) + return TRUE; + else + return FALSE; + } else + return FALSE; + } + } + + if (clen != 1) + return FALSE; + if (!strchr("*-+", ch[0])) + return FALSE; + + gtk_text_iter_forward_char(&iter); + if (gtk_text_iter_ends_line(&iter)) + return FALSE; + wc = gtk_text_iter_get_char(&iter); + if (g_unichar_isspace(wc)) + return TRUE; + else if (ch[0] == '-') { + /* -- */ + clen = g_unichar_to_utf8(wc, ch); + if (clen == 1 && ch[0] == '-') + return TRUE; + } + + return FALSE; +} + +static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer, + const GtkTextIter *start, + GtkTextIter *break_pos, + gint max_col, + gint quote_len) +{ + GtkTextIter iter = *start, line_end = *start; + PangoLogAttr *attrs; + gchar *str; + gchar *p; + gint len; + gint i; + gint col = 0; + gint pos = 0; + gboolean can_break = FALSE; + gboolean do_break = FALSE; + gboolean prev_dont_break = FALSE; + + gtk_text_iter_forward_to_line_end(&line_end); + str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE); + len = g_utf8_strlen(str, -1); + /* g_print("breaking line: %d: %s (len = %d)\n", + gtk_text_iter_get_line(&iter), str, len); */ + attrs = g_new(PangoLogAttr, len + 1); + + pango_default_break(str, -1, NULL, attrs, len + 1); + + p = str; + + /* skip quote and leading spaces */ + for (i = 0; *p != '\0' && i < len; i++) { + gunichar wc; + + wc = g_utf8_get_char(p); + if (i >= quote_len && !g_unichar_isspace(wc)) + break; + if (g_unichar_iswide(wc)) + col += 2; + else if (*p == '\t') + col += 8; + else + col++; + p = g_utf8_next_char(p); + } + + for (; *p != '\0' && i < len; i++) { + PangoLogAttr *attr = attrs + i; + gunichar wc; + gint uri_len; + + if (attr->is_line_break && can_break && !prev_dont_break) + pos = i; + + /* don't wrap URI */ + if ((uri_len = get_uri_len(p)) > 0) { + col += uri_len; + if (pos > 0 && col > max_col) { + do_break = TRUE; + break; + } + i += uri_len - 1; + p += uri_len; + can_break = TRUE; + continue; + } + + wc = g_utf8_get_char(p); + if (g_unichar_iswide(wc)) { + col += 2; + if (prev_dont_break && can_break && attr->is_line_break) + pos = i; + } else if (*p == '\t') + col += 8; + else + col++; + if (pos > 0 && col > max_col) { + do_break = TRUE; + break; + } + + if (*p == '-' || *p == '/') + prev_dont_break = TRUE; + else + prev_dont_break = FALSE; + + p = g_utf8_next_char(p); + can_break = TRUE; + } + + debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col); + + g_free(attrs); + g_free(str); + + *break_pos = *start; + gtk_text_iter_set_line_offset(break_pos, pos); + + return do_break; +} + +static gboolean compose_join_next_line(GtkTextBuffer *buffer, + GtkTextIter *iter, + const gchar *quote_str) +{ + GtkTextIter iter_ = *iter, cur, prev, next, end; + PangoLogAttr attrs[3]; + gchar *str; + gchar *next_quote_str; + gunichar wc1, wc2; + gint quote_len; + gboolean keep_cursor = FALSE; + + if (!gtk_text_iter_forward_line(&iter_) || + gtk_text_iter_ends_line(&iter_)) + return FALSE; + + next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len); + + if ((quote_str || next_quote_str) && + strcmp2(quote_str, next_quote_str) != 0) { + g_free(next_quote_str); + return FALSE; + } + g_free(next_quote_str); + + end = iter_; + if (quote_len > 0) { + gtk_text_iter_forward_chars(&end, quote_len); + if (gtk_text_iter_ends_line(&end)) + return FALSE; + } + + /* don't join itemized lines */ + if (compose_is_itemized(buffer, &end)) + return FALSE; + + /* delete quote str */ + if (quote_len > 0) + gtk_text_buffer_delete(buffer, &iter_, &end); + + /* delete linebreak and extra spaces */ + prev = cur = iter_; + while (gtk_text_iter_backward_char(&cur)) { + wc1 = gtk_text_iter_get_char(&cur); + if (!g_unichar_isspace(wc1)) + break; + prev = cur; + } + next = cur = iter_; + while (!gtk_text_iter_ends_line(&cur)) { + wc1 = gtk_text_iter_get_char(&cur); + if (!g_unichar_isspace(wc1)) + break; + gtk_text_iter_forward_char(&cur); + next = cur; + } + if (!gtk_text_iter_equal(&prev, &next)) { + GtkTextMark *mark; + + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark); + if (gtk_text_iter_equal(&prev, &cur)) + keep_cursor = TRUE; + gtk_text_buffer_delete(buffer, &prev, &next); + } + iter_ = prev; + + /* insert space if required */ + gtk_text_iter_backward_char(&prev); + wc1 = gtk_text_iter_get_char(&prev); + wc2 = gtk_text_iter_get_char(&next); + gtk_text_iter_forward_char(&next); + str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE); + pango_default_break(str, -1, NULL, attrs, 3); + if (!attrs[1].is_line_break || + (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) { + gtk_text_buffer_insert(buffer, &iter_, " ", 1); + if (keep_cursor) { + gtk_text_iter_backward_char(&iter_); + gtk_text_buffer_place_cursor(buffer, &iter_); + } + } + g_free(str); + + *iter = iter_; + return TRUE; +} + +static void compose_wrap_paragraph(Compose *compose, GtkTextIter *par_iter) +{ + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + GtkTextIter iter, break_pos; + gchar *quote_str = NULL; + gint quote_len; + gboolean wrap_quote = prefs_common.linewrap_quote; + gboolean prev_autowrap = compose->autowrap; + + compose->autowrap = FALSE; + + buffer = gtk_text_view_get_buffer(text); + + if (par_iter) { + iter = *par_iter; + } else { + GtkTextMark *mark; + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark); + } + + /* move to paragraph start */ + gtk_text_iter_set_line_offset(&iter, 0); + if (gtk_text_iter_ends_line(&iter)) { + while (gtk_text_iter_ends_line(&iter) && + gtk_text_iter_forward_line(&iter)) + ; + } else { + while (gtk_text_iter_backward_line(&iter)) { + if (gtk_text_iter_ends_line(&iter)) { + gtk_text_iter_forward_line(&iter); + break; + } + } + } + + /* go until paragraph end (empty line) */ + while (!gtk_text_iter_ends_line(&iter)) { + quote_str = compose_get_quote_str(buffer, &iter, "e_len); + if (quote_str) { + if (!wrap_quote) { + gtk_text_iter_forward_line(&iter); + g_free(quote_str); + continue; + } + debug_print("compose_wrap_paragraph(): quote_str = '%s'\n", quote_str); + } + + if (compose_get_line_break_pos(buffer, &iter, &break_pos, + prefs_common.linewrap_len, + quote_len)) { + GtkTextIter prev, next, cur; + + gtk_text_buffer_insert(buffer, &break_pos, "\n", 1); + + /* remove trailing spaces */ + cur = break_pos; + gtk_text_iter_backward_char(&cur); + prev = next = cur; + while (!gtk_text_iter_starts_line(&cur)) { + gunichar wc; + + gtk_text_iter_backward_char(&cur); + wc = gtk_text_iter_get_char(&cur); + if (!g_unichar_isspace(wc)) + break; + prev = cur; + } + if (!gtk_text_iter_equal(&prev, &next)) { + gtk_text_buffer_delete(buffer, &prev, &next); + break_pos = next; + gtk_text_iter_forward_char(&break_pos); + } + + if (quote_str) + gtk_text_buffer_insert(buffer, &break_pos, + quote_str, -1); + + iter = break_pos; + compose_join_next_line(buffer, &iter, quote_str); + + /* move iter to current line start */ + gtk_text_iter_set_line_offset(&iter, 0); + } else { + /* move iter to next line start */ + iter = break_pos; + gtk_text_iter_forward_line(&iter); + } + + g_free(quote_str); + } + + if (par_iter) + *par_iter = iter; + + compose->autowrap = prev_autowrap; +} + +static void compose_wrap_all(Compose *compose) +{ + compose_wrap_all_full(compose, FALSE); +} + +static void compose_wrap_all_full(Compose *compose, gboolean autowrap) +{ + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + GtkTextIter iter; + + buffer = gtk_text_view_get_buffer(text); + + gtk_text_buffer_get_start_iter(buffer, &iter); + while (!gtk_text_iter_is_end(&iter)) + compose_wrap_paragraph(compose, &iter); +} + +static void compose_set_title(Compose *compose) +{ + gchar *str; + gchar *edited; + const gchar *subject; + + subject = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry)); + if (!subject || subject[0] == '\0') + subject = _("(No Subject)"); + + edited = compose->modified ? " *" : ""; + str = g_strdup_printf(_("%s - Compose%s"), subject, edited); + gtk_window_set_title(GTK_WINDOW(compose->window), str); + g_free(str); +} + +static void compose_select_account(Compose *compose, PrefsAccount *account, + gboolean init) +{ + GtkItemFactory *ifactory; + PrefsAccount *prev_account; + + g_return_if_fail(account != NULL); + + prev_account = compose->account; + compose->account = account; + + compose_set_title(compose); + + ifactory = gtk_item_factory_from_widget(compose->menubar); + + if (account->protocol == A_NNTP && + (init || prev_account->protocol != A_NNTP)) { + gtk_widget_show(compose->newsgroups_hbox); + gtk_widget_show(compose->newsgroups_entry); + gtk_table_set_row_spacing(GTK_TABLE(compose->table), 2, 4); + compose->use_newsgroups = TRUE; + + menu_set_active(ifactory, "/View/To", FALSE); + menu_set_sensitive(ifactory, "/View/To", TRUE); + menu_set_active(ifactory, "/View/Cc", FALSE); + menu_set_sensitive(ifactory, "/View/Followup-To", TRUE); + } else if (account->protocol != A_NNTP && + (init || prev_account->protocol == A_NNTP)) { + gtk_widget_hide(compose->newsgroups_hbox); + gtk_widget_hide(compose->newsgroups_entry); + gtk_table_set_row_spacing(GTK_TABLE(compose->table), 2, 0); + gtk_widget_queue_resize(compose->table_vbox); + compose->use_newsgroups = FALSE; + + menu_set_active(ifactory, "/View/To", TRUE); + menu_set_sensitive(ifactory, "/View/To", FALSE); + menu_set_active(ifactory, "/View/Cc", TRUE); + menu_set_active(ifactory, "/View/Followup-To", FALSE); + menu_set_sensitive(ifactory, "/View/Followup-To", FALSE); + } + + if (account->set_autocc) { + compose_entry_show(compose, COMPOSE_ENTRY_CC); + if (account->auto_cc && compose->mode != COMPOSE_REEDIT) + compose_entry_set(compose, account->auto_cc, + COMPOSE_ENTRY_CC); + } + if (account->set_autobcc) { + compose_entry_show(compose, COMPOSE_ENTRY_BCC); + if (account->auto_bcc && compose->mode != COMPOSE_REEDIT) + compose_entry_set(compose, account->auto_bcc, + COMPOSE_ENTRY_BCC); + } + if (account->set_autoreplyto) { + compose_entry_show(compose, COMPOSE_ENTRY_REPLY_TO); + if (account->auto_replyto && compose->mode != COMPOSE_REEDIT) + compose_entry_set(compose, account->auto_replyto, + COMPOSE_ENTRY_REPLY_TO); + } + +#if USE_GPGME + if (rfc2015_is_available()) { + if (account->default_sign) + menu_set_active(ifactory, "/Tools/PGP Sign", TRUE); + if (account->default_encrypt) + menu_set_active(ifactory, "/Tools/PGP Encrypt", TRUE); + } +#endif /* USE_GPGME */ + + compose_update_signature_menu(compose); + + if (!init && compose->mode != COMPOSE_REDIRECT && prefs_common.auto_sig) + compose_insert_sig(compose, TRUE, TRUE, FALSE); +} + +static void compose_update_signature_menu(Compose *compose) +{ + GtkTreeModel *model; + GtkListStore *store; + GtkTreeIter iter; + gboolean valid; + gint i; + gchar *name; + + if (!compose->account) + return; + + model = gtk_combo_box_get_model(GTK_COMBO_BOX(compose->sig_combo)); + store = GTK_LIST_STORE(model); + valid = gtk_tree_model_get_iter_first(model, &iter); + + for (i = 0; valid && i < sizeof(compose->account->sig_names) / + sizeof(compose->account->sig_names[0]); i++) { + if (compose->account->sig_names[i] && + compose->account->sig_names[i][0] != '\0') { + name = g_strdup_printf + ("%s", compose->account->sig_names[i]); + } else { + name = g_strdup_printf(_("Signature %d"), i + 1); + } + gtk_list_store_set(store, &iter, 0, name, -1); + g_free(name); + valid = gtk_tree_model_iter_next(model, &iter); + } + + g_signal_handlers_block_by_func(G_OBJECT(compose->sig_combo), + G_CALLBACK(sig_combo_changed), compose); + gtk_combo_box_set_active(GTK_COMBO_BOX(compose->sig_combo), 0); + g_signal_handlers_unblock_by_func(G_OBJECT(compose->sig_combo), + G_CALLBACK(sig_combo_changed), + compose); + + if (compose->account->sig_type != SIG_DIRECT) { + gtk_widget_set_sensitive(compose->sig_combo, FALSE); + } else { + gtk_widget_set_sensitive(compose->sig_combo, TRUE); + } +} + +static gboolean compose_check_for_valid_recipient(Compose *compose) +{ + const gchar *to_raw = "", *cc_raw = "", *bcc_raw = ""; + const gchar *newsgroups_raw = ""; + gchar *to, *cc, *bcc; + gchar *newsgroups; + gboolean valid; + + if (compose->use_to) + to_raw = gtk_entry_get_text(GTK_ENTRY(compose->to_entry)); + if (compose->use_cc) + cc_raw = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry)); + if (compose->use_bcc) + bcc_raw = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry)); + if (compose->use_newsgroups) + newsgroups_raw = gtk_entry_get_text + (GTK_ENTRY(compose->newsgroups_entry)); + + if (*to_raw == '\0' && *cc_raw == '\0' && *bcc_raw == '\0' && + *newsgroups_raw == '\0') + return FALSE; + + to = g_strstrip(g_strdup(to_raw)); + cc = g_strstrip(g_strdup(cc_raw)); + bcc = g_strstrip(g_strdup(bcc_raw)); + newsgroups = g_strstrip(g_strdup(newsgroups_raw)); + + if (*to == '\0' && *cc == '\0' && *bcc == '\0' && *newsgroups == '\0') + valid = FALSE; + else + valid = TRUE; + + g_free(newsgroups); + g_free(bcc); + g_free(cc); + g_free(to); + + return valid; +} + +static gboolean compose_check_entries(Compose *compose) +{ + const gchar *str; + + if (compose_check_for_valid_recipient(compose) == FALSE) { + alertpanel_error(_("Recipient is not specified.")); + return FALSE; + } + + str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry)); + if (*str == '\0') { + AlertValue aval; + + aval = alertpanel(_("Empty subject"), + _("Subject is empty. Send it anyway?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if (aval != G_ALERTDEFAULT) + return FALSE; + } + + return TRUE; +} + +static gboolean compose_check_attachments(Compose *compose) +{ + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + GtkTextIter iter, line_end; + gchar *line; + gchar **strv; + gint i; + gboolean attach_found = FALSE; + gboolean valid = TRUE; + + if (!prefs_common.check_attach) + return TRUE; + if (!prefs_common.check_attach_str) + return TRUE; + + if (compose->use_attach && + gtk_tree_model_iter_n_children + (GTK_TREE_MODEL(compose->attach_store), NULL) > 0) + return TRUE; + + buffer = gtk_text_view_get_buffer(text); + gtk_text_buffer_get_start_iter(buffer, &iter); + line_end = iter; + + strv = g_strsplit(prefs_common.check_attach_str, ",", -1); + for (i = 0; strv[i] != NULL; i++) + g_strstrip(strv[i]); + + while (valid) { + valid = gtk_text_iter_forward_to_line_end(&line_end); + line = gtk_text_buffer_get_text(buffer, &iter, &line_end, + FALSE); + iter = line_end; + if (get_quote_level(line) != -1) + continue; + + for (i = 0; strv[i] != NULL; i++) { + if (strv[i][0] == '\0') + continue; + if (strcasestr(line, strv[i])) { + attach_found = TRUE; + valid = FALSE; + break; + } + } + + g_free(line); + } + + g_strfreev(strv); + + if (attach_found) { + AlertValue aval; + + aval = alertpanel(_("Attachment is missing"), + _("There is no attachment. Send it without attachments?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if (aval != G_ALERTDEFAULT) + return FALSE; + } + + return TRUE; +} + +static gint check_recp_delete_event(GtkWidget *widget, GdkEventAny *event, + gint *state) +{ + *state = GTK_RESPONSE_CANCEL; + return TRUE; +} + +static gboolean check_recp_key_pressed(GtkWidget *widget, GdkEventKey *event, + gint *state) +{ + if (event && event->keyval == GDK_Escape) { + *state = GTK_RESPONSE_CANCEL; + return TRUE; + } + return FALSE; +} + +static void check_recp_ok(GtkWidget *widget, gint *state) +{ + *state = GTK_RESPONSE_OK; +} + +static void check_recp_cancel(GtkWidget *widget, gint *state) +{ + *state = GTK_RESPONSE_CANCEL; +} + +static gboolean compose_check_recipients(Compose *compose) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *image; + GtkWidget *vbox2; + GtkWidget *label; + GtkWidget *table; + GtkWidget *entry; + gchar buf[1024]; + const gchar *text; + GtkWidget *scrwin; + GtkWidget *treeview; + GtkTreeStore *store; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeIter iter, parent; + GtkWidget *hbbox; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + static PangoFontDescription *font_desc; + GtkStyle *style; + + GSList *cur, *to_list = NULL; + gboolean check_recp = FALSE; + gint state = 0; + + g_return_val_if_fail(compose->account != NULL, FALSE); + g_return_val_if_fail(compose->account->address != NULL, FALSE); + + if (!prefs_common.check_recipients) + return TRUE; + + if (prefs_common.check_recp_exclude) { + gchar **strv; + gint i; + + strv = g_strsplit(prefs_common.check_recp_exclude, ",", -1); + for (i = 0; strv[i] != NULL; i++) + g_strstrip(strv[i]); + + if (compose->use_to) { + text = gtk_entry_get_text(GTK_ENTRY(compose->to_entry)); + to_list = address_list_append_orig(NULL, text); + } + if (compose->use_cc) { + text = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry)); + to_list = address_list_append_orig(to_list, text); + } + if (compose->use_bcc) { + text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry)); + to_list = address_list_append_orig(to_list, text); + } + + for (cur = to_list; cur != NULL; cur = cur->next) { + for (i = 0; strv[i] != NULL; i++) { + if (strv[i][0] == '\0') + continue; + if (strcasestr((gchar *)cur->data, strv[i])) + break; + } + if (!strv[i]) { + /* not found in exclude list */ + check_recp = TRUE; + break; + } + } + + slist_free_strings(to_list); + g_slist_free(to_list); + to_list = NULL; + g_strfreev(strv); + } else + check_recp = TRUE; + + if (!check_recp) + return TRUE; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + gtk_window_set_title(GTK_WINDOW(window), _("Check recipients")); + gtk_window_set_position(GTK_WINDOW(window), + GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_widget_set_size_request(window, 480 * gtkut_get_dpi_multiplier(), -1); + gtk_widget_realize(window); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(check_recp_delete_event), &state); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(check_recp_key_pressed), &state); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add(GTK_CONTAINER(window), vbox); + + hbox = gtk_hbox_new(FALSE, 12); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 12); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + image = gtk_image_new_from_stock + (GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.0); + gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); + + vbox2 = gtk_vbox_new(FALSE, 12); + gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0); + + label = gtk_label_new(_("Check recipients")); + gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + + if (!font_desc) { + gint size; + + size = pango_font_description_get_size + (label->style->font_desc); + font_desc = pango_font_description_new(); + pango_font_description_set_weight + (font_desc, PANGO_WEIGHT_BOLD); + pango_font_description_set_size + (font_desc, size * PANGO_SCALE_LARGE); + } + if (font_desc) + gtk_widget_modify_font(label, font_desc); + + label = gtk_label_new + (_("Really send this mail to the following addresses?")); + gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + GTK_WIDGET_UNSET_FLAGS(label, GTK_CAN_FOCUS); + + table = gtk_table_new(2, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_table_set_row_spacings(GTK_TABLE(table), 4); + gtk_table_set_col_spacings(GTK_TABLE(table), 4); + + hbox = gtk_hbox_new(FALSE, 0); + label = gtk_label_new(prefs_common.trans_hdr ? _("From:") + : "From:"); + gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, 0, 1, + GTK_FILL, 0, 2, 0); + entry = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry), MAX_ENTRY_LENGTH); + gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE); + style = gtk_widget_get_style(window); + gtk_widget_modify_base(entry, GTK_STATE_NORMAL, + &style->bg[GTK_STATE_NORMAL]); + gtk_table_attach_defaults + (GTK_TABLE(table), entry, 1, 2, 0, 1); + + if (compose->account->name && *compose->account->name) { + g_snprintf(buf, sizeof(buf), "%s <%s>", + compose->account->name, compose->account->address); + gtk_entry_set_text(GTK_ENTRY(entry), buf); + } else + gtk_entry_set_text(GTK_ENTRY(entry), compose->account->address); + + hbox = gtk_hbox_new(FALSE, 0); + label = gtk_label_new(prefs_common.trans_hdr ? _("Subject:") + : "Subject:"); + gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, 1, 2, + GTK_FILL, 0, 2, 0); + entry = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry), MAX_ENTRY_LENGTH); + gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE); + style = gtk_widget_get_style(window); + gtk_widget_modify_base(entry, GTK_STATE_NORMAL, + &style->bg[GTK_STATE_NORMAL]); + gtk_table_attach_defaults + (GTK_TABLE(table), entry, 1, 2, 1, 2); + + text = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry)); + gtk_entry_set_text(GTK_ENTRY(entry), text); + + scrwin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin), + GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwin), + GTK_SHADOW_IN); + gtk_widget_set_size_request(scrwin, -1, 180 * gtkut_get_dpi_multiplier()); + gtk_box_pack_start(GTK_BOX(vbox), scrwin, TRUE, TRUE, 0); + + store = gtk_tree_store_new(1, G_TYPE_STRING); + if (compose->use_to) { + text = gtk_entry_get_text(GTK_ENTRY(compose->to_entry)); + to_list = address_list_append_orig(NULL, text); + if (to_list) { + gtk_tree_store_append(store, &parent, NULL); + gtk_tree_store_set(store, &parent, 0, + prefs_common.trans_hdr ? + _("To:") : "To:", -1); + for (cur = to_list; cur != NULL; cur = cur->next) { + gtk_tree_store_append(store, &iter, &parent); + gtk_tree_store_set(store, &iter, 0, + (gchar *)cur->data, -1); + } + slist_free_strings(to_list); + g_slist_free(to_list); + } + } + if (compose->use_cc) { + text = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry)); + to_list = address_list_append_orig(NULL, text); + if (to_list) { + gtk_tree_store_append(store, &parent, NULL); + gtk_tree_store_set(store, &parent, 0, + prefs_common.trans_hdr ? + _("Cc:") : "Cc:", -1); + for (cur = to_list; cur != NULL; cur = cur->next) { + gtk_tree_store_append(store, &iter, &parent); + gtk_tree_store_set(store, &iter, 0, + (gchar *)cur->data, -1); + } + slist_free_strings(to_list); + g_slist_free(to_list); + } + } + if (compose->use_bcc) { + text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry)); + to_list = address_list_append_orig(NULL, text); + if (to_list) { + gtk_tree_store_append(store, &parent, NULL); + gtk_tree_store_set(store, &parent, 0, + prefs_common.trans_hdr ? + _("Bcc:") : "Bcc:", -1); + for (cur = to_list; cur != NULL; cur = cur->next) { + gtk_tree_store_append(store, &iter, &parent); + gtk_tree_store_set(store, &iter, 0, + (gchar *)cur->data, -1); + } + slist_free_strings(to_list); + g_slist_free(to_list); + } + } + + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + + gtk_container_add(GTK_CONTAINER(scrwin), treeview); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes + (_("Address"), renderer, "text", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + gtk_tree_view_expand_all(GTK_TREE_VIEW(treeview)); + + gtkut_stock_button_set_create(&hbbox, &ok_btn, _("_Send"), + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_btn); + gtk_widget_grab_focus(ok_btn); + + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(check_recp_ok), &state); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(check_recp_cancel), &state); + + manage_window_set_transient(GTK_WINDOW(window)); + + gtk_widget_show_all(window); + + while (state == 0) + gtk_main_iteration(); + + gtk_widget_destroy(window); + + if (state == GTK_RESPONSE_OK) + return TRUE; + + return FALSE; +} + +static gboolean compose_check_activities(Compose *compose) +{ + if (inc_is_active()) { + alertpanel_notice(_("Checking for new messages is currently running.\n" + "Please try again later.")); + return FALSE; + } + + return TRUE; +} + +static void compose_add_new_recipients_to_addressbook(Compose *compose) +{ + GSList *to_list = NULL, *cur; + const gchar *text; + + if (compose->use_to) { + text = gtk_entry_get_text(GTK_ENTRY(compose->to_entry)); + to_list = address_list_append_orig(NULL, text); + } + if (compose->use_cc) { + text = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry)); + to_list = address_list_append_orig(to_list, text); + } + if (compose->use_bcc) { + text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry)); + to_list = address_list_append_orig(to_list, text); + } + + for (cur = to_list; cur != NULL; cur = cur->next) { + gchar *orig_addr = cur->data; + gchar *name, *addr; + + name = procheader_get_fromname(orig_addr); + addr = g_strdup(orig_addr); + extract_address(addr); + if (!g_ascii_strcasecmp(name, addr)) { + g_free(name); + name = NULL; + } + + if (addressbook_has_address(addr)) + debug_print("compose_add_new_recipients_to_addressbook: address <%s> already registered.\n", addr); + else + addressbook_add_contact_autoreg(name, addr, NULL); + + g_free(addr); + g_free(name); + } + + slist_free_strings(to_list); + g_slist_free(to_list); +} + +void compose_lock(Compose *compose) +{ + compose->lock_count++; +} + +void compose_unlock(Compose *compose) +{ + if (compose->lock_count > 0) + compose->lock_count--; +} + +void compose_block_modified(Compose *compose) +{ + compose->block_modified = TRUE; +} + +void compose_unblock_modified(Compose *compose) +{ + compose->block_modified = FALSE; +} + +#define C_LOCK() \ +{ \ + inc_lock(); \ + compose_lock(compose); \ +} + +#define C_UNLOCK() \ +{ \ + compose_unlock(compose); \ + inc_unlock(); \ +} + +static gint compose_send_real(Compose *compose) +{ + gchar tmp[MAXPATHLEN + 1]; + gint ok = 0; + gboolean cancel = FALSE; + + if (compose->lock_count > 0) + return 1; + + g_return_val_if_fail(compose->account != NULL, -1); + + C_LOCK(); + + if (compose_check_entries(compose) == FALSE) { + C_UNLOCK(); + return 1; + } + if (compose_check_attachments(compose) == FALSE) { + C_UNLOCK(); + return 1; + } + if (compose_check_recipients(compose) == FALSE) { + C_UNLOCK(); + return 1; + } + if (compose_check_activities(compose) == FALSE) { + C_UNLOCK(); + return 1; + } + + if (!main_window_toggle_online_if_offline(main_window_get())) { + C_UNLOCK(); + return 1; + } + + /* write to temporary file */ + g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.%p", + get_tmp_dir(), G_DIR_SEPARATOR, compose); + + if (compose->mode == COMPOSE_REDIRECT) { + if (compose_redirect_write_to_file(compose, tmp) < 0) { + C_UNLOCK(); + return -1; + } + } else { + if (compose_write_to_file(compose, tmp, FALSE) < 0) { + C_UNLOCK(); + return -1; + } + } + + if (!compose->to_list && !compose->newsgroup_list) { + g_warning(_("can't get recipient list.")); + g_unlink(tmp); + C_UNLOCK(); + return 1; + } + + syl_plugin_signal_emit("compose-send", compose, compose->mode, 0, + tmp, compose->to_list, &cancel); + if (cancel) { + g_unlink(tmp); + C_UNLOCK(); + return -1; + } + + if (compose->to_list) { + PrefsAccount *ac; + + if (compose->account->protocol != A_NNTP) + ac = compose->account; + else { + ac = account_find_from_address(compose->account->address); + if (!ac) { + if (cur_account && cur_account->protocol != A_NNTP) + ac = cur_account; + else + ac = account_get_default(); + } + if (!ac || ac->protocol == A_NNTP) { + alertpanel_error(_("Account for sending mail is not specified.\n" + "Please select a mail account before sending.")); + g_unlink(tmp); + C_UNLOCK(); + return -1; + } + } + + /* POP before SMTP requires inc to be unlocked. + send_message() also locks inc internally. */ + inc_unlock(); + ok = send_message(tmp, ac, compose->to_list); + inc_lock(); + + statusbar_pop_all(); + } + + if (ok == 0 && compose->newsgroup_list) { + ok = news_post(FOLDER(compose->account->folder), tmp); + if (ok < 0) { + alertpanel_error(_("Error occurred while posting the message to %s ."), + compose->account->nntp_server); + g_unlink(tmp); + C_UNLOCK(); + return -1; + } + } + + if (ok == 0) { + if (compose->mode == COMPOSE_REEDIT) { + compose_remove_reedit_target(compose); + if (compose->targetinfo) + folderview_update_item + (compose->targetinfo->folder, TRUE); + } + + if (compose->reply_target) + send_message_set_reply_flag(compose->reply_target, + compose->inreplyto); + else if (compose->forward_targets) + send_message_set_forward_flags + (compose->forward_targets); + + /* save message to outbox */ + if (prefs_common.savemsg) { + FolderItem *outbox; + gboolean drop_done = FALSE; + + /* filter sent message */ + if (prefs_common.filter_sent) { + FilterInfo *fltinfo; + + fltinfo = filter_info_new(); + fltinfo->account = compose->account; + fltinfo->flags.perm_flags = 0; + fltinfo->flags.tmp_flags = MSG_RECEIVED; + + filter_apply(prefs_common.fltlist, tmp, + fltinfo); + + drop_done = fltinfo->drop_done; + folderview_update_all_updated(TRUE); + filter_info_free(fltinfo); + } + + if (!drop_done) { + outbox = account_get_special_folder + (compose->account, F_OUTBOX); + if (procmsg_save_to_outbox(outbox, tmp) < 0) { + alertpanel_error + (_("Sending of message was completed, but the message could not be saved to outbox.")); + ok = -2; + } else + folderview_update_item(outbox, TRUE); + } + } + + /* Add recipients to addressbook automatically */ + if (prefs_common.recipients_autoreg) { + compose_add_new_recipients_to_addressbook(compose); + } + } + + g_unlink(tmp); + C_UNLOCK(); + + return ok; +} + +#if USE_GPGME +static const gchar *compose_get_self_key_id(Compose *compose) +{ + const gchar *key_id = NULL; + + switch (compose->account->sign_key) { + case SIGN_KEY_DEFAULT: + break; + case SIGN_KEY_BY_FROM: + key_id = compose->account->address; + break; + case SIGN_KEY_CUSTOM: + key_id = compose->account->sign_key_id; + break; + default: + break; + } + + return key_id; +} + +/* interfaces to rfc2015 to keep out the prefs stuff there. + * returns 0 on success and -1 on error. */ +static gint compose_create_signers_list(Compose *compose, GSList **pkey_list) +{ + const gchar *key_id = NULL; + GSList *key_list; + + key_id = compose_get_self_key_id(compose); + if (!key_id) { + *pkey_list = NULL; + return 0; + } + + key_list = rfc2015_create_signers_list(key_id); + if (!key_list) { + alertpanel_error(_("Could not find any key associated with " + "currently selected key id `%s'."), key_id); + return -1; + } + + *pkey_list = key_list; + return 0; +} + +static GSList *compose_create_encrypt_recipients_list(Compose *compose) +{ + GSList *recp_list = NULL; + const gchar *key_id = NULL; + + g_return_val_if_fail(compose->to_list != NULL, NULL); + + recp_list = g_slist_copy(compose->to_list); + if (compose->account->encrypt_to_self) { + key_id = compose_get_self_key_id(compose); + recp_list = g_slist_append(recp_list, (gpointer)key_id); + } + + return recp_list; +} + +/* clearsign message body text */ +static gint compose_clearsign_text(Compose *compose, gchar **text) +{ + GSList *key_list; + gchar *tmp_file; + + tmp_file = get_tmp_file(); + if (str_write_to_file(*text, tmp_file) < 0) { + g_free(tmp_file); + return -1; + } + + if (compose_create_signers_list(compose, &key_list) < 0) { + g_unlink(tmp_file); + g_free(tmp_file); + return -1; + } + if (rfc2015_clearsign(tmp_file, key_list) < 0) { + alertpanel_error(_("Can't sign the message.")); + g_unlink(tmp_file); + g_free(tmp_file); + return -1; + } + + g_free(*text); + *text = file_read_to_str(tmp_file); + g_unlink(tmp_file); + g_free(tmp_file); + if (*text == NULL) + return -1; + + return 0; +} + +static gint compose_encrypt_armored(Compose *compose, gchar **text) +{ + gchar *tmp_file; + GSList *recp_list; + + tmp_file = get_tmp_file(); + if (str_write_to_file(*text, tmp_file) < 0) { + g_free(tmp_file); + return -1; + } + + recp_list = compose_create_encrypt_recipients_list(compose); + + if (rfc2015_encrypt_armored(tmp_file, recp_list) < 0) { + alertpanel_error(_("Can't encrypt the message.")); + g_slist_free(recp_list); + g_unlink(tmp_file); + g_free(tmp_file); + return -1; + } + + g_slist_free(recp_list); + g_free(*text); + *text = file_read_to_str(tmp_file); + g_unlink(tmp_file); + g_free(tmp_file); + if (*text == NULL) + return -1; + + return 0; +} + +static gint compose_encrypt_sign_armored(Compose *compose, gchar **text) +{ + GSList *key_list; + GSList *recp_list; + gchar *tmp_file; + + tmp_file = get_tmp_file(); + if (str_write_to_file(*text, tmp_file) < 0) { + g_free(tmp_file); + return -1; + } + + if (compose_create_signers_list(compose, &key_list) < 0) { + g_unlink(tmp_file); + g_free(tmp_file); + return -1; + } + + recp_list = compose_create_encrypt_recipients_list(compose); + + if (rfc2015_encrypt_sign_armored(tmp_file, recp_list, key_list) < 0) { + alertpanel_error(_("Can't encrypt or sign the message.")); + g_slist_free(recp_list); + g_unlink(tmp_file); + g_free(tmp_file); + return -1; + } + + g_slist_free(recp_list); + g_free(*text); + *text = file_read_to_str(tmp_file); + g_unlink(tmp_file); + g_free(tmp_file); + if (*text == NULL) + return -1; + + return 0; +} +#endif /* USE_GPGME */ + +static gint compose_write_to_file(Compose *compose, const gchar *file, + gboolean is_draft) +{ + GtkTextBuffer *buffer; + GtkTextIter start, end; + GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store); + FILE *fp; + size_t len; + gchar *chars; + gchar *buf; + gchar *canon_buf; + const gchar *out_charset; + const gchar *body_charset; + const gchar *src_charset = CS_INTERNAL; + EncodingType encoding; + gint line; +#if USE_GPGME + gboolean use_pgpmime_encryption = FALSE; + gboolean use_pgpmime_signing = FALSE; +#endif + + if ((fp = g_fopen(file, "wb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + return -1; + } + + /* chmod for security */ + if (change_file_mode_rw(fp, file) < 0) { + FILE_OP_ERROR(file, "chmod"); + g_warning(_("can't change file mode\n")); + } + + /* get outgoing charset */ + out_charset = conv_get_charset_str(compose->out_encoding); + if (!out_charset) + out_charset = conv_get_outgoing_charset_str(); + if (!g_ascii_strcasecmp(out_charset, CS_US_ASCII)) + out_charset = CS_ISO_8859_1; + body_charset = out_charset; + + /* get all composed text */ + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text)); + gtk_text_buffer_get_start_iter(buffer, &start); + gtk_text_buffer_get_end_iter(buffer, &end); + chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); + if (is_ascii_str(chars)) { + buf = chars; + chars = NULL; + body_charset = CS_US_ASCII; + encoding = ENC_7BIT; + } else { + gint error = 0; + + buf = conv_codeset_strdup_full + (chars, src_charset, body_charset, &error); + if (!buf || error != 0) { + AlertValue aval = G_ALERTDEFAULT; + gchar *msg; + + g_free(buf); + + if (!is_draft) { + msg = g_strdup_printf(_("Can't convert the character encoding of the message body from %s to %s.\n" + "\n" + "Send it as %s anyway?"), + src_charset, body_charset, + src_charset); + aval = alertpanel_full + (_("Code conversion error"), msg, ALERT_ERROR, + G_ALERTALTERNATE, + FALSE, GTK_STOCK_YES, GTK_STOCK_NO, NULL); + g_free(msg); + } + + if (aval != G_ALERTDEFAULT) { + g_free(chars); + fclose(fp); + g_unlink(file); + return -1; + } else { + buf = chars; + out_charset = body_charset = src_charset; + chars = NULL; + } + } + + if (prefs_common.encoding_method == CTE_BASE64) + encoding = ENC_BASE64; + else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE) + encoding = ENC_QUOTED_PRINTABLE; + else if (prefs_common.encoding_method == CTE_8BIT) + encoding = ENC_8BIT; + else + encoding = procmime_get_encoding_for_charset + (body_charset); + } + g_free(chars); + + canon_buf = canonicalize_str(buf); + g_free(buf); + buf = canon_buf; + +#if USE_GPGME + if (compose->use_signing && !compose->account->clearsign) + use_pgpmime_signing = TRUE; + if (compose->use_encryption && compose->account->ascii_armored) { + use_pgpmime_encryption = FALSE; + use_pgpmime_signing = FALSE; + } + if (compose->use_encryption && !compose->account->ascii_armored) + use_pgpmime_encryption = TRUE; + + /* protect trailing spaces */ + if (rfc2015_is_available() && !is_draft && use_pgpmime_signing) { + if (encoding == ENC_7BIT) { + if (!g_ascii_strcasecmp(body_charset, CS_ISO_2022_JP)) { + gchar *tmp; + tmp = strchomp_all(buf); + g_free(buf); + buf = tmp; + } else + encoding = ENC_QUOTED_PRINTABLE; + } else if (encoding == ENC_8BIT) { + encoding = procmime_get_encoding_for_str(buf); + if (encoding == ENC_7BIT) + encoding = ENC_QUOTED_PRINTABLE; + } + } + + if (rfc2015_is_available() && !is_draft) { + if ((compose->use_encryption && + compose->account->ascii_armored) || + (compose->use_signing && compose->account->clearsign)) { + /* MIME encoding doesn't fit with cleartext signature */ + if (encoding == ENC_QUOTED_PRINTABLE || encoding == ENC_BASE64) + encoding = ENC_8BIT; + + } + } +#endif + + debug_print("src encoding = %s, out encoding = %s, " + "body encoding = %s, transfer encoding = %s\n", + src_charset, out_charset, body_charset, + procmime_get_encoding_str(encoding)); + + /* check for line length limit */ + if (!is_draft && + encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 && + check_line_length(buf, 1000, &line) < 0) { + AlertValue aval; + gchar *msg; + + msg = g_strdup_printf + (_("Line %d exceeds the line length limit (998 bytes).\n" + "The contents of the message might be broken on the way to the delivery.\n" + "\n" + "Send it anyway?"), line + 1); + aval = alertpanel_full(_("Line length limit"), + msg, ALERT_WARNING, + G_ALERTALTERNATE, FALSE, + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if (aval != G_ALERTDEFAULT) { + g_free(msg); + fclose(fp); + g_unlink(file); + g_free(buf); + return -1; + } + } + + /* write headers */ + if (compose_write_headers(compose, fp, out_charset, + body_charset, encoding, is_draft) < 0) { + g_warning("can't write headers\n"); + fclose(fp); + g_unlink(file); + g_free(buf); + return -1; + } + +#if USE_GPGME + /* do ascii-armor encryption and/or clearsign */ + if (rfc2015_is_available() && !is_draft) { + gint ret; + + if (compose->use_encryption && compose->account->ascii_armored) { + if (compose->use_signing) + ret = compose_encrypt_sign_armored(compose, &buf); + else + ret = compose_encrypt_armored(compose, &buf); + if (ret < 0) { + g_warning("ascii-armored encryption failed\n"); + fclose(fp); + g_unlink(file); + g_free(buf); + return -1; + } + } else if (compose->use_signing && compose->account->clearsign) { + if (compose_clearsign_text(compose, &buf) < 0) { + g_warning("clearsign failed\n"); + fclose(fp); + g_unlink(file); + g_free(buf); + return -1; + } + } + } +#endif + + if (compose->use_attach && + gtk_tree_model_iter_n_children(model, NULL) > 0) { +#if USE_GPGME + /* This prolog message is ignored by mime software and + * because it would make our signing/encryption task + * tougher, we don't emit it in that case */ + if (!rfc2015_is_available() || + (!compose->use_signing && !compose->use_encryption)) +#endif + fputs("This is a multi-part message in MIME format.\n", fp); + + fprintf(fp, "\n--%s\n", compose->boundary); + fprintf(fp, "Content-Type: text/plain; charset=%s\n", + body_charset); +#if USE_GPGME + if (rfc2015_is_available() && use_pgpmime_signing) + fprintf(fp, "Content-Disposition: inline\n"); +#endif + fprintf(fp, "Content-Transfer-Encoding: %s\n", + procmime_get_encoding_str(encoding)); + fputc('\n', fp); + } + + /* write body */ + len = strlen(buf); + if (encoding == ENC_BASE64) { + gchar outbuf[B64_BUFFSIZE]; + gint i, l; + + for (i = 0; i < len; i += B64_LINE_SIZE) { + l = MIN(B64_LINE_SIZE, len - i); + base64_encode(outbuf, (guchar *)buf + i, l); + fputs(outbuf, fp); + fputc('\n', fp); + } + } else if (encoding == ENC_QUOTED_PRINTABLE) { + gchar *outbuf; + size_t outlen; + + outbuf = g_malloc(len * 4); + qp_encode_line(outbuf, (guchar *)buf); + outlen = strlen(outbuf); + if (fwrite(outbuf, sizeof(gchar), outlen, fp) != outlen) { + FILE_OP_ERROR(file, "fwrite"); + fclose(fp); + g_unlink(file); + g_free(outbuf); + g_free(buf); + return -1; + } + g_free(outbuf); + } else if (fwrite(buf, sizeof(gchar), len, fp) != len) { + FILE_OP_ERROR(file, "fwrite"); + fclose(fp); + g_unlink(file); + g_free(buf); + return -1; + } + g_free(buf); + + if (compose->use_attach && + gtk_tree_model_iter_n_children(model, NULL) > 0) { + if (compose_write_attach(compose, fp, out_charset) < 0) { + fclose(fp); + g_unlink(file); + return -1; + } + } + + if (fclose(fp) == EOF) { + FILE_OP_ERROR(file, "fclose"); + g_unlink(file); + return -1; + } + +#if USE_GPGME + if (!rfc2015_is_available() || is_draft) { + uncanonicalize_file_replace(file); + return 0; + } + + if (use_pgpmime_signing || use_pgpmime_encryption) { + if (canonicalize_file_replace(file) < 0) { + g_unlink(file); + return -1; + } + } + + if (use_pgpmime_signing && !use_pgpmime_encryption) { + GSList *key_list; + + if (compose_create_signers_list(compose, &key_list) < 0) { + g_unlink(file); + return -1; + } + if (rfc2015_sign(file, key_list) < 0) { + alertpanel_error(_("Can't sign the message.")); + g_unlink(file); + return -1; + } + } else if (use_pgpmime_encryption) { + GSList *key_list; + GSList *recp_list; + + if (compose->use_bcc) { + const gchar *text; + gchar *bcc; + AlertValue aval; + + text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry)); + if (*text != '\0') { + bcc = g_strdup(text); + g_strstrip(bcc); + if (*bcc != '\0') { + aval = alertpanel_full + (_("Encrypting with Bcc"), + _("This message has Bcc recipients. If this message is encrypted, all Bcc recipients will be visible by examing the encryption key list, leading to loss of confidentiality.\n" + "\n" + "Send it anyway?"), + ALERT_WARNING, G_ALERTDEFAULT, FALSE, + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if (aval != G_ALERTDEFAULT) { + g_free(bcc); + g_unlink(file); + return -1; + } + } + g_free(bcc); + } + } + + recp_list = compose_create_encrypt_recipients_list(compose); + + if (use_pgpmime_signing) { + if (compose_create_signers_list + (compose, &key_list) < 0) { + g_unlink(file); + return -1; + } + + if (rfc2015_encrypt_sign(file, recp_list, key_list) < 0) { + alertpanel_error(_("Can't encrypt or sign the message.")); + g_slist_free(recp_list); + g_unlink(file); + return -1; + } + } else if (rfc2015_encrypt(file, recp_list) < 0) { + alertpanel_error(_("Can't encrypt the message.")); + g_slist_free(recp_list); + g_unlink(file); + return -1; + } + + g_slist_free(recp_list); + } +#endif /* USE_GPGME */ + + uncanonicalize_file_replace(file); + + return 0; +} + +static gint compose_write_body_to_file(Compose *compose, const gchar *file) +{ + GtkTextBuffer *buffer; + GtkTextIter start, end; + FILE *fp; + size_t len; + gchar *chars, *tmp; + + if ((fp = g_fopen(file, "wb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + return -1; + } + + /* chmod for security */ + if (change_file_mode_rw(fp, file) < 0) { + FILE_OP_ERROR(file, "chmod"); + g_warning(_("can't change file mode\n")); + } + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text)); + gtk_text_buffer_get_start_iter(buffer, &start); + gtk_text_buffer_get_end_iter(buffer, &end); + tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); + + chars = conv_codeset_strdup + (tmp, CS_INTERNAL, conv_get_locale_charset_str()); + + g_free(tmp); + + if (!chars) { + fclose(fp); + g_unlink(file); + return -1; + } + + /* write body */ + len = strlen(chars); + if (fwrite(chars, sizeof(gchar), len, fp) != len) { + FILE_OP_ERROR(file, "fwrite"); + g_free(chars); + fclose(fp); + g_unlink(file); + return -1; + } + + g_free(chars); + + if (fclose(fp) == EOF) { + FILE_OP_ERROR(file, "fclose"); + g_unlink(file); + return -1; + } + return 0; +} + +static gint compose_redirect_write_to_file(Compose *compose, const gchar *file) +{ + FILE *fp; + FILE *fdest; + size_t len; + gchar buf[BUFFSIZE]; + + g_return_val_if_fail(file != NULL, -1); + g_return_val_if_fail(compose->account != NULL, -1); + g_return_val_if_fail(compose->account->address != NULL, -1); + g_return_val_if_fail(compose->mode == COMPOSE_REDIRECT, -1); + g_return_val_if_fail(compose->targetinfo != NULL, -1); + + if ((fp = procmsg_open_message(compose->targetinfo)) == NULL) + return -1; + + if ((fdest = g_fopen(file, "wb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + fclose(fp); + return -1; + } + + if (change_file_mode_rw(fdest, file) < 0) { + FILE_OP_ERROR(file, "chmod"); + g_warning(_("can't change file mode\n")); + } + + while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) == 0) { + if (g_ascii_strncasecmp(buf, "Return-Path:", + strlen("Return-Path:")) == 0 || + g_ascii_strncasecmp(buf, "Delivered-To:", + strlen("Delivered-To:")) == 0 || + g_ascii_strncasecmp(buf, "Received:", + strlen("Received:")) == 0 || + g_ascii_strncasecmp(buf, "Subject:", + strlen("Subject:")) == 0 || + g_ascii_strncasecmp(buf, "X-UIDL:", + strlen("X-UIDL:")) == 0) + continue; + + if (fputs(buf, fdest) == EOF) + goto error; + +#if 0 + if (g_ascii_strncasecmp(buf, "From:", strlen("From:")) == 0) { + fputs("\n (by way of ", fdest); + if (compose->account->name) { + compose_convert_header(compose, + buf, sizeof(buf), + compose->account->name, + strlen(" (by way of "), + FALSE, NULL); + fprintf(fdest, "%s <%s>", buf, + compose->account->address); + } else + fputs(compose->account->address, fdest); + fputs(")", fdest); + } +#endif + + if (fputs("\n", fdest) == EOF) + goto error; + } + + compose_redirect_write_headers(compose, fdest); + + while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) { + if (fwrite(buf, sizeof(gchar), len, fdest) != len) { + FILE_OP_ERROR(file, "fwrite"); + goto error; + } + } + + fclose(fp); + if (fclose(fdest) == EOF) { + FILE_OP_ERROR(file, "fclose"); + g_unlink(file); + return -1; + } + + return 0; +error: + fclose(fp); + fclose(fdest); + g_unlink(file); + + return -1; +} + +static gint compose_remove_reedit_target(Compose *compose) +{ + FolderItem *item; + MsgInfo *msginfo = compose->targetinfo; + + g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1); + if (!msginfo) return -1; + + item = msginfo->folder; + g_return_val_if_fail(item != NULL, -1); + + folder_item_scan(item); + if (procmsg_msg_exist(msginfo) && + (item->stype == F_DRAFT || item->stype == F_QUEUE)) { + if (folder_item_remove_msg(item, msginfo) < 0) { + g_warning(_("can't remove the old message\n")); + return -1; + } + } + + return 0; +} + +static gint compose_queue(Compose *compose, const gchar *file) +{ + FolderItem *queue; + gchar *tmp; + FILE *fp, *src_fp; + GSList *cur; + gchar buf[BUFFSIZE]; + gint num; + MsgFlags flag = {0, MSG_QUEUED}; + + debug_print(_("queueing message...\n")); + g_return_val_if_fail(compose->to_list != NULL || + compose->newsgroup_list != NULL, + -1); + g_return_val_if_fail(compose->account != NULL, -1); + + tmp = g_strdup_printf("%s%cqueue.%p", get_tmp_dir(), + G_DIR_SEPARATOR, compose); + if ((fp = g_fopen(tmp, "wb")) == NULL) { + FILE_OP_ERROR(tmp, "fopen"); + g_free(tmp); + return -1; + } + if ((src_fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + fclose(fp); + g_unlink(tmp); + g_free(tmp); + return -1; + } + if (change_file_mode_rw(fp, tmp) < 0) { + FILE_OP_ERROR(tmp, "chmod"); + g_warning(_("can't change file mode\n")); + } + + /* queueing variables */ + fprintf(fp, "AF:\n"); + fprintf(fp, "NF:0\n"); + fprintf(fp, "PS:10\n"); + fprintf(fp, "SRH:1\n"); + fprintf(fp, "SFN:\n"); + fprintf(fp, "DSR:\n"); + if (compose->msgid) + fprintf(fp, "MID:<%s>\n", compose->msgid); + else + fprintf(fp, "MID:\n"); + fprintf(fp, "CFG:\n"); + fprintf(fp, "PT:0\n"); + fprintf(fp, "S:%s\n", compose->account->address); + fprintf(fp, "RQ:\n"); + if (compose->account->smtp_server) + fprintf(fp, "SSV:%s\n", compose->account->smtp_server); + else + fprintf(fp, "SSV:\n"); + if (compose->account->nntp_server) + fprintf(fp, "NSV:%s\n", compose->account->nntp_server); + else + fprintf(fp, "NSV:\n"); + fprintf(fp, "SSH:\n"); + if (compose->to_list) { + fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data); + for (cur = compose->to_list->next; cur != NULL; + cur = cur->next) + fprintf(fp, ",<%s>", (gchar *)cur->data); + fprintf(fp, "\n"); + } else + fprintf(fp, "R:\n"); + /* Sylpheed account ID */ + fprintf(fp, "AID:%d\n", compose->account->account_id); + /* Reply target */ + if (compose->reply_target) + fprintf(fp, "REP:%s\n", compose->reply_target); + /* Forward target */ + if (compose->forward_targets) + fprintf(fp, "FWD:%s\n", compose->forward_targets); + fprintf(fp, "\n"); + + while (fgets(buf, sizeof(buf), src_fp) != NULL) { + if (fputs(buf, fp) == EOF) { + FILE_OP_ERROR(tmp, "fputs"); + fclose(fp); + fclose(src_fp); + g_unlink(tmp); + g_free(tmp); + return -1; + } + } + + fclose(src_fp); + if (fclose(fp) == EOF) { + FILE_OP_ERROR(tmp, "fclose"); + g_unlink(tmp); + g_free(tmp); + return -1; + } + + queue = account_get_special_folder(compose->account, F_QUEUE); + if (!queue) { + g_warning(_("can't find queue folder\n")); + g_unlink(tmp); + g_free(tmp); + return -1; + } + folder_item_scan(queue); + if ((num = folder_item_add_msg(queue, tmp, &flag, TRUE)) < 0) { + g_warning(_("can't queue the message\n")); + g_unlink(tmp); + g_free(tmp); + return -1; + } + g_free(tmp); + + if (compose->mode == COMPOSE_REEDIT) { + compose_remove_reedit_target(compose); + if (compose->targetinfo && + compose->targetinfo->folder != queue) + folderview_update_item + (compose->targetinfo->folder, TRUE); + } + + folder_item_scan(queue); + folderview_update_item(queue, TRUE); + + /* Add recipients to addressbook automatically */ + if (prefs_common.recipients_autoreg) { + compose_add_new_recipients_to_addressbook(compose); + } + + main_window_set_menu_sensitive(main_window_get()); + main_window_set_toolbar_sensitive(main_window_get()); + + return 0; +} + +static gint compose_write_attach(Compose *compose, FILE *fp, + const gchar *charset) +{ + GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store); + GtkTreeIter iter; + gboolean valid; + AttachInfo *ainfo; + FILE *attach_fp; + gint len; + EncodingType encoding; + ContentType content_type; + gchar *tmp_file = NULL; + const gchar *src_file; + FILE *src_fp; + FILE *tmp_fp = NULL; + + for (valid = gtk_tree_model_get_iter_first(model, &iter); valid; + valid = gtk_tree_model_iter_next(model, &iter)) { + gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1); + + if (!is_file_exist(ainfo->file)) { + alertpanel_error(_("File %s doesn't exist."), + ainfo->file); + return -1; + } + if (get_file_size(ainfo->file) <= 0) { + alertpanel_error(_("File %s is empty."), ainfo->file); + return -1; + } + if ((attach_fp = g_fopen(ainfo->file, "rb")) == NULL) { + alertpanel_error(_("Can't open file %s."), ainfo->file); + return -1; + } + + fprintf(fp, "\n--%s\n", compose->boundary); + + encoding = ainfo->encoding; + + if (!g_ascii_strncasecmp(ainfo->content_type, "message/", 8)) { + fprintf(fp, "Content-Type: %s\n", ainfo->content_type); + fprintf(fp, "Content-Disposition: inline\n"); + + /* message/... shouldn't be encoded */ + if (encoding == ENC_QUOTED_PRINTABLE || + encoding == ENC_BASE64) + encoding = ENC_8BIT; + } else { + if (prefs_common.mime_fencoding_method == + FENC_RFC2231) { + gchar *param; + + param = compose_convert_filename + (compose, ainfo->name, "name", charset); + fprintf(fp, "Content-Type: %s;\n" + "%s\n", + ainfo->content_type, param); + g_free(param); + param = compose_convert_filename + (compose, ainfo->name, "filename", + charset); + fprintf(fp, "Content-Disposition: attachment;\n" + "%s\n", param); + g_free(param); + } else { + gchar filename[BUFFSIZE]; + + compose_convert_header(compose, filename, + sizeof(filename), + ainfo->name, 12, FALSE, + charset); + fprintf(fp, "Content-Type: %s;\n" + " name=\"%s\"\n", + ainfo->content_type, filename); + fprintf(fp, "Content-Disposition: attachment;\n" + " filename=\"%s\"\n", filename); + } + +#if USE_GPGME + /* force encoding to protect trailing spaces */ + if (rfc2015_is_available() && compose->use_signing && + !compose->account->clearsign) { + if (encoding == ENC_7BIT) + encoding = ENC_QUOTED_PRINTABLE; + else if (encoding == ENC_8BIT) + encoding = ENC_BASE64; + } +#endif + } + + fprintf(fp, "Content-Transfer-Encoding: %s\n\n", + procmime_get_encoding_str(encoding)); + + content_type = procmime_scan_mime_type(ainfo->content_type); + + if (content_type == MIME_TEXT || content_type == MIME_TEXT_HTML) { + CharSet enc; + + enc = conv_check_file_encoding(ainfo->file); + if (enc == C_UTF_16 || enc == C_UTF_16BE || enc == C_UTF_16LE) { + tmp_file = get_tmp_file(); + if (conv_copy_file(ainfo->file, tmp_file, conv_get_charset_str(enc)) < 0) { + g_warning("compose_write_attach: Cannot convert UTF-16 file %s to UTF-8", ainfo->file); + g_free(tmp_file); + tmp_file = NULL; + } + } + } + + if (tmp_file) { + src_file = tmp_file; + } else { + src_file = ainfo->file; + } + + if (encoding == ENC_BASE64) { + gchar inbuf[B64_LINE_SIZE], outbuf[B64_BUFFSIZE]; + gchar *canon_file = NULL; + + if (content_type == MIME_TEXT || + content_type == MIME_TEXT_HTML || + content_type == MIME_MESSAGE_RFC822) { + canon_file = get_tmp_file(); + if (canonicalize_file(src_file, canon_file) < 0) { + g_free(canon_file); + if (tmp_file) { + g_unlink(tmp_file); + g_free(tmp_file); + } + fclose(attach_fp); + return -1; + } + if ((tmp_fp = g_fopen(canon_file, "rb")) == NULL) { + FILE_OP_ERROR(canon_file, "fopen"); + g_unlink(canon_file); + g_free(canon_file); + if (tmp_file) { + g_unlink(tmp_file); + g_free(tmp_file); + } + fclose(attach_fp); + return -1; + } + } + + if (tmp_fp) { + src_fp = tmp_fp; + } else { + src_fp = attach_fp; + } + + while ((len = fread(inbuf, sizeof(gchar), + B64_LINE_SIZE, src_fp)) + == B64_LINE_SIZE) { + base64_encode(outbuf, (guchar *)inbuf, + B64_LINE_SIZE); + fputs(outbuf, fp); + fputc('\n', fp); + } + if (len > 0 && feof(src_fp)) { + base64_encode(outbuf, (guchar *)inbuf, len); + fputs(outbuf, fp); + fputc('\n', fp); + } + + if (tmp_fp) { + fclose(tmp_fp); + tmp_fp = NULL; + } + if (canon_file) { + g_unlink(canon_file); + g_free(canon_file); + } + } else { + if (tmp_file) { + if ((tmp_fp = g_fopen(tmp_file, "rb")) == NULL) { + FILE_OP_ERROR(tmp_file, "fopen"); + g_unlink(tmp_file); + g_free(tmp_file); + fclose(attach_fp); + return -1; + } + src_fp = tmp_fp; + } else { + src_fp = attach_fp; + } + + if (encoding == ENC_QUOTED_PRINTABLE) { + gchar inbuf[BUFFSIZE], outbuf[BUFFSIZE * 4]; + + while (fgets(inbuf, sizeof(inbuf), src_fp) != NULL) { + qp_encode_line(outbuf, (guchar *)inbuf); + fputs(outbuf, fp); + } + } else { + gchar buf[BUFFSIZE]; + + while (fgets(buf, sizeof(buf), src_fp) != NULL) { + strcrchomp(buf); + fputs(buf, fp); + } + } + + if (tmp_fp) { + fclose(tmp_fp); + tmp_fp = NULL; + } + } + + if (tmp_file) { + g_unlink(tmp_file); + g_free(tmp_file); + tmp_file = NULL; + } + fclose(attach_fp); + } + + fprintf(fp, "\n--%s--\n", compose->boundary); + return 0; +} + +#define QUOTE_REQUIRED(str) \ + (*str != '"' && strpbrk(str, ",.[]<>") != NULL) + +#define PUT_RECIPIENT_HEADER(header, str) \ +{ \ + if (*str != '\0') { \ + gchar *dest; \ + \ + dest = g_strdup(str); \ + g_strstrip(dest); \ + if (*dest != '\0') { \ + compose->to_list = address_list_append \ + (compose->to_list, dest); \ + compose_convert_header \ + (compose, buf, sizeof(buf), dest, \ + strlen(header) + 2, TRUE, charset); \ + fprintf(fp, "%s: %s\n", header, buf); \ + } \ + g_free(dest); \ + } \ +} + +#define IS_IN_CUSTOM_HEADER(header) \ + (compose->account->add_customhdr && \ + custom_header_find(compose->account->customhdr_list, header) != NULL) + +static gint compose_write_headers(Compose *compose, FILE *fp, + const gchar *charset, + const gchar *body_charset, + EncodingType encoding, gboolean is_draft) +{ + gchar buf[BUFFSIZE]; + const gchar *entry_str; + gchar *str; + + g_return_val_if_fail(fp != NULL, -1); + g_return_val_if_fail(charset != NULL, -1); + g_return_val_if_fail(compose->account != NULL, -1); + g_return_val_if_fail(compose->account->address != NULL, -1); + + /* Date */ + if (compose->account->add_date) { + get_rfc822_date(buf, sizeof(buf)); + fprintf(fp, "Date: %s\n", buf); + } + + /* From */ + if (compose->account->name && *compose->account->name) { + compose_convert_header + (compose, buf, sizeof(buf), compose->account->name, + strlen("From: "), TRUE, charset); + if (QUOTE_REQUIRED(buf)) + fprintf(fp, "From: \"%s\" <%s>\n", + buf, compose->account->address); + else + fprintf(fp, "From: %s <%s>\n", + buf, compose->account->address); + } else + fprintf(fp, "From: %s\n", compose->account->address); + + slist_free_strings(compose->to_list); + g_slist_free(compose->to_list); + compose->to_list = NULL; + + /* To */ + if (compose->use_to) { + entry_str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry)); + PUT_RECIPIENT_HEADER("To", entry_str); + } + + slist_free_strings(compose->newsgroup_list); + g_slist_free(compose->newsgroup_list); + compose->newsgroup_list = NULL; + + /* Newsgroups */ + if (compose->use_newsgroups) { + entry_str = gtk_entry_get_text + (GTK_ENTRY(compose->newsgroups_entry)); + if (*entry_str != '\0') { + str = g_strdup(entry_str); + g_strstrip(str); + remove_space(str); + if (*str != '\0') { + compose->newsgroup_list = + newsgroup_list_append + (compose->newsgroup_list, str); + compose_convert_header(compose, + buf, sizeof(buf), str, + strlen("Newsgroups: "), + FALSE, charset); + fprintf(fp, "Newsgroups: %s\n", buf); + } + g_free(str); + } + } + + /* Cc */ + if (compose->use_cc) { + entry_str = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry)); + PUT_RECIPIENT_HEADER("Cc", entry_str); + } + + /* Bcc */ + if (compose->use_bcc) { + entry_str = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry)); + PUT_RECIPIENT_HEADER("Bcc", entry_str); + } + + if (!is_draft && !compose->to_list && !compose->newsgroup_list) + return -1; + + /* Subject */ + entry_str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry)); + if (*entry_str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) { + str = g_strdup(entry_str); + g_strstrip(str); + if (*str != '\0') { + compose_convert_header(compose, buf, sizeof(buf), str, + strlen("Subject: "), FALSE, + charset); + fprintf(fp, "Subject: %s\n", buf); + } + g_free(str); + } + + /* Message-ID */ + if (compose->account->gen_msgid) { + compose_generate_msgid(compose, buf, sizeof(buf)); + fprintf(fp, "Message-Id: <%s>\n", buf); + compose->msgid = g_strdup(buf); + } + + /* In-Reply-To */ + if (compose->inreplyto && compose->to_list) + fprintf(fp, "In-Reply-To: <%s>\n", compose->inreplyto); + + /* References */ + if (compose->references) + fprintf(fp, "References: %s\n", compose->references); + + /* Followup-To */ + if (compose->use_followupto && !IS_IN_CUSTOM_HEADER("Followup-To")) { + entry_str = gtk_entry_get_text + (GTK_ENTRY(compose->followup_entry)); + if (*entry_str != '\0') { + str = g_strdup(entry_str); + g_strstrip(str); + remove_space(str); + if (*str != '\0') { + compose_convert_header(compose, + buf, sizeof(buf), str, + strlen("Followup-To: "), + FALSE, charset); + fprintf(fp, "Followup-To: %s\n", buf); + } + g_free(str); + } + } + + /* Reply-To */ + if (compose->use_replyto && !IS_IN_CUSTOM_HEADER("Reply-To")) { + entry_str = gtk_entry_get_text(GTK_ENTRY(compose->reply_entry)); + if (*entry_str != '\0') { + str = g_strdup(entry_str); + g_strstrip(str); + if (*str != '\0') { + compose_convert_header(compose, + buf, sizeof(buf), str, + strlen("Reply-To: "), + TRUE, charset); + fprintf(fp, "Reply-To: %s\n", buf); + } + g_free(str); + } + } + + /* Disposition-Notification-To */ + if (compose->use_mdn && + !IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) { + fprintf(fp, "Disposition-Notification-To: %s\n", + compose->account->address); + } + + /* Organization */ + if (compose->account->organization && + !IS_IN_CUSTOM_HEADER("Organization")) { + compose_convert_header(compose, buf, sizeof(buf), + compose->account->organization, + strlen("Organization: "), FALSE, + charset); + fprintf(fp, "Organization: %s\n", buf); + } + + /* Program version and system info */ + if (prefs_common.user_agent_str) { + if (compose->to_list && !IS_IN_CUSTOM_HEADER("X-Mailer")) { + fprintf(fp, "X-Mailer: %s\n", + prefs_common.user_agent_str); + } + if (compose->newsgroup_list && !IS_IN_CUSTOM_HEADER("X-Newsreader")) { + fprintf(fp, "X-Newsreader: %s\n", + prefs_common.user_agent_str); + } + } + + /* custom headers */ + if (compose->account->add_customhdr) { + GSList *cur; + + for (cur = compose->account->customhdr_list; cur != NULL; + cur = cur->next) { + CustomHeader *chdr = (CustomHeader *)cur->data; + + if (g_ascii_strcasecmp(chdr->name, "Date") != 0 && + g_ascii_strcasecmp(chdr->name, "From") != 0 && + g_ascii_strcasecmp(chdr->name, "To") != 0 && + /* g_ascii_strcasecmp(chdr->name, "Sender") != 0 && */ + g_ascii_strcasecmp(chdr->name, "Message-Id") != 0 && + g_ascii_strcasecmp(chdr->name, "In-Reply-To") != 0 && + g_ascii_strcasecmp(chdr->name, "References") != 0 && + g_ascii_strcasecmp(chdr->name, "Mime-Version") != 0 && + g_ascii_strcasecmp(chdr->name, "Content-Type") != 0 && + g_ascii_strcasecmp(chdr->name, "Content-Transfer-Encoding") != 0) { + compose_convert_header + (compose, buf, sizeof(buf), + chdr->value ? chdr->value : "", + strlen(chdr->name) + 2, FALSE, + charset); + fprintf(fp, "%s: %s\n", chdr->name, buf); + } + } + } + + /* MIME */ + fprintf(fp, "Mime-Version: 1.0\n"); + if (compose->use_attach && + gtk_tree_model_iter_n_children + (GTK_TREE_MODEL(compose->attach_store), NULL) > 0) { + compose->boundary = generate_mime_boundary(NULL); + fprintf(fp, + "Content-Type: multipart/mixed;\n" + " boundary=\"%s\"\n", compose->boundary); + } else { + fprintf(fp, "Content-Type: text/plain; charset=%s\n", + body_charset); +#if USE_GPGME + if (rfc2015_is_available() && + compose->use_signing && !compose->account->clearsign) + fprintf(fp, "Content-Disposition: inline\n"); +#endif + fprintf(fp, "Content-Transfer-Encoding: %s\n", + procmime_get_encoding_str(encoding)); + } + + /* X-Sylpheed headers */ + if (is_draft) { + fprintf(fp, "X-Sylpheed-Account-Id: %d\n", + compose->account->account_id); + if (compose->reply_target) + fprintf(fp, "X-Sylpheed-Reply: %s\n", + compose->reply_target); + else if (compose->forward_targets) + fprintf(fp, "X-Sylpheed-Forward: %s\n", + compose->forward_targets); + fprintf(fp, "X-Sylpheed-Compose-AutoWrap: %s\n", + compose->autowrap ? "TRUE" : "FALSE"); +#if USE_GTKSPELL + fprintf(fp, "X-Sylpheed-Compose-CheckSpell: %s\n", + compose->check_spell ? "TRUE" : "FALSE"); + if (compose->spell_lang) + fprintf(fp, "X-Sylpheed-Compose-SpellLang: %s\n", + compose->spell_lang); +#endif +#if USE_GPGME + fprintf(fp, "X-Sylpheed-Compose-UseSigning: %s\n", + compose->use_signing ? "TRUE" : "FALSE"); + fprintf(fp, "X-Sylpheed-Compose-UseEncryption: %s\n", + compose->use_encryption ? "TRUE" : "FALSE"); +#endif + } + + /* separator between header and body */ + fputs("\n", fp); + + return 0; +} + +static gint compose_redirect_write_headers(Compose *compose, FILE *fp) +{ + gchar buf[BUFFSIZE]; + const gchar *entry_str; + gchar *str; + const gchar *charset = NULL; + + g_return_val_if_fail(fp != NULL, -1); + g_return_val_if_fail(compose->account != NULL, -1); + g_return_val_if_fail(compose->account->address != NULL, -1); + + /* Resent-Date */ + get_rfc822_date(buf, sizeof(buf)); + fprintf(fp, "Resent-Date: %s\n", buf); + + /* Resent-From */ + if (compose->account->name) { + compose_convert_header + (compose, buf, sizeof(buf), compose->account->name, + strlen("Resent-From: "), TRUE, NULL); + fprintf(fp, "Resent-From: %s <%s>\n", + buf, compose->account->address); + } else + fprintf(fp, "Resent-From: %s\n", compose->account->address); + + slist_free_strings(compose->to_list); + g_slist_free(compose->to_list); + compose->to_list = NULL; + + /* Resent-To */ + if (compose->use_to) { + entry_str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry)); + PUT_RECIPIENT_HEADER("Resent-To", entry_str); + } + if (compose->use_cc) { + entry_str = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry)); + PUT_RECIPIENT_HEADER("Resent-Cc", entry_str); + } + if (compose->use_bcc) { + entry_str = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry)); + PUT_RECIPIENT_HEADER("Bcc", entry_str); + } + + slist_free_strings(compose->newsgroup_list); + g_slist_free(compose->newsgroup_list); + compose->newsgroup_list = NULL; + + /* Newsgroups */ + if (compose->use_newsgroups) { + entry_str = gtk_entry_get_text + (GTK_ENTRY(compose->newsgroups_entry)); + if (*entry_str != '\0') { + str = g_strdup(entry_str); + g_strstrip(str); + remove_space(str); + if (*str != '\0') { + compose->newsgroup_list = + newsgroup_list_append + (compose->newsgroup_list, str); + compose_convert_header(compose, + buf, sizeof(buf), str, + strlen("Newsgroups: "), + FALSE, NULL); + fprintf(fp, "Newsgroups: %s\n", buf); + } + g_free(str); + } + } + + if (!compose->to_list && !compose->newsgroup_list) + return -1; + + /* Subject */ + entry_str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry)); + if (*entry_str != '\0') { + str = g_strdup(entry_str); + g_strstrip(str); + if (*str != '\0') { + compose_convert_header(compose, buf, sizeof(buf), str, + strlen("Subject: "), FALSE, + NULL); + fprintf(fp, "Subject: %s\n", buf); + } + g_free(str); + } + + /* Resent-Message-Id */ + if (compose->account->gen_msgid) { + compose_generate_msgid(compose, buf, sizeof(buf)); + fprintf(fp, "Resent-Message-Id: <%s>\n", buf); + compose->msgid = g_strdup(buf); + } + + /* Followup-To */ + if (compose->use_followupto) { + entry_str = gtk_entry_get_text + (GTK_ENTRY(compose->followup_entry)); + if (*entry_str != '\0') { + str = g_strdup(entry_str); + g_strstrip(str); + remove_space(str); + if (*str != '\0') { + compose_convert_header(compose, + buf, sizeof(buf), str, + strlen("Followup-To: "), + FALSE, NULL); + fprintf(fp, "Followup-To: %s\n", buf); + } + g_free(str); + } + } + + /* Resent-Reply-To */ + if (compose->use_replyto) { + entry_str = gtk_entry_get_text(GTK_ENTRY(compose->reply_entry)); + if (*entry_str != '\0') { + str = g_strdup(entry_str); + g_strstrip(str); + if (*str != '\0') { + compose_convert_header + (compose, buf, sizeof(buf), str, + strlen("Resent-Reply-To: "), TRUE, + NULL); + fprintf(fp, "Resent-Reply-To: %s\n", buf); + } + g_free(str); + } + } + + fputs("\n", fp); + + return 0; +} + +#undef IS_IN_CUSTOM_HEADER + +static void compose_convert_header(Compose *compose, gchar *dest, gint len, + const gchar *src, gint header_len, + gboolean addr_field, const gchar *encoding) +{ + gchar *src_; + + g_return_if_fail(src != NULL); + g_return_if_fail(dest != NULL); + + if (len < 1) return; + + if (addr_field) + src_ = normalize_address_field(src); + else + src_ = g_strdup(src); + g_strchomp(src_); + if (!encoding) + encoding = conv_get_charset_str(compose->out_encoding); + + conv_encode_header(dest, len, src_, header_len, addr_field, encoding); + + g_free(src_); +} + +static gchar *compose_convert_filename(Compose *compose, const gchar *src, + const gchar *param_name, + const gchar *encoding) +{ + gchar *str; + + g_return_val_if_fail(src != NULL, NULL); + + if (!encoding) + encoding = conv_get_charset_str(compose->out_encoding); + + str = conv_encode_filename(src, param_name, encoding); + + return str; +} + +static void compose_generate_msgid(Compose *compose, gchar *buf, gint len) +{ + struct tm *lt; + time_t t; + const gchar *addr, *p; + gchar *addr_left; + gchar *addr_right; + gchar hash_str[64]; + SMD5 *md5; + gchar *md5str; + + t = time(NULL); + lt = localtime(&t); + + if (compose->account && compose->account->address && + *compose->account->address) { + addr = compose->account->address; + if ((p = strchr(addr, '@'))) { + addr_left = g_strndup(addr, p - addr); + addr_right = g_strdup(p + 1); + } else { + addr_left = g_strdup(addr); + addr_right = g_strdup(get_domain_name()); + } + } else { + addr_left = g_strdup(g_get_user_name()); + addr_right = g_strdup(get_domain_name()); + } + + g_snprintf(hash_str, sizeof(hash_str), "%08x%s", + g_random_int(), addr_left); + md5 = s_gnet_md5_new((guchar *)hash_str, strlen(hash_str)); + md5str = s_gnet_md5_get_string(md5); + + g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%.24s@%s", + lt->tm_year + 1900, lt->tm_mon + 1, + lt->tm_mday, lt->tm_hour, + lt->tm_min, lt->tm_sec, + md5str, addr_right); + + g_free(md5str); + s_gnet_md5_delete(md5); + g_free(addr_right); + g_free(addr_left); + + debug_print("generated Message-ID: %s\n", buf); +} + +static void compose_add_entry_field(GtkWidget *table, GtkWidget **hbox, + GtkWidget **entry, gint *count, + const gchar *label_str, + gboolean is_addr_entry) +{ + GtkWidget *label; + + if (GTK_TABLE(table)->nrows < (*count) + 1) + gtk_table_resize(GTK_TABLE(table), (*count) + 1, 2); + + *hbox = gtk_hbox_new(FALSE, 0); + label = gtk_label_new + (prefs_common.trans_hdr ? gettext(label_str) : label_str); + gtk_box_pack_end(GTK_BOX(*hbox), label, FALSE, FALSE, 0); + gtk_table_attach(GTK_TABLE(table), *hbox, 0, 1, *count, (*count) + 1, + GTK_FILL, 0, 2, 0); + *entry = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(*entry), MAX_ENTRY_LENGTH); + gtk_table_attach_defaults + (GTK_TABLE(table), *entry, 1, 2, *count, (*count) + 1); + if (GTK_TABLE(table)->nrows > (*count) + 1) + gtk_table_set_row_spacing(GTK_TABLE(table), *count, 4); + + if (is_addr_entry && prefs_common.enable_address_completion) + address_completion_register_entry(GTK_ENTRY(*entry)); + + (*count)++; +} + +static Compose *compose_create(PrefsAccount *account, ComposeMode mode) +{ + Compose *compose; + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *menubar; + GtkWidget *toolbar; + + GtkWidget *vbox2; + + GtkWidget *table_vbox; + GtkWidget *table; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *from_optmenu_hbox; + GtkWidget *sig_combo; + GtkWidget *to_entry; + GtkWidget *to_hbox; + GtkWidget *newsgroups_entry; + GtkWidget *newsgroups_hbox; + GtkWidget *subject_entry; + GtkWidget *cc_entry; + GtkWidget *cc_hbox; + GtkWidget *bcc_entry; + GtkWidget *bcc_hbox; + GtkWidget *reply_entry; + GtkWidget *reply_hbox; + GtkWidget *followup_entry; + GtkWidget *followup_hbox; + +#if USE_GPGME + GtkWidget *misc_hbox; + GtkWidget *signing_chkbtn; + GtkWidget *encrypt_chkbtn; +#endif /* USE_GPGME */ +#if 0 + GtkWidget *attach_img; + GtkWidget *attach_toggle; +#endif + + GtkWidget *paned; + + GtkWidget *attach_scrwin; + GtkWidget *attach_treeview; + GtkListStore *store; + GtkTreeSelection *selection; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + GtkWidget *edit_vbox; + GtkWidget *ruler_hbox; + GtkWidget *ruler; + GtkWidget *scrolledwin; + GtkWidget *text; + + GtkTextBuffer *buffer; + GtkClipboard *clipboard; + GtkTextTag *sig_tag; + +#if USE_GTKSPELL + GtkWidget *spell_menu; +#endif /* USE_GTKSPELL */ + + UndoMain *undostruct; + + guint n_menu_entries; + GdkColormap *cmap; + GdkColor color[1]; + gboolean success[1]; + GtkWidget *popupmenu; + GtkItemFactory *popupfactory; + GtkItemFactory *ifactory; + GtkWidget *tmpl_menu; + gint n_entries; + gint count = 0; + +#ifndef G_OS_WIN32 + static GdkGeometry geometry; +#endif + + g_return_val_if_fail(account != NULL, NULL); + + debug_print(_("Creating compose window...\n")); + compose = g_new0(Compose, 1); + + compose->account = account; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_wmclass(GTK_WINDOW(window), "compose", "Sylpheed"); + gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE); + gtk_widget_set_size_request(window, -1, prefs_common.compose_height); + gtkut_window_move(GTK_WINDOW(window), prefs_common.compose_x, + prefs_common.compose_y); + +#ifndef G_OS_WIN32 + if (!geometry.max_width) { + geometry.max_width = gdk_screen_width(); + geometry.max_height = gdk_screen_height(); + } + gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, + &geometry, GDK_HINT_MAX_SIZE); +#endif + + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(compose_delete_cb), compose); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(window), vbox); + + n_menu_entries = sizeof(compose_entries) / sizeof(compose_entries[0]); + menubar = menubar_create(window, compose_entries, + n_menu_entries, "", compose); + gtk_widget_set_size_request(menubar, 300, -1); + gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0); + + compose->toolbar_tip = gtk_tooltips_new(); + g_object_ref_sink(compose->toolbar_tip); + toolbar = compose_toolbar_create(compose); + gtk_widget_set_size_request(toolbar, 300, -1); + gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); + + vbox2 = gtk_vbox_new(FALSE, 2); + gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0); + gtk_container_set_border_width(GTK_CONTAINER(vbox2), BORDER_WIDTH); + + table_vbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox2), table_vbox, FALSE, TRUE, 0); + gtk_container_set_border_width(GTK_CONTAINER(table_vbox), BORDER_WIDTH); + + table = gtk_table_new(8, 2, FALSE); + gtk_box_pack_start(GTK_BOX(table_vbox), table, FALSE, TRUE, 0); + + /* option menu for selecting accounts */ + hbox = gtk_hbox_new(FALSE, 0); + label = gtk_label_new(prefs_common.trans_hdr ? _("From:") : "From:"); + gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, count, count + 1, + GTK_FILL, 0, 2, 0); + + from_optmenu_hbox = gtk_hbox_new(FALSE, 0); + gtk_table_attach_defaults(GTK_TABLE(table), from_optmenu_hbox, + 1, 2, count, count + 1); + //gtk_table_attach(GTK_TABLE(table), from_optmenu_hbox, + //1, 2, count, count + 1, GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + gtk_widget_set_size_request(from_optmenu_hbox, 300, -1); + gtk_table_set_row_spacing(GTK_TABLE(table), 0, 4); + count++; + compose_account_option_menu_create(compose, from_optmenu_hbox); + sig_combo = compose_signature_menu_create(compose, from_optmenu_hbox); + + /* header labels and entries */ + compose_add_entry_field(table, &to_hbox, &to_entry, &count, + "To:", TRUE); + compose_add_entry_field(table, &newsgroups_hbox, &newsgroups_entry, + &count, "Newsgroups:", FALSE); + compose_add_entry_field(table, &cc_hbox, &cc_entry, &count, + "Cc:", TRUE); + compose_add_entry_field(table, &bcc_hbox, &bcc_entry, &count, + "Bcc:", TRUE); + compose_add_entry_field(table, &reply_hbox, &reply_entry, &count, + "Reply-To:", TRUE); + compose_add_entry_field(table, &followup_hbox, &followup_entry, &count, + "Followup-To:", FALSE); + compose_add_entry_field(table, &hbox, &subject_entry, &count, + "Subject:", FALSE); + + gtk_table_set_col_spacings(GTK_TABLE(table), 4); + +#ifndef __APPLE__ + g_signal_connect(G_OBJECT(to_entry), "activate", + G_CALLBACK(to_activated), compose); + g_signal_connect(G_OBJECT(newsgroups_entry), "activate", + G_CALLBACK(newsgroups_activated), compose); + g_signal_connect(G_OBJECT(cc_entry), "activate", + G_CALLBACK(cc_activated), compose); + g_signal_connect(G_OBJECT(bcc_entry), "activate", + G_CALLBACK(bcc_activated), compose); + g_signal_connect(G_OBJECT(reply_entry), "activate", + G_CALLBACK(replyto_activated), compose); + g_signal_connect(G_OBJECT(followup_entry), "activate", + G_CALLBACK(followupto_activated), compose); + g_signal_connect(G_OBJECT(subject_entry), "activate", + G_CALLBACK(subject_activated), compose); +#endif + + g_signal_connect(G_OBJECT(to_entry), "grab_focus", + G_CALLBACK(compose_grab_focus_cb), compose); + g_signal_connect(G_OBJECT(newsgroups_entry), "grab_focus", + G_CALLBACK(compose_grab_focus_cb), compose); + g_signal_connect(G_OBJECT(cc_entry), "grab_focus", + G_CALLBACK(compose_grab_focus_cb), compose); + g_signal_connect(G_OBJECT(bcc_entry), "grab_focus", + G_CALLBACK(compose_grab_focus_cb), compose); + g_signal_connect(G_OBJECT(reply_entry), "grab_focus", + G_CALLBACK(compose_grab_focus_cb), compose); + g_signal_connect(G_OBJECT(followup_entry), "grab_focus", + G_CALLBACK(compose_grab_focus_cb), compose); + g_signal_connect(G_OBJECT(subject_entry), "grab_focus", + G_CALLBACK(compose_grab_focus_cb), compose); + +#if 0 + attach_img = stock_pixbuf_widget(window, STOCK_PIXMAP_CLIP); + attach_toggle = gtk_toggle_button_new(); + GTK_WIDGET_UNSET_FLAGS(attach_toggle, GTK_CAN_FOCUS); + gtk_container_add(GTK_CONTAINER(attach_toggle), attach_img); + gtk_box_pack_start(GTK_BOX(misc_hbox), attach_toggle, FALSE, FALSE, 8); + g_signal_connect(G_OBJECT(attach_toggle), "toggled", + G_CALLBACK(compose_attach_toggled), compose); +#endif + +#if USE_GPGME + misc_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox2), misc_hbox, FALSE, FALSE, 0); + + signing_chkbtn = gtk_check_button_new_with_label(_("PGP Sign")); + GTK_WIDGET_UNSET_FLAGS(signing_chkbtn, GTK_CAN_FOCUS); + gtk_box_pack_start(GTK_BOX(misc_hbox), signing_chkbtn, FALSE, FALSE, 8); + encrypt_chkbtn = gtk_check_button_new_with_label(_("PGP Encrypt")); + GTK_WIDGET_UNSET_FLAGS(encrypt_chkbtn, GTK_CAN_FOCUS); + gtk_box_pack_start(GTK_BOX(misc_hbox), encrypt_chkbtn, FALSE, FALSE, 8); + + g_signal_connect(G_OBJECT(signing_chkbtn), "toggled", + G_CALLBACK(compose_signing_toggled), compose); + g_signal_connect(G_OBJECT(encrypt_chkbtn), "toggled", + G_CALLBACK(compose_encrypt_toggled), compose); +#endif /* USE_GPGME */ + + /* attachment list */ + attach_scrwin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(attach_scrwin), + GTK_SHADOW_IN); + gtk_widget_set_size_request(attach_scrwin, -1, 80 * gtkut_get_dpi_multiplier()); + + store = gtk_list_store_new(N_ATTACH_COLS, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_POINTER); + + attach_treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(attach_treeview), TRUE); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_treeview), TRUE); + gtk_tree_view_set_search_column(GTK_TREE_VIEW(attach_treeview), + COL_NAME); + gtk_tree_view_set_reorderable(GTK_TREE_VIEW(attach_treeview), FALSE); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_treeview)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + + gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_treeview); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes + (_("Data type"), renderer, "text", COL_MIMETYPE, NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(column, 240); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(attach_treeview), column); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "xalign", 1.0, "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes + (_("Size"), renderer, "text", COL_SIZE, NULL); + gtk_tree_view_column_set_alignment(column, 1.0); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(column, 64); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(attach_treeview), column); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes + (_("Name"), renderer, "text", COL_NAME, NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(attach_treeview), column); + + g_signal_connect(G_OBJECT(selection), "changed", + G_CALLBACK(attach_selection_changed), compose); + g_signal_connect(G_OBJECT(attach_treeview), "button_press_event", + G_CALLBACK(attach_button_pressed), compose); + g_signal_connect(G_OBJECT(attach_treeview), "key_press_event", + G_CALLBACK(attach_key_pressed), compose); + + /* drag and drop */ + gtk_drag_dest_set(window, + GTK_DEST_DEFAULT_ALL, compose_drag_types, + N_DRAG_TYPES, GDK_ACTION_COPY | GDK_ACTION_MOVE); + g_signal_connect(G_OBJECT(window), "drag-data-received", + G_CALLBACK(compose_attach_drag_received_cb), + compose); + + /* pane between attach tree view and text */ + paned = gtk_vpaned_new(); + gtk_paned_add1(GTK_PANED(paned), attach_scrwin); + gtk_widget_ref(paned); + gtk_widget_show_all(paned); + + edit_vbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox2), edit_vbox, TRUE, TRUE, 0); + + /* ruler */ + ruler_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0); + + ruler = gtk_shruler_new(); + gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0); + gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE, 0); + + /* text widget */ + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0); + gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, + -1); + + text = gtk_text_view_new(); + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); + gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), TEXTVIEW_MARGIN); + gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), TEXTVIEW_MARGIN); + clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY); + gtk_text_buffer_add_selection_clipboard(buffer, clipboard); + sig_tag = gtk_text_buffer_create_tag(buffer, "signature", NULL); + gtk_container_add(GTK_CONTAINER(scrolledwin), text); + + gtk_shruler_set_start_pos(GTK_SHRULER(ruler), + text->style->xthickness + TEXTVIEW_MARGIN); + + g_signal_connect(G_OBJECT(text), "grab_focus", + G_CALLBACK(compose_grab_focus_cb), compose); + g_signal_connect(G_OBJECT(buffer), "insert_text", + G_CALLBACK(text_inserted), compose); + g_signal_connect_after(G_OBJECT(text), "size_allocate", + G_CALLBACK(compose_edit_size_alloc), + ruler); + + /* drag and drop */ + gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_drag_types, + N_DRAG_TYPES, GDK_ACTION_COPY | GDK_ACTION_MOVE); + g_signal_connect(G_OBJECT(text), "drag-data-received", + G_CALLBACK(compose_insert_drag_received_cb), + compose); + + gtk_widget_show_all(vbox); + + if (prefs_common.textfont) { + PangoFontDescription *font_desc; + + font_desc = pango_font_description_from_string + (prefs_common.textfont); + if (font_desc) { + gtk_widget_modify_font(text, font_desc); + pango_font_description_free(font_desc); + } + } + + gtk_text_view_set_pixels_above_lines + (GTK_TEXT_VIEW(text), + prefs_common.line_space - prefs_common.line_space / 2); + gtk_text_view_set_pixels_below_lines + (GTK_TEXT_VIEW(text), prefs_common.line_space / 2); + gtk_text_view_set_pixels_inside_wrap + (GTK_TEXT_VIEW(text), prefs_common.line_space); + + n_entries = sizeof(compose_popup_entries) / + sizeof(compose_popup_entries[0]); + popupmenu = menu_create_items(compose_popup_entries, n_entries, + "", &popupfactory, + compose); + + ifactory = gtk_item_factory_from_widget(menubar); + menu_set_sensitive(ifactory, "/Edit/Undo", FALSE); + menu_set_sensitive(ifactory, "/Edit/Redo", FALSE); + + tmpl_menu = gtk_item_factory_get_item(ifactory, "/Tools/Template"); + +#if USE_GTKSPELL + spell_menu = gtk_item_factory_get_item + (ifactory, "/Tools/Set spell language"); +#endif + + gtk_widget_hide(bcc_hbox); + gtk_widget_hide(bcc_entry); + gtk_widget_hide(reply_hbox); + gtk_widget_hide(reply_entry); + gtk_widget_hide(followup_hbox); + gtk_widget_hide(followup_entry); + gtk_widget_hide(ruler_hbox); + gtk_table_set_row_spacing(GTK_TABLE(table), 4, 0); + gtk_table_set_row_spacing(GTK_TABLE(table), 5, 0); + gtk_table_set_row_spacing(GTK_TABLE(table), 6, 0); + + if (account->protocol == A_NNTP) { + gtk_widget_hide(to_hbox); + gtk_widget_hide(to_entry); + gtk_widget_hide(cc_hbox); + gtk_widget_hide(cc_entry); + gtk_table_set_row_spacing(GTK_TABLE(table), 1, 0); + gtk_table_set_row_spacing(GTK_TABLE(table), 3, 0); + } else { + gtk_widget_hide(newsgroups_hbox); + gtk_widget_hide(newsgroups_entry); + gtk_table_set_row_spacing(GTK_TABLE(table), 2, 0); + } + +#if USE_GPGME + if (!rfc2015_is_available()) + gtk_widget_hide(misc_hbox); +#endif + + undostruct = undo_init(text); + undo_set_change_state_func(undostruct, &compose_undo_state_changed, + menubar); + + address_completion_start(window); + + compose->window = window; + compose->vbox = vbox; + compose->menubar = menubar; + compose->toolbar = toolbar; + + compose->vbox2 = vbox2; + + compose->table_vbox = table_vbox; + compose->table = table; + compose->to_hbox = to_hbox; + compose->to_entry = to_entry; + compose->newsgroups_hbox = newsgroups_hbox; + compose->newsgroups_entry = newsgroups_entry; + compose->subject_entry = subject_entry; + compose->cc_hbox = cc_hbox; + compose->cc_entry = cc_entry; + compose->bcc_hbox = bcc_hbox; + compose->bcc_entry = bcc_entry; + compose->reply_hbox = reply_hbox; + compose->reply_entry = reply_entry; + compose->followup_hbox = followup_hbox; + compose->followup_entry = followup_entry; + + compose->sig_combo = sig_combo; + + /* compose->attach_toggle = attach_toggle; */ +#if USE_GPGME + compose->misc_hbox = misc_hbox; + compose->signing_chkbtn = signing_chkbtn; + compose->encrypt_chkbtn = encrypt_chkbtn; +#endif /* USE_GPGME */ + + compose->paned = paned; + + compose->attach_scrwin = attach_scrwin; + compose->attach_treeview = attach_treeview; + compose->attach_store = store; + + compose->edit_vbox = edit_vbox; + compose->ruler_hbox = ruler_hbox; + compose->ruler = ruler; + compose->scrolledwin = scrolledwin; + compose->text = text; + +#ifdef USE_GTKSPELL + compose->check_spell = prefs_common.check_spell; + compose->spell_lang = g_strdup(prefs_common.spell_lang); + compose->spell_menu = spell_menu; + compose->dict_list = NULL; +#endif /* USE_GTKSPELL */ + + compose->focused_editable = NULL; + + compose->popupmenu = popupmenu; + compose->popupfactory = popupfactory; + + compose->tmpl_menu = tmpl_menu; + + compose->mode = mode; + + compose->targetinfo = NULL; + compose->reply_target = NULL; + compose->forward_targets = NULL; + + compose->replyto = NULL; + compose->cc = NULL; + compose->bcc = NULL; + compose->followup_to = NULL; + + compose->ml_post = NULL; + + compose->inreplyto = NULL; + compose->references = NULL; + compose->msgid = NULL; + compose->boundary = NULL; + + compose->autowrap = prefs_common.autowrap; + + compose->use_to = FALSE; + compose->use_cc = FALSE; + compose->use_bcc = FALSE; + compose->use_replyto = FALSE; + compose->use_newsgroups = FALSE; + compose->use_followupto = FALSE; + compose->use_attach = FALSE; + + compose->out_encoding = C_AUTO; + + compose->use_mdn = FALSE; + +#if USE_GPGME + compose->use_signing = FALSE; + compose->use_encryption = FALSE; +#endif /* USE_GPGME */ + + compose->modified = FALSE; + + compose->to_list = NULL; + compose->newsgroup_list = NULL; + + compose->undostruct = undostruct; + + compose->sig_tag = sig_tag; + + compose->exteditor_file = NULL; + compose->exteditor_pid = 0; + compose->exteditor_tag = 0; + + compose->autosave_tag = 0; + + compose->window_maximized = prefs_common.compose_maximized; + + compose->block_modified = FALSE; + + compose_set_toolbar_button_visibility(compose); + + compose_select_account(compose, account, TRUE); + + if (prefs_common.compose_maximized) + gtk_window_maximize(GTK_WINDOW(window)); + + g_signal_connect(G_OBJECT(window), "window_state_event", + G_CALLBACK(compose_window_state_cb), compose); + + menu_set_active(ifactory, "/Edit/Auto wrapping", prefs_common.autowrap); + menu_set_active(ifactory, "/View/Ruler", prefs_common.show_ruler); + + if (mode == COMPOSE_REDIRECT) { + menu_set_sensitive(ifactory, "/File/Save to draft folder", FALSE); + menu_set_sensitive(ifactory, "/File/Save and keep editing", FALSE); + menu_set_sensitive(ifactory, "/File/Attach file", FALSE); + menu_set_sensitive(ifactory, "/File/Insert file", FALSE); + menu_set_sensitive(ifactory, "/File/Insert signature", FALSE); + menu_set_sensitive(ifactory, "/Edit/Cut", FALSE); + menu_set_sensitive(ifactory, "/Edit/Paste", FALSE); + menu_set_sensitive(ifactory, "/Edit/Wrap current paragraph", FALSE); + menu_set_sensitive(ifactory, "/Edit/Wrap all long lines", FALSE); + menu_set_sensitive(ifactory, "/Edit/Auto wrapping", FALSE); + menu_set_sensitive(ifactory, "/View/Attachment", FALSE); + menu_set_sensitive(ifactory, "/Tools/Template", FALSE); +#ifndef G_OS_WIN32 + menu_set_sensitive(ifactory, "/Tools/Actions", FALSE); +#endif + menu_set_sensitive(ifactory, "/Tools/Edit with external editor", FALSE); +#if USE_GPGME + menu_set_sensitive(ifactory, "/Tools/PGP Sign", FALSE); + menu_set_sensitive(ifactory, "/Tools/PGP Encrypt", FALSE); +#endif /* USE_GPGME */ +#if USE_GTKSPELL + menu_set_sensitive(ifactory, "/Tools/Check spell", FALSE); + menu_set_sensitive(ifactory, "/Tools/Set spell language", + FALSE); +#endif + + if (compose->insert_btn) + gtk_widget_set_sensitive(compose->insert_btn, FALSE); + if (compose->attach_btn) + gtk_widget_set_sensitive(compose->attach_btn, FALSE); + if (compose->sig_btn) + gtk_widget_set_sensitive(compose->sig_btn, FALSE); + if (compose->exteditor_btn) + gtk_widget_set_sensitive(compose->exteditor_btn, FALSE); + if (compose->linewrap_btn) + gtk_widget_set_sensitive(compose->linewrap_btn, FALSE); + + /* gtk_widget_set_sensitive(compose->attach_toggle, FALSE); */ + + menu_set_sensitive_all(GTK_MENU_SHELL(compose->popupmenu), + FALSE); + } + +#if USE_GPGME + if (!rfc2015_is_available()) { + menu_set_sensitive(ifactory, "/Tools/PGP Sign", FALSE); + menu_set_sensitive(ifactory, "/Tools/PGP Encrypt", FALSE); + } +#endif /* USE_GPGME */ + + compose_set_out_encoding(compose); + addressbook_set_target_compose(compose); +#ifndef G_OS_WIN32 + action_update_compose_menu(ifactory, compose); +#endif + compose_set_template_menu(compose); + +#if USE_GTKSPELL + compose_set_spell_lang_menu(compose); + if (mode != COMPOSE_REDIRECT) + menu_set_active(ifactory, "/Tools/Check spell", + prefs_common.check_spell); +#endif + + compose_list = g_list_append(compose_list, compose); + + gtk_widget_show(window); + + color[0] = quote_color; + cmap = gdk_window_get_colormap(window->window); + gdk_colormap_alloc_colors(cmap, color, 1, FALSE, TRUE, success); + if (success[0] == FALSE) { + GtkStyle *style; + + g_warning("Compose: color allocation failed.\n"); + style = gtk_widget_get_style(text); + quote_color = style->black; + } + + return compose; +} + +static Compose *compose_find_window_by_target(MsgInfo *msginfo) +{ + GList *cur; + Compose *compose; + + g_return_val_if_fail(msginfo != NULL, NULL); + + for (cur = compose_list; cur != NULL; cur = cur->next) { + compose = cur->data; + if (procmsg_msginfo_equal(compose->targetinfo, msginfo)) + return compose; + } + + return NULL; +} + +static gboolean compose_window_exist(gint x, gint y) +{ + GList *cur; + Compose *compose; + gint x_, y_; + + for (cur = compose_list; cur != NULL; cur = cur->next) { + compose = cur->data; + gtkut_widget_get_uposition(compose->window, &x_, &y_); + if (x == x_ && y == y_) + return TRUE; + } + + return FALSE; +} + +static void compose_connect_changed_callbacks(Compose *compose) +{ + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + + buffer = gtk_text_view_get_buffer(text); + + g_signal_connect(G_OBJECT(buffer), "changed", + G_CALLBACK(compose_buffer_changed_cb), compose); + g_signal_connect(G_OBJECT(compose->to_entry), "changed", + G_CALLBACK(compose_changed_cb), compose); + g_signal_connect(G_OBJECT(compose->newsgroups_entry), "changed", + G_CALLBACK(compose_changed_cb), compose); + g_signal_connect(G_OBJECT(compose->cc_entry), "changed", + G_CALLBACK(compose_changed_cb), compose); + g_signal_connect(G_OBJECT(compose->bcc_entry), "changed", + G_CALLBACK(compose_changed_cb), compose); + g_signal_connect(G_OBJECT(compose->reply_entry), "changed", + G_CALLBACK(compose_changed_cb), compose); + g_signal_connect(G_OBJECT(compose->followup_entry), "changed", + G_CALLBACK(compose_changed_cb), compose); + g_signal_connect(G_OBJECT(compose->subject_entry), "changed", + G_CALLBACK(compose_changed_cb), compose); +} + +static PrefsToolbarItem items[] = +{ + {T_SEND, TRUE, toolbar_send_cb}, + {T_SEND_LATER, TRUE, toolbar_send_later_cb}, + {T_DRAFT, TRUE, toolbar_draft_cb}, + {T_INSERT_FILE, FALSE, toolbar_insert_cb}, + {T_ATTACH_FILE, FALSE, toolbar_attach_cb}, + {T_SIGNATURE, FALSE, toolbar_sig_cb}, + {T_EDITOR, FALSE, toolbar_ext_editor_cb}, + {T_LINEWRAP, FALSE, toolbar_linewrap_cb}, + {T_ADDRESS_BOOK, FALSE, toolbar_address_cb}, + {T_COMMON_PREFS, FALSE, toolbar_prefs_common_cb}, + {T_ACCOUNT_PREFS, FALSE, toolbar_prefs_account_cb}, + + {-1, FALSE, NULL} +}; + +static GtkWidget *compose_toolbar_create(Compose *compose) +{ + GtkWidget *toolbar; + const gchar *setting; + GList *item_list; + + if (prefs_common.compose_toolbar_setting && + *prefs_common.compose_toolbar_setting != '\0') + setting = prefs_common.compose_toolbar_setting; + else + setting = prefs_toolbar_get_default_compose_setting_name_list(); + + item_list = prefs_toolbar_get_item_list_from_name_list(setting); + toolbar = compose_toolbar_create_from_list(compose, item_list); + g_list_free(item_list); + + return toolbar; +} + +static GtkWidget *compose_toolbar_create_from_list(Compose *compose, + GList *item_list) +{ + GtkWidget *toolbar; + GtkWidget *icon_wid; + GtkToolItem *toolitem; + gint i; + GList *cur; + + toolbar = gtk_toolbar_new(); + gtk_toolbar_set_orientation(GTK_TOOLBAR(toolbar), + GTK_ORIENTATION_HORIZONTAL); + gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH); + g_signal_connect(G_OBJECT(toolbar), "button_press_event", + G_CALLBACK(toolbar_button_pressed), compose); + + items[0].data = &compose->send_btn; + items[1].data = &compose->sendl_btn; + items[2].data = &compose->draft_btn; + items[3].data = &compose->insert_btn; + items[4].data = &compose->attach_btn; + items[5].data = &compose->sig_btn; + items[6].data = &compose->exteditor_btn; + items[7].data = &compose->linewrap_btn; + items[8].data = &compose->addrbook_btn; + items[9].data = &compose->prefs_common_btn; + items[10].data = &compose->prefs_account_btn; + for (i = 0; i <= 10; i++) + *(GtkWidget **)items[i].data = NULL; + + for (cur = item_list; cur != NULL; cur = cur->next) { + const PrefsDisplayItem *ditem = cur->data; + PrefsToolbarItem *item; + gint width; + + if (ditem->id == T_SEPARATOR) { + toolitem = gtk_separator_tool_item_new(); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); + continue; + } + + for (item = items; item->id != -1; item++) { + if (ditem->id == item->id) + break; + } + if (item->id == -1) + continue; + + icon_wid = stock_pixbuf_widget_for_toolbar(ditem->icon); + toolitem = gtk_tool_button_new(icon_wid, gettext(ditem->label)); + if (ditem->description) { + gtk_tool_item_set_tooltip(toolitem, + compose->toolbar_tip, + gettext(ditem->description), + ditem->name); + } + + gtkut_get_str_size(GTK_WIDGET(toolitem), gettext(ditem->label), + &width, NULL); + gtk_tool_item_set_homogeneous + (toolitem, width < 52 ? TRUE : FALSE); + gtk_tool_item_set_is_important(toolitem, item->is_important); + + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); + + g_signal_connect(G_OBJECT(toolitem), "clicked", + G_CALLBACK(item->callback), compose); + g_signal_connect(G_OBJECT(GTK_BIN(toolitem)->child), + "button_press_event", + G_CALLBACK(toolbar_button_pressed), compose); + + *(GtkWidget **)item->data = GTK_WIDGET(toolitem); + } + + gtk_widget_show_all(toolbar); + + return toolbar; +} + +static void compose_set_toolbar_button_visibility(Compose *compose) +{ + GtkToolbarStyle style = GTK_TOOLBAR_BOTH_HORIZ; + + if (prefs_common.toolbar_style == TOOLBAR_NONE) + style = -1; + else if (prefs_common.toolbar_style == TOOLBAR_ICON) + style = GTK_TOOLBAR_ICONS; + else if (prefs_common.toolbar_style == TOOLBAR_TEXT) + style = GTK_TOOLBAR_TEXT; + else if (prefs_common.toolbar_style == TOOLBAR_BOTH) + style = GTK_TOOLBAR_BOTH; + else if (prefs_common.toolbar_style == TOOLBAR_BOTH_HORIZ) + style = GTK_TOOLBAR_BOTH_HORIZ; + + if (style != -1) { + gtk_toolbar_set_style(GTK_TOOLBAR(compose->toolbar), style); + gtk_widget_show(compose->toolbar); + gtk_widget_queue_resize(compose->toolbar); + } else + gtk_widget_hide(compose->toolbar); +} + +static GtkWidget *compose_account_option_menu_create(Compose *compose, + GtkWidget *hbox) +{ + GList *accounts; + GtkWidget *optmenu; + GtkWidget *menu; + gint num = 0, def_menu = 0; + + accounts = account_get_list(); + g_return_val_if_fail(accounts != NULL, NULL); + + optmenu = gtk_option_menu_new(); + gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0); + menu = gtk_menu_new(); + + for (; accounts != NULL; accounts = accounts->next, num++) { + PrefsAccount *ac = (PrefsAccount *)accounts->data; + GtkWidget *menuitem; + gchar *name; + + if (ac == compose->account) def_menu = num; + + if (ac->name) + name = g_strdup_printf("%s: %s <%s>", + ac->account_name, + ac->name, ac->address); + else + name = g_strdup_printf("%s: %s", + ac->account_name, ac->address); + MENUITEM_ADD(menu, menuitem, name, ac); + g_free(name); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(account_activated), + compose); + } + + gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu); + gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), def_menu); + + return optmenu; +} + +static GtkWidget *compose_signature_menu_create(Compose *compose, + GtkWidget *hbox) +{ + GtkWidget *label; + GtkWidget *combo; + gint i; + + label = gtk_label_new(_("Signature:")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 4); + + combo = gtk_combo_box_new_text(); + gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0); + + for (i = 0; i < 10; i++) { + gchar buf[256]; + g_snprintf(buf, sizeof(buf), _("Signature %d"), i + 1); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), buf); + } + gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); + g_signal_connect(GTK_COMBO_BOX(combo), "changed", + G_CALLBACK(sig_combo_changed), compose); + + gtk_widget_set_tooltip_text(combo, _("Change signature")); + + return combo; +} + +static void compose_set_out_encoding(Compose *compose) +{ + GtkItemFactoryEntry *entry; + GtkItemFactory *ifactory; + CharSet out_encoding; + gchar *path, *p, *q; + GtkWidget *item; + + out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset); + ifactory = gtk_item_factory_from_widget(compose->menubar); + + for (entry = compose_entries; entry->callback != compose_address_cb; + entry++) { + if (entry->callback == compose_set_encoding_cb && + (CharSet)entry->callback_action == out_encoding) { + p = q = path = g_strdup(entry->path); + while (*p) { + if (*p == '_') { + if (p[1] == '_') { + p++; + *q++ = '_'; + } + } else + *q++ = *p; + p++; + } + *q = '\0'; + item = gtk_item_factory_get_item(ifactory, path); + gtk_widget_activate(item); + g_free(path); + break; + } + } +} + +#if USE_GTKSPELL +#if USE_ENCHANT +static void ench_dict_desc_cb(const char *const lang_tag, + const char *const provider_name, + const char *const provider_desc, + const char *const provider_file, + void *user_data) +{ + GSList **dict_list = (GSList **)user_data; + *dict_list = g_slist_append(*dict_list, g_strdup((gchar*)lang_tag)); +} + +static void compose_set_spell_lang_menu(Compose *compose) +{ + EnchantBroker *eb; + GSList *dict_list = NULL, *menu_list = NULL, *cur; + GtkWidget *menu; + gboolean lang_set = FALSE; + + eb = enchant_broker_init(); + enchant_broker_list_dicts(eb, ench_dict_desc_cb, &dict_list); + enchant_broker_free(eb); + + for (cur = dict_list; cur != NULL; cur = cur->next) { + if (compose->spell_lang != NULL && + g_ascii_strcasecmp(compose->spell_lang, + (gchar *)cur->data) == 0) + lang_set = TRUE; + } +#else /* !USE_ENCHANT */ +static void compose_set_spell_lang_menu(Compose *compose) +{ + AspellConfig *config; + AspellDictInfoList *dlist; + AspellDictInfoEnumeration *dels; + const AspellDictInfo *entry; + GSList *dict_list = NULL, *menu_list = NULL, *cur; + GtkWidget *menu; + gboolean lang_set = FALSE; + + config = new_aspell_config(); + dlist = get_aspell_dict_info_list(config); + delete_aspell_config(config); + + dels = aspell_dict_info_list_elements(dlist); + while ((entry = aspell_dict_info_enumeration_next(dels)) != 0) { + dict_list = g_slist_append(dict_list, g_strdup(entry->name)); + if (compose->spell_lang != NULL && + g_ascii_strcasecmp(compose->spell_lang, entry->name) == 0) + lang_set = TRUE; + } + delete_aspell_dict_info_enumeration(dels); +#endif /* USE_ENCHANT */ + + compose->dict_list = dict_list; + + menu = gtk_menu_new(); + + for (cur = dict_list; cur != NULL; cur = cur->next) { + gchar *dict = (gchar *)cur->data; + GtkWidget *item; + + if (dict == NULL) continue; + + item = gtk_radio_menu_item_new_with_label(menu_list, dict); + menu_list = gtk_radio_menu_item_get_group + (GTK_RADIO_MENU_ITEM(item)); + if (compose->spell_lang != NULL && + g_ascii_strcasecmp(compose->spell_lang, dict) == 0) + gtk_check_menu_item_set_active + (GTK_CHECK_MENU_ITEM(item), TRUE); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + g_signal_connect(G_OBJECT(item), "activate", + G_CALLBACK(compose_set_spell_lang_cb), + compose); + g_object_set_data(G_OBJECT(item), "spell-lang", dict); + gtk_widget_show(item); + + if (!lang_set && g_ascii_strcasecmp("en", dict) == 0) { + g_free(compose->spell_lang); + compose->spell_lang = g_strdup("en"); + gtk_check_menu_item_set_active + (GTK_CHECK_MENU_ITEM(item), TRUE); + } + } + + gtk_widget_show(menu); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->spell_menu), menu); +} + +static void compose_change_spell_lang_menu(Compose *compose, const gchar *lang) +{ + GtkWidget *menu; + GtkWidget *def_item = NULL; + GList *cur_item; + const gchar *dict; + + if (!lang) + return; + + menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(compose->spell_menu)); + for (cur_item = GTK_MENU_SHELL(menu)->children; cur_item != NULL; + cur_item = cur_item->next) { + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(cur_item->data))) + def_item = GTK_WIDGET(cur_item->data); + dict = g_object_get_data(G_OBJECT(cur_item->data), "spell-lang"); + if (dict && !g_ascii_strcasecmp(dict, lang)) { + gtk_check_menu_item_set_active + (GTK_CHECK_MENU_ITEM(cur_item->data), TRUE); + return; + } + } + + if (def_item) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(def_item), + TRUE); + compose_set_spell_lang_cb(def_item, compose); + } +} +#endif /* USE_GTKSPELL */ + +static void compose_set_template_menu(Compose *compose) +{ + GSList *tmpl_list, *cur; + GtkWidget *menu; + GtkWidget *item; + + tmpl_list = template_get_config(); + + menu = gtk_menu_new(); + + for (cur = tmpl_list; cur != NULL; cur = cur->next) { + Template *tmpl = (Template *)cur->data; + + item = gtk_menu_item_new_with_label(tmpl->name); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + g_signal_connect(G_OBJECT(item), "activate", + G_CALLBACK(compose_template_activate_cb), + compose); + g_object_set_data(G_OBJECT(item), "template", tmpl); + gtk_widget_show(item); + } + + gtk_widget_show(menu); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu); +} + +void compose_reflect_prefs_all(void) +{ + GList *cur; + Compose *compose; + + for (cur = compose_list; cur != NULL; cur = cur->next) { + compose = (Compose *)cur->data; + + if (compose->autosave_tag > 0) { + g_source_remove(compose->autosave_tag); + compose->autosave_tag = 0; + } + + compose_set_template_menu(compose); + + if (prefs_common.enable_autosave && + prefs_common.autosave_itv > 0 && + compose->mode != COMPOSE_REDIRECT) + compose->autosave_tag = + g_timeout_add_full + (G_PRIORITY_LOW, + prefs_common.autosave_itv * 60 * 1000, + autosave_timeout, compose, NULL); + } +} + +GtkWidget *compose_get_toolbar(Compose *compose) +{ + g_return_val_if_fail(compose != NULL, NULL); + return compose->toolbar; +} + +GtkWidget *compose_get_misc_hbox(Compose *compose) +{ + g_return_val_if_fail(compose != NULL, NULL); + return compose->misc_hbox; +} + +GtkWidget *compose_get_textview(Compose *compose) +{ + g_return_val_if_fail(compose != NULL, NULL); + return compose->text; +} + +static void compose_template_apply(Compose *compose, Template *tmpl, + gboolean replace) +{ + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + GtkTextMark *mark; + GtkTextIter iter; + gchar *parsed_str = NULL; + + if (!tmpl || !tmpl->value) return; + + buffer = gtk_text_view_get_buffer(text); + + if (tmpl->to && *tmpl->to != '\0') + compose_entry_set(compose, tmpl->to, COMPOSE_ENTRY_TO); + if (tmpl->cc && *tmpl->cc != '\0') + compose_entry_set(compose, tmpl->cc, COMPOSE_ENTRY_CC); + if (tmpl->bcc && *tmpl->bcc != '\0') + compose_entry_set(compose, tmpl->bcc, COMPOSE_ENTRY_BCC); + if (tmpl->replyto && *tmpl->replyto != '\0') + compose_entry_set(compose, tmpl->replyto, + COMPOSE_ENTRY_REPLY_TO); + if (tmpl->subject && *tmpl->subject != '\0') + compose_entry_set(compose, tmpl->subject, + COMPOSE_ENTRY_SUBJECT); + + if (replace) + gtk_text_buffer_set_text(buffer, "", 0); + + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark); + + if (compose->reply_target) { + FolderItem *item; + gint num; + MsgInfo *msginfo = NULL; + MsgInfo *full_msginfo; + + item = folder_find_item_and_num_from_id(compose->reply_target, + &num); + if (item && num > 0) { + msginfo = folder_item_get_msginfo(item, num); + if (msginfo) { + full_msginfo = procmsg_msginfo_get_full_info(msginfo); + if (full_msginfo) { + procmsg_msginfo_free(msginfo); + msginfo = full_msginfo; + } + } + } + parsed_str = compose_quote_fmt(compose, msginfo, + tmpl->value, + prefs_common.quotemark, NULL); + procmsg_msginfo_free(msginfo); + } else if (compose->forward_targets) { + FolderItem *item; + gint num; + gchar **targets; + gint i; + MsgInfo *msginfo; + MsgInfo *full_msginfo; + + targets = g_strsplit(compose->forward_targets, "\n", 0); + + for (i = 0; targets[i] != NULL; i++) { + g_strstrip(targets[i]); + item = folder_find_item_and_num_from_id(targets[i], &num); + if (!item || num <= 0) + continue; + msginfo = procmsg_get_msginfo(item, num); + if (!msginfo) + continue; + full_msginfo = procmsg_msginfo_get_full_info(msginfo); + parsed_str = compose_quote_fmt(compose, full_msginfo ? full_msginfo : msginfo, + tmpl->value, + prefs_common.fw_quotemark, NULL); + procmsg_msginfo_free(full_msginfo); + procmsg_msginfo_free(msginfo); + } + g_strfreev(targets); + } else { + parsed_str = compose_quote_fmt(compose, NULL, tmpl->value, + NULL, NULL); + } + + if (replace && parsed_str && prefs_common.auto_sig) + compose_insert_sig(compose, TRUE, FALSE, FALSE); + + if (replace && parsed_str) { + gtk_text_buffer_get_start_iter(buffer, &iter); + gtk_text_buffer_place_cursor(buffer, &iter); + } + + if (parsed_str) + compose_changed_cb(NULL, compose); +} + +static void compose_destroy(Compose *compose) +{ + GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store); + GtkTreeIter iter; + gboolean valid; + AttachInfo *ainfo; + GtkTextBuffer *buffer; + GtkClipboard *clipboard; + + compose_list = g_list_remove(compose_list, compose); + + if (compose->autosave_tag > 0) + g_source_remove(compose->autosave_tag); + + syl_plugin_signal_emit("compose-destroy", compose); + + /* NOTE: address_completion_end() does nothing with the window + * however this may change. */ + address_completion_end(compose->window); + +#if USE_GTKSPELL + slist_free_strings(compose->dict_list); + g_slist_free(compose->dict_list); + g_free(compose->spell_lang); +#endif + + slist_free_strings(compose->to_list); + g_slist_free(compose->to_list); + slist_free_strings(compose->newsgroup_list); + g_slist_free(compose->newsgroup_list); + + procmsg_msginfo_free(compose->targetinfo); + g_free(compose->reply_target); + g_free(compose->forward_targets); + g_free(compose->replyto); + g_free(compose->cc); + g_free(compose->bcc); + g_free(compose->newsgroups); + g_free(compose->followup_to); + + g_free(compose->ml_post); + + g_free(compose->inreplyto); + g_free(compose->references); + g_free(compose->msgid); + g_free(compose->boundary); + + if (compose->undostruct) + undo_destroy(compose->undostruct); + + if (compose->exteditor_file) { + g_unlink(compose->exteditor_file); + g_free(compose->exteditor_file); + } + + for (valid = gtk_tree_model_get_iter_first(model, &iter); valid; + valid = gtk_tree_model_iter_next(model, &iter)) { + gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1); + compose_attach_info_free(ainfo); + } + + if (addressbook_get_target_compose() == compose) + addressbook_set_target_compose(NULL); + + prefs_common.compose_maximized = compose->window_maximized; + if (!prefs_common.compose_maximized) { + gtkut_widget_get_uposition(compose->window, + &prefs_common.compose_x, + &prefs_common.compose_y); + prefs_common.compose_width = + compose->scrolledwin->allocation.width; + prefs_common.compose_height = + compose->window->allocation.height; + } + + if (!gtk_widget_get_parent(compose->paned)) + gtk_widget_destroy(compose->paned); + gtk_widget_destroy(compose->popupmenu); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text)); + clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY); + gtk_text_buffer_remove_selection_clipboard(buffer, clipboard); + + g_object_unref(compose->toolbar_tip); + + gtk_widget_destroy(compose->window); + + g_free(compose); +} + +static void compose_attach_info_free(AttachInfo *ainfo) +{ + g_free(ainfo->file); + g_free(ainfo->content_type); + g_free(ainfo->name); + g_free(ainfo); +} + +static void compose_attach_remove_selected(Compose *compose) +{ + GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store); + GtkTreeSelection *selection; + GtkTreeIter iter; + GList *rows, *cur; + AttachInfo *ainfo; + + selection = gtk_tree_view_get_selection + (GTK_TREE_VIEW(compose->attach_treeview)); + + rows = gtk_tree_selection_get_selected_rows(selection, NULL); + + /* delete from below so that GtkTreePath doesn't point wrong row */ + rows = g_list_reverse(rows); + + for (cur = rows; cur != NULL; cur = cur->next) { + gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)cur->data); + gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1); + compose_attach_info_free(ainfo); + gtk_list_store_remove(compose->attach_store, &iter); + gtk_tree_path_free((GtkTreePath *)cur->data); + } + + g_list_free(rows); + + syl_plugin_signal_emit("compose-attach-changed", compose); +} + +void compose_attach_remove_all(Compose *compose) +{ + GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store); + GtkTreeIter iter; + AttachInfo *ainfo; + + g_return_if_fail(compose != NULL); + + if (gtk_tree_model_get_iter_first(model, &iter) == FALSE) + return; + + do { + gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1); + compose_attach_info_free(ainfo); + } while (gtk_tree_model_iter_next(model, &iter) == TRUE); + + gtk_list_store_clear(GTK_LIST_STORE(model)); + + syl_plugin_signal_emit("compose-attach-changed", compose); +} + +GSList *compose_get_attach_list(Compose *compose) +{ + GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store); + GtkTreeIter iter; + gboolean valid; + AttachInfo *ainfo; + GSList *alist = NULL; + + if (!compose->use_attach) + return NULL; + + for (valid = gtk_tree_model_get_iter_first(model, &iter); valid; + valid = gtk_tree_model_iter_next(model, &iter)) { + gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1); + alist = g_slist_append(alist, ainfo); + } + + return alist; +} + +static struct _AttachProperty +{ + GtkWidget *window; + GtkWidget *mimetype_entry; + GtkWidget *encoding_optmenu; + GtkWidget *path_entry; + GtkWidget *filename_entry; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; +} attach_prop; + +static void compose_attach_property(Compose *compose) +{ + GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store); + GtkTreeSelection *selection; + GtkTreeIter iter; + GList *rows; + AttachInfo *ainfo; + gchar *path = NULL; + GtkOptionMenu *optmenu; + static gboolean cancelled; + + selection = gtk_tree_view_get_selection + (GTK_TREE_VIEW(compose->attach_treeview)); + + rows = gtk_tree_selection_get_selected_rows(selection, NULL); + + if (!rows) + return; + + gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)rows->data); + gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1); + + g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL); + g_list_free(rows); + + compose_attach_property_create(&cancelled); + gtk_widget_grab_focus(attach_prop.ok_btn); + gtk_widget_show(attach_prop.window); + manage_window_focus_in(compose->window, NULL, NULL); + manage_window_set_transient(GTK_WINDOW(attach_prop.window)); + + optmenu = GTK_OPTION_MENU(attach_prop.encoding_optmenu); + if (ainfo->encoding == ENC_UNKNOWN) + gtk_option_menu_set_history(optmenu, ENC_BASE64); + else + gtk_option_menu_set_history(optmenu, ainfo->encoding); + + if (ainfo->file) + path = conv_filename_to_utf8(ainfo->file); + + gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry), + ainfo->content_type ? ainfo->content_type : ""); + gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry), path ? path : ""); + gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry), + ainfo->name ? ainfo->name : ""); + + g_free(path); + + for (;;) { + const gchar *entry_text; + gchar *text; + gchar *cnttype = NULL; + gchar *file = NULL; + off_t size = 0; + GtkWidget *menu; + GtkWidget *menuitem; + + cancelled = FALSE; + gtk_main(); + + if (cancelled == TRUE) + break; + + entry_text = gtk_entry_get_text + (GTK_ENTRY(attach_prop.mimetype_entry)); + if (*entry_text != '\0') { + gchar *p; + + text = g_strstrip(g_strdup(entry_text)); + if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) { + cnttype = g_strdup(text); + g_free(text); + } else { + alertpanel_error(_("Invalid MIME type.")); + g_free(text); + continue; + } + } + + menu = gtk_option_menu_get_menu(optmenu); + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + ainfo->encoding = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID)); + + entry_text = gtk_entry_get_text + (GTK_ENTRY(attach_prop.path_entry)); + if (*entry_text != '\0') { + file = conv_filename_from_utf8(entry_text); + if (!is_file_exist(file) || + (size = get_file_size(file)) <= 0) { + alertpanel_error + (_("File doesn't exist or is empty.")); + g_free(file); + g_free(cnttype); + continue; + } + g_free(ainfo->file); + ainfo->file = file; + } + + entry_text = gtk_entry_get_text + (GTK_ENTRY(attach_prop.filename_entry)); + if (*entry_text != '\0') { + g_free(ainfo->name); + ainfo->name = g_strdup(entry_text); + } + + if (cnttype) { + g_free(ainfo->content_type); + ainfo->content_type = cnttype; + } + if (size) + ainfo->size = size; + + gtk_list_store_set(compose->attach_store, &iter, + COL_MIMETYPE, ainfo->content_type, + COL_SIZE, to_human_readable(ainfo->size), + COL_NAME, ainfo->name, + -1); + break; + } + + gtk_widget_destroy(attach_prop.window); + memset(&attach_prop, 0, sizeof(attach_prop)); +} + +#define SET_LABEL_AND_ENTRY(str, entry, top) \ +{ \ + label = gtk_label_new(str); \ + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \ + GTK_FILL, 0, 0, 0); \ + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \ + \ + entry = gtk_entry_new(); \ + gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \ + GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \ +} + +static void compose_attach_property_create(gboolean *cancelled) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *label; + GtkWidget *mimetype_entry; + GtkWidget *hbox; + GtkWidget *optmenu; + GtkWidget *optmenu_menu; + GtkWidget *menuitem; + GtkWidget *path_entry; + GtkWidget *filename_entry; + GtkWidget *hbbox; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + + debug_print("Creating attach property window...\n"); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request(window, 480 * gtkut_get_dpi_multiplier(), -1); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + gtk_window_set_title(GTK_WINDOW(window), _("Properties")); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(attach_property_delete_event), + cancelled); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(attach_property_key_pressed), + cancelled); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add(GTK_CONTAINER(window), vbox); + + table = gtk_table_new(4, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + + SET_LABEL_AND_ENTRY(_("MIME type"), mimetype_entry, 0); + + label = gtk_label_new(_("Encoding")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2, + GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + optmenu = gtk_option_menu_new(); + gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0); + + optmenu_menu = gtk_menu_new(); + MENUITEM_ADD(optmenu_menu, menuitem, "7bit", ENC_7BIT); + gtk_widget_set_sensitive(menuitem, FALSE); + MENUITEM_ADD(optmenu_menu, menuitem, "8bit", ENC_8BIT); + gtk_widget_set_sensitive(menuitem, FALSE); + MENUITEM_ADD(optmenu_menu, menuitem, "quoted-printable", + ENC_QUOTED_PRINTABLE); + MENUITEM_ADD(optmenu_menu, menuitem, "base64", ENC_BASE64); + + gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), optmenu_menu); + + SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2); + SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3); + + gtkut_stock_button_set_create(&hbbox, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_btn); + + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(attach_property_ok), + cancelled); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(attach_property_cancel), + cancelled); + + gtk_widget_show_all(vbox); + + attach_prop.window = window; + attach_prop.mimetype_entry = mimetype_entry; + attach_prop.encoding_optmenu = optmenu; + attach_prop.path_entry = path_entry; + attach_prop.filename_entry = filename_entry; + attach_prop.ok_btn = ok_btn; + attach_prop.cancel_btn = cancel_btn; +} + +#undef SET_LABEL_AND_ENTRY + +static void attach_property_ok(GtkWidget *widget, gboolean *cancelled) +{ + *cancelled = FALSE; + gtk_main_quit(); +} + +static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled) +{ + *cancelled = TRUE; + gtk_main_quit(); +} + +static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event, + gboolean *cancelled) +{ + *cancelled = TRUE; + gtk_main_quit(); + + return TRUE; +} + +static gboolean attach_property_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gboolean *cancelled) +{ + if (event && event->keyval == GDK_Escape) { + *cancelled = TRUE; + gtk_main_quit(); + } + return FALSE; +} + +static void compose_attach_open(Compose *compose) +{ + GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store); + GtkTreeSelection *selection; + GtkTreeIter iter; + GList *rows; + AttachInfo *ainfo = NULL; +#ifdef G_OS_WIN32 + DWORD dwtype; +#endif + + selection = gtk_tree_view_get_selection + (GTK_TREE_VIEW(compose->attach_treeview)); + + rows = gtk_tree_selection_get_selected_rows(selection, NULL); + + if (!rows) + return; + + gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)rows->data); + gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1); + + g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL); + g_list_free(rows); + + if (!ainfo || !ainfo->file) + return; + + if (!is_file_exist(ainfo->file)) { + alertpanel_error(_("File not exist.")); + return; + } + +#ifdef G_OS_WIN32 + if (g_file_test(ainfo->file, G_FILE_TEST_IS_EXECUTABLE) || + str_has_suffix_case(ainfo->file, ".scr") || + str_has_suffix_case(ainfo->file, ".pif") || + GetBinaryType(ainfo->file, &dwtype)) { + alertpanel_full + (_("Opening executable file"), + _("This is an executable file. Opening executable file is restricted for security.\n" + "If you want to launch it, save it to somewhere and make sure it is not an virus or something like a malicious program."), + ALERT_WARNING, G_ALERTDEFAULT, FALSE, + GTK_STOCK_OK, NULL, NULL); + return; + } + execute_open_file(ainfo->file, ainfo->content_type); +#else + procmime_execute_open_file(ainfo->file, ainfo->content_type); +#endif +} + +static void compose_exec_ext_editor(Compose *compose) +{ + gchar *tmp; + GPid pid; + static gchar *def_cmd = "emacs %s"; + gchar buf[1024]; + gchar **cmdline; + GError *error = NULL; + + tmp = g_strdup_printf("%s%ctmpmsg-%p.txt", get_tmp_dir(), + G_DIR_SEPARATOR, compose); + + if (compose_write_body_to_file(compose, tmp) < 0) { + g_warning("Coundn't write to file: %s\n", tmp); + g_free(tmp); + return; + } +#ifdef G_OS_WIN32 + if (canonicalize_file_replace(tmp) < 0) { + g_warning("Coundn't write to file: %s\n", tmp); + g_free(tmp); + return; + } +#endif + + if (prefs_common.ext_editor_cmd && + str_find_format_times(prefs_common.ext_editor_cmd, 's') == 1) + g_snprintf(buf, sizeof(buf), prefs_common.ext_editor_cmd, tmp); + else { + if (prefs_common.ext_editor_cmd) + g_warning(_("External editor command line is invalid: `%s'\n"), + prefs_common.ext_editor_cmd); + g_snprintf(buf, sizeof(buf), def_cmd, tmp); + } + + cmdline = strsplit_with_quote(buf, " ", 1024); + + if (g_spawn_async(NULL, cmdline, NULL, + G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, + NULL, NULL, &pid, &error) == FALSE) { + g_warning("Couldn't execute external editor: %s\n", buf); + if (error) { + g_warning("g_spawn_async(): %s\n", error->message); + g_error_free(error); + } + g_strfreev(cmdline); + g_unlink(tmp); + g_free(tmp); + return; + } + if (pid == 0) { + g_warning("Couldn't get PID of external editor\n"); + g_strfreev(cmdline); + g_unlink(tmp); + g_free(tmp); + return; + } + + g_strfreev(cmdline); + + compose_set_ext_editor_sensitive(compose, FALSE); + + debug_print("compose_exec_ext_editor(): pid: %d file: %s\n", pid, tmp); + + compose->exteditor_file = tmp; + compose->exteditor_pid = pid; + compose->exteditor_tag = + g_child_watch_add(pid, compose_ext_editor_child_exit, compose); +} + +static gboolean compose_ext_editor_kill(Compose *compose) +{ +#ifdef G_OS_WIN32 + DWORD exitcode; +#endif + gint ret; + + g_return_val_if_fail(compose->exteditor_pid != 0, TRUE); + +#ifdef G_OS_WIN32 + ret = GetExitCodeProcess(compose->exteditor_pid, &exitcode); + + if (ret && exitcode == STILL_ACTIVE) { +#else + ret = kill(compose->exteditor_pid, 0); + + if (ret == 0 || (ret == -1 && EPERM == errno)) { +#endif + AlertValue val; + gchar *msg; + + msg = g_strdup_printf + (_("The external editor is still working.\n" + "Force terminating the process (pid: %d)?\n"), + compose->exteditor_pid); + val = alertpanel_full(_("Notice"), msg, ALERT_NOTICE, + G_ALERTALTERNATE, FALSE, + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + g_free(msg); + + if (val != G_ALERTDEFAULT) + return FALSE; + } + + if (compose->exteditor_tag != 0) { + g_source_remove(compose->exteditor_tag); + compose->exteditor_tag = 0; + } + + if (compose->exteditor_pid != 0) { +#ifdef G_OS_WIN32 + if (TerminateProcess(compose->exteditor_pid, 1) == 0) + g_warning("TerminateProcess() failed: %d\n", + GetLastError()); +#else + if (kill(compose->exteditor_pid, SIGTERM) < 0) + perror("kill"); +#endif + g_message("Terminated process group id: %d\n", + compose->exteditor_pid); + g_message("Temporary file: %s\n", + compose->exteditor_file); + compose->exteditor_pid = 0; + } + + if (compose->exteditor_file) { + g_unlink(compose->exteditor_file); + g_free(compose->exteditor_file); + compose->exteditor_file = NULL; + } + + compose_set_ext_editor_sensitive(compose, TRUE); + + return TRUE; +} + +static void compose_ext_editor_child_exit(GPid pid, gint status, gpointer data) +{ + Compose *compose = (Compose *)data; + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + GtkTextMark *mark; + GtkTextIter iter; + + debug_print("Compose: child exit (pid: %d status: %d)\n", pid, status); + + compose_lock(compose); + + g_spawn_close_pid(pid); + + buffer = gtk_text_view_get_buffer(text); + + gtk_text_buffer_set_text(buffer, "", 0); + compose_insert_file(compose, compose->exteditor_file, FALSE); + compose_enable_sig(compose); + + gtk_text_buffer_get_start_iter(buffer, &iter); + gtk_text_buffer_place_cursor(buffer, &iter); + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_view_scroll_mark_onscreen(text, mark); + + compose_changed_cb(NULL, compose); + + if (g_unlink(compose->exteditor_file) < 0) + FILE_OP_ERROR(compose->exteditor_file, "unlink"); + + compose_set_ext_editor_sensitive(compose, TRUE); + gtk_window_present(GTK_WINDOW(compose->window)); + + g_free(compose->exteditor_file); + compose->exteditor_file = NULL; + compose->exteditor_pid = 0; + compose->exteditor_tag = 0; + + compose_unlock(compose); +} + +static void compose_set_ext_editor_sensitive(Compose *compose, + gboolean sensitive) +{ + GtkItemFactory *ifactory; + + ifactory = gtk_item_factory_from_widget(compose->menubar); + + menu_set_sensitive(ifactory, "/File/Send", sensitive); + menu_set_sensitive(ifactory, "/File/Send later", sensitive); + menu_set_sensitive(ifactory, "/File/Save to draft folder", + sensitive); + menu_set_sensitive(ifactory, "/File/Insert file", sensitive); + menu_set_sensitive(ifactory, "/File/Insert signature", sensitive); + menu_set_sensitive(ifactory, "/File/Append signature", sensitive); + menu_set_sensitive(ifactory, "/Edit/Wrap current paragraph", sensitive); + menu_set_sensitive(ifactory, "/Edit/Wrap all long lines", sensitive); + menu_set_sensitive(ifactory, "/Tools/Edit with external editor", + sensitive); + +#define SET_SENS(w) \ + if (compose->w) \ + gtk_widget_set_sensitive(compose->w, sensitive); + + SET_SENS(text); + SET_SENS(send_btn); + SET_SENS(sendl_btn); + SET_SENS(draft_btn); + SET_SENS(insert_btn); + SET_SENS(sig_btn); + SET_SENS(exteditor_btn); + SET_SENS(linewrap_btn); + +#undef SET_SENS +} + +/** + * compose_undo_state_changed: + * + * Change the sensivity of the menuentries undo and redo + **/ +static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state, + gint redo_state, gpointer data) +{ + GtkWidget *widget = GTK_WIDGET(data); + GtkItemFactory *ifactory; + + g_return_if_fail(widget != NULL); + + ifactory = gtk_item_factory_from_widget(widget); + + switch (undo_state) { + case UNDO_STATE_TRUE: + if (!undostruct->undo_state) { + debug_print ("Set_undo - Testpoint\n"); + undostruct->undo_state = TRUE; + menu_set_sensitive(ifactory, "/Edit/Undo", TRUE); + } + break; + case UNDO_STATE_FALSE: + if (undostruct->undo_state) { + undostruct->undo_state = FALSE; + menu_set_sensitive(ifactory, "/Edit/Undo", FALSE); + } + break; + case UNDO_STATE_UNCHANGED: + break; + case UNDO_STATE_REFRESH: + menu_set_sensitive(ifactory, "/Edit/Undo", + undostruct->undo_state); + break; + default: + g_warning("Undo state not recognized"); + break; + } + + switch (redo_state) { + case UNDO_STATE_TRUE: + if (!undostruct->redo_state) { + undostruct->redo_state = TRUE; + menu_set_sensitive(ifactory, "/Edit/Redo", TRUE); + } + break; + case UNDO_STATE_FALSE: + if (undostruct->redo_state) { + undostruct->redo_state = FALSE; + menu_set_sensitive(ifactory, "/Edit/Redo", FALSE); + } + break; + case UNDO_STATE_UNCHANGED: + break; + case UNDO_STATE_REFRESH: + menu_set_sensitive(ifactory, "/Edit/Redo", + undostruct->redo_state); + break; + default: + g_warning("Redo state not recognized"); + break; + } +} + +static gint calc_cursor_xpos(GtkTextView *text, gint extra, gint char_width) +{ +#if 0 + gint cursor_pos; + + cursor_pos = (text->cursor_pos_x - extra) / char_width; + cursor_pos = MAX(cursor_pos, 0); + + return cursor_pos; +#endif + return 0; +} + +/* callback functions */ + +/* compose_edit_size_alloc() - called when resized. don't know whether Gtk + * includes "non-client" (windows-izm) in calculation, so this calculation + * may not be accurate. + */ +static gboolean compose_edit_size_alloc(GtkEditable *widget, + GtkAllocation *allocation, + GtkSHRuler *shruler) +{ + if (prefs_common.show_ruler) { + gint char_width = 0, char_height = 0; + gint line_width_in_chars; + + gtkut_get_font_size(GTK_WIDGET(widget), + &char_width, &char_height); + line_width_in_chars = + (allocation->width - allocation->x) / char_width; + + /* got the maximum */ + gtk_ruler_set_range(GTK_RULER(shruler), + 0.0, line_width_in_chars, + calc_cursor_xpos(GTK_TEXT_VIEW(widget), + allocation->x, + char_width), + /*line_width_in_chars*/ char_width); + } + + return TRUE; +} + +static void toolbar_send_cb(GtkWidget *widget, gpointer data) +{ + compose_send((Compose *)data, TRUE); +} + +static void toolbar_send_later_cb(GtkWidget *widget, gpointer data) +{ + compose_send_later_cb(data, 0, NULL); +} + +static void toolbar_draft_cb(GtkWidget *widget, gpointer data) +{ + compose_draft_cb(data, 0, NULL); +} + +static void toolbar_insert_cb(GtkWidget *widget, gpointer data) +{ + compose_insert_file_cb(data, 0, NULL); +} + +static void toolbar_attach_cb(GtkWidget *widget, gpointer data) +{ + compose_attach_cb(data, 0, NULL); +} + +static void toolbar_sig_cb(GtkWidget *widget, gpointer data) +{ + Compose *compose = (Compose *)data; + + compose_insert_sig(compose, TRUE, TRUE, TRUE); +} + +static void toolbar_ext_editor_cb(GtkWidget *widget, gpointer data) +{ + Compose *compose = (Compose *)data; + + compose_exec_ext_editor(compose); +} + +static void toolbar_linewrap_cb(GtkWidget *widget, gpointer data) +{ + Compose *compose = (Compose *)data; + + compose_wrap_all(compose); +} + +static void toolbar_address_cb(GtkWidget *widget, gpointer data) +{ + compose_address_cb(data, 0, NULL); +} + +static void toolbar_prefs_common_cb(GtkWidget *widget, gpointer data) +{ + prefs_common_open(); +} + +static void toolbar_prefs_account_cb(GtkWidget *widget, gpointer data) +{ + account_open(cur_account); +} + +static void toolbar_customize(GtkWidget *widget, gpointer data) +{ + Compose *compose = (Compose *)data; + gint *visible_items; + GList *item_list = NULL; + GtkWidget *toolbar; + gint ret; + const gchar *setting; + + if (prefs_common.compose_toolbar_setting && + *prefs_common.compose_toolbar_setting != '\0') + setting = prefs_common.compose_toolbar_setting; + else + setting = prefs_toolbar_get_default_compose_setting_name_list(); + visible_items = prefs_toolbar_get_id_list_from_name_list(setting); + ret = prefs_toolbar_open(TOOLBAR_COMPOSE, visible_items, &item_list); + g_free(visible_items); + + if (ret == 0) { + gtk_widget_destroy(compose->toolbar); + toolbar = compose_toolbar_create_from_list(compose, item_list); + gtk_widget_set_size_request(toolbar, 300, -1); + gtk_box_pack_start(GTK_BOX(compose->vbox), toolbar, + FALSE, FALSE, 0); + gtk_box_reorder_child(GTK_BOX(compose->vbox), toolbar, 1); + compose->toolbar = toolbar; + compose_set_toolbar_button_visibility(compose); + g_free(prefs_common.compose_toolbar_setting); + prefs_common.compose_toolbar_setting = + prefs_toolbar_get_name_list_from_item_list(item_list); + g_list_free(item_list); + prefs_common_write_config(); + + syl_plugin_signal_emit("compose-toolbar-changed", compose); + } +} + +static gboolean toolbar_button_pressed(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + Compose *compose = (Compose *)data; + GtkWidget *menu; + GtkWidget *menuitem; + + if (!event) return FALSE; + if (event->button != 3) return FALSE; + + menu = gtk_menu_new(); + gtk_widget_show(menu); + + MENUITEM_ADD_WITH_MNEMONIC(menu, menuitem, _("_Customize toolbar..."), + 0); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(toolbar_customize), compose); + g_signal_connect(G_OBJECT(menu), "selection_done", + G_CALLBACK(gtk_widget_destroy), NULL); + + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, + event->button, event->time); + + return TRUE; +} + +static void account_activated(GtkMenuItem *menuitem, gpointer data) +{ + Compose *compose = (Compose *)data; + + PrefsAccount *ac; + + ac = (PrefsAccount *)g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID); + g_return_if_fail(ac != NULL); + + if (ac != compose->account) + compose_select_account(compose, ac, FALSE); +} + +static void sig_combo_changed(GtkComboBox *combo, gpointer data) +{ + Compose *compose = (Compose *)data; + + if (compose->mode != COMPOSE_REDIRECT && prefs_common.auto_sig) + compose_insert_sig(compose, TRUE, TRUE, FALSE); +} + +static void attach_selection_changed(GtkTreeSelection *selection, gpointer data) +{ +} + +static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + Compose *compose = (Compose *)data; + GtkTreeView *treeview = GTK_TREE_VIEW(compose->attach_treeview); + GtkTreeSelection *selection; + GtkTreePath *path = NULL; + + if (!event) return FALSE; + + gtk_tree_view_get_path_at_pos(treeview, event->x, event->y, + &path, NULL, NULL, NULL); + + if (event->button == 2 && path) + gtk_tree_view_set_cursor(treeview, path, NULL, FALSE); + + if (event->button == 2 || + (event->button == 1 && event->type == GDK_2BUTTON_PRESS)) { + compose_attach_property(compose); + } else if (event->button == 3) { + GList *rows; + gboolean has_selection = FALSE; + + selection = gtk_tree_view_get_selection(treeview); + rows = gtk_tree_selection_get_selected_rows(selection, NULL); + if (rows) { + has_selection = TRUE; + g_list_free(rows); + } + if (path) + has_selection = TRUE; + menu_set_sensitive(compose->popupfactory, "/Open", has_selection); + menu_set_sensitive(compose->popupfactory, "/Add...", TRUE); + menu_set_sensitive(compose->popupfactory, "/Remove", has_selection); + menu_set_sensitive(compose->popupfactory, "/Properties...", has_selection); + gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL, + NULL, NULL, event->button, event->time); + + if (path && + gtk_tree_selection_path_is_selected(selection, path)) { + gtk_tree_path_free(path); + return TRUE; + } + } + + gtk_tree_path_free(path); + return FALSE; +} + +static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + Compose *compose = (Compose *)data; + + if (!event) return FALSE; + + switch (event->keyval) { + case GDK_Delete: + compose_attach_remove_selected(compose); + break; + } + + return FALSE; +} + +gint compose_send(Compose *compose, gboolean close_on_success) +{ + gint val; + + if (compose->lock_count > 0) + return 1; + + gtk_widget_set_sensitive(compose->vbox, FALSE); + val = compose_send_real(compose); + gtk_widget_set_sensitive(compose->vbox, TRUE); + + if (val == 0 && close_on_success) + compose_destroy(compose); + + return val; +} + +static void compose_send_cb(gpointer data, guint action, GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + compose_send(compose, TRUE); +} + +static void compose_send_later_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + FolderItem *queue; + gchar tmp[MAXPATHLEN + 1]; + gboolean cancel = FALSE; + + if (compose->lock_count > 0) + return; + + C_LOCK(); + + if (compose_check_entries(compose) == FALSE) { + C_UNLOCK(); + return; + } + if (compose_check_attachments(compose) == FALSE) { + C_UNLOCK(); + return; + } + if (compose_check_recipients(compose) == FALSE) { + C_UNLOCK(); + return; + } + + queue = account_get_special_folder(compose->account, F_QUEUE); + if (!queue) { + g_warning("can't find queue folder\n"); + C_UNLOCK(); + return; + } + + if (!FOLDER_IS_LOCAL(queue->folder)) { + if (compose_check_activities(compose) == FALSE) { + C_UNLOCK(); + return; + } + if (!main_window_toggle_online_if_offline(main_window_get())) { + C_UNLOCK(); + return; + } + } + + g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.%p", + get_tmp_dir(), G_DIR_SEPARATOR, compose); + + if (compose->mode == COMPOSE_REDIRECT) { + if (compose_redirect_write_to_file(compose, tmp) < 0) { + alertpanel_error(_("Can't queue the message.")); + C_UNLOCK(); + return; + } + } else { + if (compose_write_to_file(compose, tmp, FALSE) < 0) { + alertpanel_error(_("Can't queue the message.")); + C_UNLOCK(); + return; + } + } + + if (!compose->to_list && !compose->newsgroup_list) { + g_warning("can't get recipient list."); + g_unlink(tmp); + C_UNLOCK(); + return; + } + + syl_plugin_signal_emit("compose-send", compose, compose->mode, 1, + tmp, compose->to_list, &cancel); + if (cancel) { + g_unlink(tmp); + C_UNLOCK(); + return; + } + + if (compose_queue(compose, tmp) < 0) { + alertpanel_error(_("Can't queue the message.")); + g_unlink(tmp); + C_UNLOCK(); + return; + } + + if (g_unlink(tmp) < 0) + FILE_OP_ERROR(tmp, "unlink"); + + C_UNLOCK(); + compose_destroy(compose); +} + +static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + FolderItem *draft; + gchar *tmp; + gint msgnum; + MsgFlags flag = {0, 0}; + + if (compose->lock_count > 0) + return; + + draft = account_get_special_folder(compose->account, F_DRAFT); + g_return_if_fail(draft != NULL); + + C_LOCK(); + + if (!FOLDER_IS_LOCAL(draft->folder)) { + if (compose_check_activities(compose) == FALSE) { + C_UNLOCK(); + return; + } + if (!main_window_toggle_online_if_offline(main_window_get())) { + C_UNLOCK(); + return; + } + } + + tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(), + G_DIR_SEPARATOR, compose); + + if (compose_write_to_file(compose, tmp, TRUE) < 0) { + g_free(tmp); + C_UNLOCK(); + return; + } + + folder_item_scan(draft); + if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) { + g_unlink(tmp); + g_free(tmp); + C_UNLOCK(); + return; + } + g_free(tmp); + + if (compose->mode == COMPOSE_REEDIT) { + compose_remove_reedit_target(compose); + if (compose->targetinfo && + compose->targetinfo->folder != draft) + folderview_update_item(compose->targetinfo->folder, + TRUE); + } + + folder_item_scan(draft); + folderview_update_item(draft, TRUE); + + /* 0: quit editing 1: keep editing */ + if (action == 0) { + C_UNLOCK(); + compose_destroy(compose); + } else { + GStatBuf s; + gchar *path; + + path = folder_item_fetch_msg(draft, msgnum); + C_UNLOCK(); + g_return_if_fail(path != NULL); + if (g_stat(path, &s) < 0) { + FILE_OP_ERROR(path, "stat"); + g_free(path); + return; + } + g_free(path); + + procmsg_msginfo_free(compose->targetinfo); + compose->targetinfo = g_new0(MsgInfo, 1); + compose->targetinfo->msgnum = msgnum; + compose->targetinfo->size = s.st_size; + compose->targetinfo->mtime = s.st_mtime; + compose->targetinfo->folder = draft; + compose->mode = COMPOSE_REEDIT; + compose->modified = FALSE; + compose_set_title(compose); + } +} + +static void compose_attach_open_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + compose_attach_open(compose); +} + +static void compose_attach_cb(gpointer data, guint action, GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + GSList *files; + GSList *cur; + + files = filesel_select_files(_("Select files"), NULL, + GTK_FILE_CHOOSER_ACTION_OPEN); + + for (cur = files; cur != NULL; cur = cur->next) { + gchar *file = (gchar *)cur->data; + gchar *utf8_filename; + + utf8_filename = conv_filename_to_utf8(file); + compose_attach_append(compose, file, utf8_filename, NULL); + compose_changed_cb(NULL, compose); + g_free(utf8_filename); + g_free(file); + } + + g_slist_free(files); +} + +static void compose_insert_file_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + gchar *file; + + file = filesel_select_file(_("Select file"), NULL, + GTK_FILE_CHOOSER_ACTION_OPEN); + + if (file && *file) + compose_insert_file(compose, file, TRUE); + + g_free(file); +} + +static void compose_insert_sig_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + compose_insert_sig(compose, action, TRUE, TRUE); +} + +static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + compose_close_cb(data, 0, NULL); + return TRUE; +} + +static gint compose_window_state_cb(GtkWidget *widget, + GdkEventWindowState *event, + gpointer data) +{ + Compose *compose = (Compose *)data; + + if ((event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) != 0) { + if ((event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0) + compose->window_maximized = TRUE; + else + compose->window_maximized = FALSE; + } + + return FALSE; +} + +static void compose_close_cb(gpointer data, guint action, GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + AlertValue val; + + if (compose->lock_count > 0) + return; + + if (compose->exteditor_pid != 0) { + if (!compose_ext_editor_kill(compose)) + return; + } + + if (compose->modified) { + val = alertpanel(_("Save message"), + _("This message has been modified. Save it to draft folder?"), +#ifdef G_OS_WIN32 + GTK_STOCK_SAVE, _("Close _without saving"), + GTK_STOCK_CANCEL); +#else + GTK_STOCK_SAVE, GTK_STOCK_CANCEL, + _("Close _without saving")); +#endif + + switch (val) { + case G_ALERTDEFAULT: + compose_draft_cb(data, 0, NULL); + return; +#ifdef G_OS_WIN32 + case G_ALERTALTERNATE: +#else + case G_ALERTOTHER: +#endif + break; + default: + return; + } + } + + compose_destroy(compose); +} + +static void compose_set_encoding_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + if (GTK_CHECK_MENU_ITEM(widget)->active) + compose->out_encoding = (CharSet)action; +} + +static void compose_address_cb(gpointer data, guint action, GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + addressbook_open(compose); +} + +static void compose_template_activate_cb(GtkWidget *widget, gpointer data) +{ + Compose *compose = (Compose *)data; + Template *tmpl; + gchar *msg; + AlertValue val; + + tmpl = g_object_get_data(G_OBJECT(widget), "template"); + g_return_if_fail(tmpl != NULL); + + msg = g_strdup_printf(_("Do you want to apply the template `%s' ?"), + tmpl->name); + val = alertpanel(_("Apply template"), msg, + _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL); + g_free(msg); + + if (val == G_ALERTDEFAULT) + compose_template_apply(compose, tmpl, TRUE); + else if (val == G_ALERTALTERNATE) + compose_template_apply(compose, tmpl, FALSE); +} + +static void compose_ext_editor_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + compose_exec_ext_editor(compose); +} + +static void compose_undo_cb(Compose *compose) +{ + gboolean prev_autowrap = compose->autowrap; + + compose->autowrap = FALSE; + undo_undo(compose->undostruct); + compose->autowrap = prev_autowrap; +} + +static void compose_redo_cb(Compose *compose) +{ + gboolean prev_autowrap = compose->autowrap; + + compose->autowrap = FALSE; + undo_redo(compose->undostruct); + compose->autowrap = prev_autowrap; +} + +static void compose_cut_cb(Compose *compose) +{ + if (compose->focused_editable && + GTK_WIDGET_HAS_FOCUS(compose->focused_editable)) { + if (GTK_IS_EDITABLE(compose->focused_editable)) { + gtk_editable_cut_clipboard + (GTK_EDITABLE(compose->focused_editable)); + } else if (GTK_IS_TEXT_VIEW(compose->focused_editable)) { + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + GtkClipboard *clipboard; + + buffer = gtk_text_view_get_buffer(text); + clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + + gtk_text_buffer_cut_clipboard(buffer, clipboard, TRUE); + } + } +} + +static void compose_copy_cb(Compose *compose) +{ + if (compose->focused_editable && + GTK_WIDGET_HAS_FOCUS(compose->focused_editable)) { + if (GTK_IS_EDITABLE(compose->focused_editable)) { + gtk_editable_copy_clipboard + (GTK_EDITABLE(compose->focused_editable)); + } else if (GTK_IS_TEXT_VIEW(compose->focused_editable)) { + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + GtkClipboard *clipboard; + + buffer = gtk_text_view_get_buffer(text); + clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + + gtk_text_buffer_copy_clipboard(buffer, clipboard); + } + } +} + +static void compose_paste_cb(Compose *compose) +{ + if (compose->focused_editable && + GTK_WIDGET_HAS_FOCUS(compose->focused_editable)) { + if (GTK_IS_EDITABLE(compose->focused_editable)) { + gtk_editable_paste_clipboard + (GTK_EDITABLE(compose->focused_editable)); + } else if (GTK_IS_TEXT_VIEW(compose->focused_editable)) { + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + GtkTextMark *mark; + GtkClipboard *clipboard; + + buffer = gtk_text_view_get_buffer(text); + mark = gtk_text_buffer_get_insert(buffer); + clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + + gtk_text_buffer_paste_clipboard(buffer, clipboard, + NULL, TRUE); + + gtk_text_view_scroll_mark_onscreen(text, mark); + } + } +} + +static void compose_paste_as_quote_cb(Compose *compose) +{ + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + GtkTextMark *mark; + GtkClipboard *clipboard; + gchar *str = NULL; + + if (!compose->focused_editable || + !GTK_WIDGET_HAS_FOCUS(compose->focused_editable) || + !GTK_IS_TEXT_VIEW(compose->focused_editable)) + return; + + buffer = gtk_text_view_get_buffer(text); + mark = gtk_text_buffer_get_insert(buffer); + clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + str = gtk_clipboard_wait_for_text(clipboard); + if (!str) + return; + + compose_quote_fmt(compose, NULL, "%Q", prefs_common.quotemark, str); + + g_free(str); + + gtk_text_view_scroll_mark_onscreen(text, mark); +} + +static void compose_allsel_cb(Compose *compose) +{ + if (compose->focused_editable && + GTK_WIDGET_HAS_FOCUS(compose->focused_editable)) { + if (GTK_IS_EDITABLE(compose->focused_editable)) { + gtk_editable_select_region + (GTK_EDITABLE(compose->focused_editable), + 0, -1); + } else if (GTK_IS_TEXT_VIEW(compose->focused_editable)) { + GtkTextView *text = GTK_TEXT_VIEW(compose->text); + GtkTextBuffer *buffer; + GtkTextIter iter; + + buffer = gtk_text_view_get_buffer(text); + gtk_text_buffer_get_start_iter(buffer, &iter); + gtk_text_buffer_place_cursor(buffer, &iter); + gtk_text_buffer_get_end_iter(buffer, &iter); + gtk_text_buffer_move_mark_by_name + (buffer, "selection_bound", &iter); + } + } +} + +static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose) +{ + if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget)) + compose->focused_editable = widget; +} + +#if USE_GPGME +static void compose_signing_toggled(GtkWidget *widget, Compose *compose) +{ + GtkItemFactory *ifactory; + + if (!rfc2015_is_available()) + return; + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) + compose->use_signing = TRUE; + else + compose->use_signing = FALSE; + + ifactory = gtk_item_factory_from_widget(compose->menubar); + menu_set_active(ifactory, "/Tools/PGP Sign", compose->use_signing); +} + +static void compose_encrypt_toggled(GtkWidget *widget, Compose *compose) +{ + GtkItemFactory *ifactory; + + if (!rfc2015_is_available()) + return; + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) + compose->use_encryption = TRUE; + else + compose->use_encryption = FALSE; + + ifactory = gtk_item_factory_from_widget(compose->menubar); + menu_set_active(ifactory, "/Tools/PGP Encrypt", + compose->use_encryption); +} +#endif /* USE_GPGME */ + +#if 0 +static void compose_attach_toggled(GtkWidget *widget, Compose *compose) +{ + GtkItemFactory *ifactory; + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) + compose->use_attach = TRUE; + else + compose->use_attach = FALSE; + + ifactory = gtk_item_factory_from_widget(compose->menubar); + menu_set_active(ifactory, "/View/Attachment", compose->use_attach); +} +#endif + +static void compose_buffer_changed_cb(GtkTextBuffer *textbuf, Compose *compose) +{ + if (compose->modified == FALSE && compose->block_modified == FALSE) { + compose->modified = TRUE; + compose_set_title(compose); + } +} + +static void compose_changed_cb(GtkEditable *editable, Compose *compose) +{ + if (compose->block_modified == FALSE && + (compose->modified == FALSE || + editable == GTK_EDITABLE(compose->subject_entry))) { + compose->modified = TRUE; + compose_set_title(compose); + } +} + +static void compose_wrap_cb(gpointer data, guint action, GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + if (action == 1) + compose_wrap_all(compose); + else + compose_wrap_paragraph(compose, NULL); +} + +static void compose_toggle_autowrap_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + compose->autowrap = GTK_CHECK_MENU_ITEM(widget)->active; + if (compose->autowrap) + compose_wrap_all_full(compose, TRUE); +} + +static void compose_toggle_to_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + if (GTK_CHECK_MENU_ITEM(widget)->active) { + gtk_widget_show(compose->to_hbox); + gtk_widget_show(compose->to_entry); + gtk_table_set_row_spacing(GTK_TABLE(compose->table), 1, 4); + compose->use_to = TRUE; + } else { + gtk_widget_hide(compose->to_hbox); + gtk_widget_hide(compose->to_entry); + gtk_table_set_row_spacing(GTK_TABLE(compose->table), 1, 0); + gtk_widget_queue_resize(compose->table_vbox); + compose->use_to = FALSE; + } +} + +static void compose_toggle_cc_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + if (GTK_CHECK_MENU_ITEM(widget)->active) { + gtk_widget_show(compose->cc_hbox); + gtk_widget_show(compose->cc_entry); + gtk_table_set_row_spacing(GTK_TABLE(compose->table), 3, 4); + compose->use_cc = TRUE; + } else { + gtk_widget_hide(compose->cc_hbox); + gtk_widget_hide(compose->cc_entry); + gtk_table_set_row_spacing(GTK_TABLE(compose->table), 3, 0); + gtk_widget_queue_resize(compose->table_vbox); + compose->use_cc = FALSE; + } +} + +static void compose_toggle_bcc_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + if (GTK_CHECK_MENU_ITEM(widget)->active) { + gtk_widget_show(compose->bcc_hbox); + gtk_widget_show(compose->bcc_entry); + gtk_table_set_row_spacing(GTK_TABLE(compose->table), 4, 4); + compose->use_bcc = TRUE; + } else { + gtk_widget_hide(compose->bcc_hbox); + gtk_widget_hide(compose->bcc_entry); + gtk_table_set_row_spacing(GTK_TABLE(compose->table), 4, 0); + gtk_widget_queue_resize(compose->table_vbox); + compose->use_bcc = FALSE; + } +} + +static void compose_toggle_replyto_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + if (GTK_CHECK_MENU_ITEM(widget)->active) { + gtk_widget_show(compose->reply_hbox); + gtk_widget_show(compose->reply_entry); + gtk_table_set_row_spacing(GTK_TABLE(compose->table), 5, 4); + compose->use_replyto = TRUE; + } else { + gtk_widget_hide(compose->reply_hbox); + gtk_widget_hide(compose->reply_entry); + gtk_table_set_row_spacing(GTK_TABLE(compose->table), 5, 0); + gtk_widget_queue_resize(compose->table_vbox); + compose->use_replyto = FALSE; + } +} + +static void compose_toggle_followupto_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + if (GTK_CHECK_MENU_ITEM(widget)->active) { + gtk_widget_show(compose->followup_hbox); + gtk_widget_show(compose->followup_entry); + gtk_table_set_row_spacing(GTK_TABLE(compose->table), 6, 4); + compose->use_followupto = TRUE; + } else { + gtk_widget_hide(compose->followup_hbox); + gtk_widget_hide(compose->followup_entry); + gtk_table_set_row_spacing(GTK_TABLE(compose->table), 6, 0); + gtk_widget_queue_resize(compose->table_vbox); + compose->use_followupto = FALSE; + } +} + +static void compose_toggle_attach_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + if (GTK_CHECK_MENU_ITEM(widget)->active) { + gtk_widget_ref(compose->edit_vbox); + + gtkut_container_remove(GTK_CONTAINER(compose->vbox2), + compose->edit_vbox); + gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox); + gtk_box_pack_start(GTK_BOX(compose->vbox2), compose->paned, + TRUE, TRUE, 0); + gtk_widget_show(compose->paned); + + gtk_widget_unref(compose->edit_vbox); + gtk_widget_unref(compose->paned); + + compose->use_attach = TRUE; + } else { + gtk_widget_ref(compose->paned); + gtk_widget_ref(compose->edit_vbox); + + gtkut_container_remove(GTK_CONTAINER(compose->vbox2), + compose->paned); + gtkut_container_remove(GTK_CONTAINER(compose->paned), + compose->edit_vbox); + gtk_box_pack_start(GTK_BOX(compose->vbox2), + compose->edit_vbox, TRUE, TRUE, 0); + + gtk_widget_unref(compose->edit_vbox); + + compose->use_attach = FALSE; + } + +#if 0 + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->attach_toggle), + compose->use_attach); +#endif + + syl_plugin_signal_emit("compose-attach-changed", compose); +} + +static void compose_customize_toolbar_cb(gpointer data, guint action, + GtkWidget *widget) +{ + toolbar_customize(widget, data); +} + +static void compose_toggle_mdn_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + if (GTK_CHECK_MENU_ITEM(widget)->active) + compose->use_mdn = TRUE; + else + compose->use_mdn = FALSE; +} + +#if USE_GPGME +static void compose_toggle_sign_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + if (!rfc2015_is_available()) + return; + + if (GTK_CHECK_MENU_ITEM(widget)->active) + compose->use_signing = TRUE; + else + compose->use_signing = FALSE; + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->signing_chkbtn), + compose->use_signing); +} + +static void compose_toggle_encrypt_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + if (!rfc2015_is_available()) + return; + + if (GTK_CHECK_MENU_ITEM(widget)->active) + compose->use_encryption = TRUE; + else + compose->use_encryption = FALSE; + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->encrypt_chkbtn), + compose->use_encryption); +} +#endif /* USE_GPGME */ + +#if USE_GTKSPELL +static void compose_toggle_spell_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + GtkSpell *speller; + + if (GTK_CHECK_MENU_ITEM(widget)->active) { + debug_print("Spell checking enabled: %s\n", + compose->spell_lang ? compose->spell_lang : "(none)"); + speller = gtkspell_new_attach(GTK_TEXT_VIEW(compose->text), + compose->spell_lang, NULL); + compose->check_spell = TRUE; + } else { + debug_print("Spell checking disabled\n"); + speller = gtkspell_get_from_text_view + (GTK_TEXT_VIEW(compose->text)); + if (speller != NULL) + gtkspell_detach(speller); + compose->check_spell = FALSE; + } +} + +static void compose_set_spell_lang_cb(GtkWidget *widget, + gpointer data) +{ + Compose *compose = (Compose *)data; + gchar *dict; + GtkSpell *speller; + + if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) + return; + + dict = g_object_get_data(G_OBJECT(widget), "spell-lang"); + + g_free(compose->spell_lang); + compose->spell_lang = g_strdup(dict); + + speller = gtkspell_get_from_text_view(GTK_TEXT_VIEW(compose->text)); + if (speller != NULL) + gtkspell_set_language(speller, dict, NULL); + + debug_print("Spell lang set to \"%s\"\n", dict); +} +#endif /* USE_GTKSPELL */ + +static void compose_toggle_ruler_cb(gpointer data, guint action, + GtkWidget *widget) +{ + Compose *compose = (Compose *)data; + + if (GTK_CHECK_MENU_ITEM(widget)->active) { + gtk_widget_show(compose->ruler_hbox); + prefs_common.show_ruler = TRUE; + } else { + gtk_widget_hide(compose->ruler_hbox); + gtk_widget_queue_resize(compose->edit_vbox); + prefs_common.show_ruler = FALSE; + } +} + +static void compose_attach_drag_received_cb (GtkWidget *widget, + GdkDragContext *drag_context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + gpointer user_data) +{ + Compose *compose = (Compose *)user_data; + GList *list, *cur; + gchar *path, *filename; + gchar *content_type = NULL; + + if (info == DRAG_TYPE_RFC822) + content_type = "message/rfc822"; + + debug_print("compose_attach_drag_received_cb(): received %s\n", + (const gchar *)data->data); + + list = uri_list_extract_filenames((const gchar *)data->data); + for (cur = list; cur != NULL; cur = cur->next) { + path = (gchar *)cur->data; + filename = conv_filename_to_utf8(path); + compose_attach_append(compose, path, filename, content_type); + compose_changed_cb(NULL, compose); + g_free(filename); + g_free(path); + } + if (list) compose_changed_cb(NULL, compose); + g_list_free(list); + + if ((drag_context->actions & GDK_ACTION_MOVE) != 0) + drag_context->action = 0; + gtk_drag_finish(drag_context, TRUE, FALSE, time); +} + +static void compose_insert_drag_received_cb (GtkWidget *widget, + GdkDragContext *drag_context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + gpointer user_data) +{ + static GdkDragContext *context_ = NULL; + static gint x_ = -1, y_ = -1; + static guint info_ = N_DRAG_TYPES; + static guint time_ = G_MAXUINT; + + debug_print("compose_insert_drag_received_cb(): received %s\n", + (const gchar *)data->data); + + /* FIXME: somehow drag-data-received signal is emitted twice. + * This hack prevents duplicated insertion. */ + if (context_ == drag_context && x_ == x && y_ == y && info_ == info && + time_ == time) { + debug_print("dup drag-data-received event\n"); + context_ = NULL; + x_ = y_ = -1; + info_ = N_DRAG_TYPES; + time_ = G_MAXUINT; + return; + } + context_ = drag_context; + x_ = x; + y_ = y; + info_ = info; + time_ = time; + + compose_attach_drag_received_cb(widget, drag_context, x, y, data, + info, time, user_data); +} + +static void to_activated(GtkWidget *widget, Compose *compose) +{ + if (GTK_WIDGET_VISIBLE(compose->newsgroups_entry)) + gtk_widget_grab_focus(compose->newsgroups_entry); + else if (GTK_WIDGET_VISIBLE(compose->cc_entry)) + gtk_widget_grab_focus(compose->cc_entry); + else if (GTK_WIDGET_VISIBLE(compose->bcc_entry)) + gtk_widget_grab_focus(compose->bcc_entry); + else if (GTK_WIDGET_VISIBLE(compose->reply_entry)) + gtk_widget_grab_focus(compose->reply_entry); + else if (GTK_WIDGET_VISIBLE(compose->followup_entry)) + gtk_widget_grab_focus(compose->followup_entry); + else + gtk_widget_grab_focus(compose->subject_entry); +} + +static void newsgroups_activated(GtkWidget *widget, Compose *compose) +{ + if (GTK_WIDGET_VISIBLE(compose->cc_entry)) + gtk_widget_grab_focus(compose->cc_entry); + else if (GTK_WIDGET_VISIBLE(compose->bcc_entry)) + gtk_widget_grab_focus(compose->bcc_entry); + else if (GTK_WIDGET_VISIBLE(compose->reply_entry)) + gtk_widget_grab_focus(compose->reply_entry); + else if (GTK_WIDGET_VISIBLE(compose->followup_entry)) + gtk_widget_grab_focus(compose->followup_entry); + else + gtk_widget_grab_focus(compose->subject_entry); +} + +static void cc_activated(GtkWidget *widget, Compose *compose) +{ + if (GTK_WIDGET_VISIBLE(compose->bcc_entry)) + gtk_widget_grab_focus(compose->bcc_entry); + else if (GTK_WIDGET_VISIBLE(compose->reply_entry)) + gtk_widget_grab_focus(compose->reply_entry); + else if (GTK_WIDGET_VISIBLE(compose->followup_entry)) + gtk_widget_grab_focus(compose->followup_entry); + else + gtk_widget_grab_focus(compose->subject_entry); +} + +static void bcc_activated(GtkWidget *widget, Compose *compose) +{ + if (GTK_WIDGET_VISIBLE(compose->reply_entry)) + gtk_widget_grab_focus(compose->reply_entry); + else if (GTK_WIDGET_VISIBLE(compose->followup_entry)) + gtk_widget_grab_focus(compose->followup_entry); + else + gtk_widget_grab_focus(compose->subject_entry); +} + +static void replyto_activated(GtkWidget *widget, Compose *compose) +{ + if (GTK_WIDGET_VISIBLE(compose->followup_entry)) + gtk_widget_grab_focus(compose->followup_entry); + else + gtk_widget_grab_focus(compose->subject_entry); +} + +static void followupto_activated(GtkWidget *widget, Compose *compose) +{ + gtk_widget_grab_focus(compose->subject_entry); +} + +static void subject_activated(GtkWidget *widget, Compose *compose) +{ + gtk_widget_grab_focus(compose->text); +} + +static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter, + const gchar *text, gint len, Compose *compose) +{ + GtkTextMark *mark; + + /* pass to the default handler */ + if (!compose->autowrap) + return; + + g_return_if_fail(text != NULL); + + g_signal_handlers_block_by_func(G_OBJECT(buffer), + G_CALLBACK(text_inserted), + compose); + + gtk_text_buffer_insert(buffer, iter, text, len); + + mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE); + compose_wrap_all_full(compose, TRUE); + gtk_text_buffer_get_iter_at_mark(buffer, iter, mark); + gtk_text_buffer_delete_mark(buffer, mark); + + g_signal_handlers_unblock_by_func(G_OBJECT(buffer), + G_CALLBACK(text_inserted), + compose); + g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text"); +} + +static gboolean autosave_timeout(gpointer data) +{ + Compose *compose = (Compose *)data; + + gdk_threads_enter(); + + debug_print("auto-saving...\n"); + + if (compose->modified) + compose_draft_cb(data, 1, NULL); + + gdk_threads_leave(); + + return TRUE; +} diff --git a/src/compose.h b/src/compose.h new file mode 100644 index 0000000..40282cb --- /dev/null +++ b/src/compose.h @@ -0,0 +1,267 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2017 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __COMPOSE_H__ +#define __COMPOSE_H__ + +#include +#include +#include +#include +#include +#include + +typedef struct _Compose Compose; +typedef struct _AttachInfo AttachInfo; + +#include "procmsg.h" +#include "procmime.h" +#include "folder.h" +#include "addressbook.h" +#include "prefs_account.h" +#include "undo.h" +#include "codeconv.h" + +typedef enum +{ + COMPOSE_ENTRY_TO, + COMPOSE_ENTRY_CC, + COMPOSE_ENTRY_BCC, + COMPOSE_ENTRY_REPLY_TO, + COMPOSE_ENTRY_SUBJECT, + COMPOSE_ENTRY_NEWSGROUPS, + COMPOSE_ENTRY_FOLLOWUP_TO +} ComposeEntryType; + +typedef enum +{ + COMPOSE_REPLY = 1, + COMPOSE_REPLY_TO_SENDER = 2, + COMPOSE_REPLY_TO_ALL = 3, + COMPOSE_REPLY_TO_LIST = 4, + COMPOSE_FORWARD = 5, + COMPOSE_FORWARD_AS_ATTACH = 6, + COMPOSE_NEW = 7, + COMPOSE_REDIRECT = 8, + COMPOSE_REEDIT = 9, + + COMPOSE_WITH_QUOTE = 1 << 16, + COMPOSE_WITHOUT_QUOTE = 2 << 16, + + COMPOSE_MODE_MASK = 0xffff, + COMPOSE_QUOTE_MODE_MASK = 0x30000 +} ComposeMode; + +#define COMPOSE_MODE(mode) ((mode) & COMPOSE_MODE_MASK) +#define COMPOSE_QUOTE_MODE(mode) ((mode) & COMPOSE_QUOTE_MODE_MASK) + +struct _Compose +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *menubar; + + GtkWidget *toolbar; + GtkWidget *send_btn; + GtkWidget *sendl_btn; + GtkWidget *draft_btn; + GtkWidget *insert_btn; + GtkWidget *attach_btn; + GtkWidget *sig_btn; + GtkWidget *exteditor_btn; + GtkWidget *linewrap_btn; + GtkWidget *addrbook_btn; + GtkWidget *prefs_common_btn; + GtkWidget *prefs_account_btn; + + GtkWidget *vbox2; + + GtkWidget *table_vbox; + GtkWidget *table; + GtkWidget *to_hbox; + GtkWidget *to_entry; + GtkWidget *newsgroups_hbox; + GtkWidget *newsgroups_entry; + GtkWidget *subject_entry; + GtkWidget *cc_hbox; + GtkWidget *cc_entry; + GtkWidget *bcc_hbox; + GtkWidget *bcc_entry; + GtkWidget *reply_hbox; + GtkWidget *reply_entry; + GtkWidget *followup_hbox; + GtkWidget *followup_entry; + + GtkWidget *misc_hbox; + GtkWidget *attach_toggle; + GtkWidget *signing_chkbtn; + GtkWidget *encrypt_chkbtn; + + GtkWidget *paned; + + GtkWidget *attach_scrwin; + GtkWidget *attach_treeview; + GtkListStore *attach_store; + + GtkWidget *edit_vbox; + GtkWidget *ruler_hbox; + GtkWidget *ruler; + GtkWidget *scrolledwin; + GtkWidget *text; + + GtkWidget *focused_editable; + + GtkWidget *popupmenu; + + GtkItemFactory *popupfactory; + + GtkWidget *tmpl_menu; + + /* GtkSpell */ + GtkWidget *spell_menu; + gchar *spell_lang; + gboolean check_spell; + GSList *dict_list; + + ComposeMode mode; + + MsgInfo *targetinfo; + gchar *reply_target; + gchar *forward_targets; + + gchar *replyto; + gchar *cc; + gchar *bcc; + gchar *newsgroups; + gchar *followup_to; + + gchar *ml_post; + + gchar *inreplyto; + gchar *references; + gchar *msgid; + gchar *boundary; + + gboolean autowrap; + + gboolean use_to; + gboolean use_cc; + gboolean use_bcc; + gboolean use_replyto; + gboolean use_newsgroups; + gboolean use_followupto; + gboolean use_attach; + + CharSet out_encoding; + + gboolean use_mdn; + + /* privacy settings */ + gboolean use_signing; + gboolean use_encryption; + + gboolean modified; + + GSList *to_list; + GSList *newsgroup_list; + + PrefsAccount *account; + + UndoMain *undostruct; + + GtkTextTag *sig_tag; + + /* external editor */ + gchar *exteditor_file; + GPid exteditor_pid; + guint exteditor_tag; + + guint autosave_tag; + + guint lock_count; + + gboolean window_maximized; + + gboolean block_modified; + + GtkTooltips *toolbar_tip; + + GtkWidget *sig_combo; +}; + +struct _AttachInfo +{ + gchar *file; + gchar *content_type; + EncodingType encoding; + gchar *name; + gsize size; +}; + +Compose *compose_new (PrefsAccount *account, + FolderItem *item, + const gchar *mailto, + GPtrArray *attach_files); + +Compose *compose_reply (MsgInfo *msginfo, + FolderItem *item, + ComposeMode mode, + const gchar *body); +Compose *compose_forward (GSList *mlist, + FolderItem *item, + gboolean as_attach, + const gchar *body); +Compose *compose_redirect (MsgInfo *msginfo, + FolderItem *item); +Compose *compose_reedit (MsgInfo *msginfo); + +GList *compose_get_compose_list (void); + +void compose_entry_set (Compose *compose, + const gchar *text, + ComposeEntryType type); +void compose_entry_append (Compose *compose, + const gchar *text, + ComposeEntryType type); +gchar *compose_entry_get_text (Compose *compose, + ComposeEntryType type); + +void compose_lock (Compose *compose); +void compose_unlock (Compose *compose); + +void compose_block_modified (Compose *compose); +void compose_unblock_modified (Compose *compose); + +void compose_reflect_prefs_all (void); + +GtkWidget *compose_get_toolbar (Compose *compose); +GtkWidget *compose_get_misc_hbox(Compose *compose); +GtkWidget *compose_get_textview (Compose *compose); + +void compose_attach_append (Compose *compose, + const gchar *file, + const gchar *filename, + const gchar *content_type); +void compose_attach_remove_all (Compose *compose); +GSList *compose_get_attach_list (Compose *compose); + +gint compose_send (Compose *compose, + gboolean close_on_success); + +#endif /* __COMPOSE_H__ */ diff --git a/src/editaddress.c b/src/editaddress.c new file mode 100644 index 0000000..a9f058d --- /dev/null +++ b/src/editaddress.c @@ -0,0 +1,1044 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2007 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mgutils.h" +#include "addressbook.h" +#include "addressitem.h" +#include "addritem.h" +#include "addrbook.h" +#include "manage_window.h" +#include "gtkutils.h" +#include "codeconv.h" + +#include "prefs_common.h" + +/* +static struct _AddressEdit_dlg { + GtkWidget *window; + GtkWidget *name_entry; + GtkWidget *addr_entry; + GtkWidget *rem_entry; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; +} addredit; +*/ + +static struct _PersonEdit_dlg { + GtkWidget *window; + GtkWidget *notebook; + GtkWidget *hbbox; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *statusbar; + gint status_cid; + + /* Basic data tab */ + GtkWidget *entry_name; + GtkWidget *entry_first; + GtkWidget *entry_last; + GtkWidget *entry_nick; + + /* EMail data tab */ + GtkWidget *entry_email; + GtkWidget *entry_alias; + GtkWidget *entry_remarks; + GtkWidget *clist_email; + + /* Attribute data tab */ + GtkWidget *entry_atname; + GtkWidget *entry_atvalue; + GtkWidget *clist_attrib; + + gint rowIndEMail; + gint rowIndAttrib; + gboolean editNew; + +} personeditdlg; + +typedef enum { + EMAIL_COL_EMAIL = 0, + EMAIL_COL_ALIAS = 1, + EMAIL_COL_REMARKS = 2 +} PersonEditEMailColumnPos; + +typedef enum { + ATTRIB_COL_NAME = 0, + ATTRIB_COL_VALUE = 1 +} PersonEditAttribColumnPos; + +#define EDITPERSON_WIDTH 520 +#define EDITPERSON_HEIGHT 340 + +#define EMAIL_N_COLS 3 +#define EMAIL_COL_WIDTH_EMAIL 180 +#define EMAIL_COL_WIDTH_ALIAS 80 + +#define ATTRIB_N_COLS 2 +#define ATTRIB_COL_WIDTH_NAME 120 +#define ATTRIB_COL_WIDTH_VALUE 180 + +#define PAGE_BASIC 0 +#define PAGE_EMAIL 1 +#define PAGE_ATTRIBUTES 2 + + +static void edit_person_status_show( gchar *msg ) { + if( personeditdlg.statusbar != NULL ) { + gtk_statusbar_pop( GTK_STATUSBAR(personeditdlg.statusbar), personeditdlg.status_cid ); + if( msg ) { + gtk_statusbar_push( GTK_STATUSBAR(personeditdlg.statusbar), personeditdlg.status_cid, msg ); + } + } +} + +static void edit_person_ok(GtkWidget *widget, gboolean *cancelled) { + *cancelled = FALSE; + gtk_main_quit(); +} + +static void edit_person_cancel(GtkWidget *widget, gboolean *cancelled) { + *cancelled = TRUE; + gtk_main_quit(); +} + +static gint edit_person_delete_event(GtkWidget *widget, GdkEventAny *event, gboolean *cancelled) { + *cancelled = TRUE; + gtk_main_quit(); + return TRUE; +} + +static gboolean edit_person_key_pressed(GtkWidget *widget, GdkEventKey *event, gboolean *cancelled) { + if (event && event->keyval == GDK_Escape) { + *cancelled = TRUE; + gtk_main_quit(); + } + return FALSE; +} + +static gchar *_title_new_ = NULL; +static gchar *_title_edit_ = NULL; + +static void edit_person_set_window_title( gint pageNum ) { + gchar *sTitle; + + if( _title_new_ == NULL ) { + _title_new_ = g_strdup( _("Add New Person") ); + _title_edit_ = g_strdup( _("Edit Person Details") ); + } + + if( pageNum == PAGE_BASIC ) { + if( personeditdlg.editNew ) { + gtk_window_set_title( GTK_WINDOW(personeditdlg.window), _title_new_ ); + } + else { + gtk_window_set_title( GTK_WINDOW(personeditdlg.window), _title_edit_ ); + } + } + else { + if( personeditdlg.entry_name == NULL ) { + sTitle = g_strdup( _title_edit_ ); + } + else { + gchar *name; + name = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_name), 0, -1 ); + sTitle = g_strdup_printf( "%s - %s", _title_edit_, name ); + g_free( name ); + } + gtk_window_set_title( GTK_WINDOW(personeditdlg.window), sTitle ); + g_free( sTitle ); + } +} + +static void edit_person_email_clear( gpointer data ) { + gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_email), "" ); + gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_alias), "" ); + gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_remarks), "" ); +} + +static void edit_person_attrib_clear( gpointer data ) { + gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atname), "" ); + gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atvalue), "" ); +} + +static void edit_person_switch_page( GtkNotebook *notebook, GtkNotebookPage *page, + gint pageNum, gpointer user_data) +{ + edit_person_set_window_title( pageNum ); + edit_person_status_show( "" ); +} + +/* +* Load clist with a copy of person's email addresses. +*/ +void edit_person_load_email( ItemPerson *person ) { + GList *node = person->listEMail; + GtkCList *clist = GTK_CLIST(personeditdlg.clist_email); + gchar *text[ EMAIL_N_COLS ]; + while( node ) { + ItemEMail *emorig = ( ItemEMail * ) node->data; + ItemEMail *email = addritem_copy_item_email( emorig ); + gint row; + text[ EMAIL_COL_EMAIL ] = email->address; + text[ EMAIL_COL_ALIAS ] = email->obj.name; + text[ EMAIL_COL_REMARKS ] = email->remarks; + + row = gtk_clist_append( clist, text ); + gtk_clist_set_row_data( clist, row, email ); + node = g_list_next( node ); + } +} + +static void edit_person_email_list_selected( GtkCList *clist, gint row, gint column, GdkEvent *event, gpointer data ) { + ItemEMail *email = gtk_clist_get_row_data( clist, row ); + if( email ) { + if( email->address ) + gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_email), email->address ); + if( ADDRITEM_NAME(email) ) + gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_alias), ADDRITEM_NAME(email) ); + if( email->remarks ) + gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_remarks), email->remarks ); + } + personeditdlg.rowIndEMail = row; + edit_person_status_show( NULL ); +} + +static void edit_person_email_move( gint dir ) { + GtkCList *clist = GTK_CLIST(personeditdlg.clist_email); + gint row = personeditdlg.rowIndEMail + dir; + ItemEMail *email = gtk_clist_get_row_data( clist, row ); + if( email ) { + gtk_clist_row_move( clist, personeditdlg.rowIndEMail, row ); + personeditdlg.rowIndEMail = row; + } + edit_person_email_clear( NULL ); + edit_person_status_show( NULL ); +} + +static void edit_person_email_move_up( gpointer data ) { + edit_person_email_move( -1 ); +} + +static void edit_person_email_move_down( gpointer data ) { + edit_person_email_move( +1 ); +} + +static void edit_person_email_delete( gpointer data ) { + GtkCList *clist = GTK_CLIST(personeditdlg.clist_email); + gint row = personeditdlg.rowIndEMail; + ItemEMail *email = gtk_clist_get_row_data( clist, row ); + edit_person_email_clear( NULL ); + if( email ) { + /* Remove list entry */ + gtk_clist_remove( clist, row ); + addritem_free_item_email( email ); + email = NULL; + } + + /* Position hilite bar */ + email = gtk_clist_get_row_data( clist, row ); + if( ! email ) { + personeditdlg.rowIndEMail = -1 + row; + } + edit_person_status_show( NULL ); +} + +static ItemEMail *edit_person_email_edit( gboolean *error, ItemEMail *email ) { + ItemEMail *retVal = NULL; + gchar *sEmail, *sAlias, *sRemarks, *sEmail_; + + *error = TRUE; + sEmail_ = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_email), 0, -1 ); + sAlias = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_alias), 0, -1 ); + sRemarks = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_remarks), 0, -1 ); + sEmail = mgu_email_check_empty( sEmail_ ); + g_free( sEmail_ ); + + if( sEmail ) { + if( email == NULL ) { + email = addritem_create_item_email(); + } + addritem_email_set_address( email, sEmail ); + addritem_email_set_alias( email, sAlias ); + addritem_email_set_remarks( email, sRemarks ); + retVal = email; + *error = FALSE; + } + else { + edit_person_status_show( _( "An E-Mail address must be supplied." ) ); + } + + g_free( sEmail ); + g_free( sAlias ); + g_free( sRemarks ); + + return retVal; +} + +static void edit_person_email_modify( gpointer data ) { + gboolean errFlg = FALSE; + GtkCList *clist = GTK_CLIST(personeditdlg.clist_email); + gint row = personeditdlg.rowIndEMail; + ItemEMail *email = gtk_clist_get_row_data( clist, row ); + if( email ) { + edit_person_email_edit( &errFlg, email ); + if( ! errFlg ) { + gtk_clist_set_text( clist, row, EMAIL_COL_EMAIL, email->address ); + gtk_clist_set_text( clist, row, EMAIL_COL_ALIAS, email->obj.name ); + gtk_clist_set_text( clist, row, EMAIL_COL_REMARKS, email->remarks ); + edit_person_email_clear( NULL ); + } + } +} + +static void edit_person_email_add( gpointer data ) { + GtkCList *clist = GTK_CLIST(personeditdlg.clist_email); + gboolean errFlg = FALSE; + ItemEMail *email = NULL; + gint row = personeditdlg.rowIndEMail; + if( gtk_clist_get_row_data( clist, row ) == NULL ) row = 0; + + email = edit_person_email_edit( &errFlg, NULL ); + if( ! errFlg ) { + gchar *text[ EMAIL_N_COLS ]; + text[ EMAIL_COL_EMAIL ] = email->address; + text[ EMAIL_COL_ALIAS ] = email->obj.name; + text[ EMAIL_COL_REMARKS ] = email->remarks; + + row = gtk_clist_insert( clist, 1 + row, text ); + gtk_clist_set_row_data( clist, row, email ); + gtk_clist_select_row( clist, row, 0 ); + edit_person_email_clear( NULL ); + } +} + +/* +* Load clist with a copy of person's email addresses. +*/ +void edit_person_load_attrib( ItemPerson *person ) { + GList *node = person->listAttrib; + GtkCList *clist = GTK_CLIST(personeditdlg.clist_attrib); + gchar *text[ ATTRIB_N_COLS ]; + while( node ) { + UserAttribute *atorig = ( UserAttribute * ) node->data; + UserAttribute *attrib = addritem_copy_attribute( atorig ); + gint row; + text[ ATTRIB_COL_NAME ] = attrib->name; + text[ ATTRIB_COL_VALUE ] = attrib->value; + + row = gtk_clist_append( clist, text ); + gtk_clist_set_row_data( clist, row, attrib ); + node = g_list_next( node ); + } +} + +static void edit_person_attrib_list_selected( GtkCList *clist, gint row, gint column, GdkEvent *event, gpointer data ) { + UserAttribute *attrib = gtk_clist_get_row_data( clist, row ); + if( attrib ) { + gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atname), attrib->name ); + gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atvalue), attrib->value ); + } + personeditdlg.rowIndAttrib = row; + edit_person_status_show( NULL ); +} + +static void edit_person_attrib_delete( gpointer data ) { + GtkCList *clist = GTK_CLIST(personeditdlg.clist_attrib); + gint row = personeditdlg.rowIndAttrib; + UserAttribute *attrib = gtk_clist_get_row_data( clist, row ); + edit_person_attrib_clear( NULL ); + if( attrib ) { + /* Remove list entry */ + gtk_clist_remove( clist, row ); + addritem_free_attribute( attrib ); + attrib = NULL; + } + + /* Position hilite bar */ + attrib = gtk_clist_get_row_data( clist, row ); + if( ! attrib ) { + personeditdlg.rowIndAttrib = -1 + row; + } + edit_person_status_show( NULL ); +} + +static UserAttribute *edit_person_attrib_edit( gboolean *error, UserAttribute *attrib ) { + UserAttribute *retVal = NULL; + gchar *sName, *sValue, *sName_, *sValue_; + + *error = TRUE; + sName_ = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_atname), 0, -1 ); + sValue_ = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_atvalue), 0, -1 ); + sName = mgu_email_check_empty( sName_ ); + sValue = mgu_email_check_empty( sValue_ ); + g_free( sName_ ); + g_free( sValue_ ); + + if( sName && sValue ) { + if( attrib == NULL ) { + attrib = addritem_create_attribute(); + } + addritem_attrib_set_name( attrib, sName ); + addritem_attrib_set_value( attrib, sValue ); + retVal = attrib; + *error = FALSE; + } + else { + edit_person_status_show( _( "A Name and Value must be supplied." ) ); + } + + g_free( sName ); + g_free( sValue ); + + return retVal; +} + +static void edit_person_attrib_modify( gpointer data ) { + gboolean errFlg = FALSE; + GtkCList *clist = GTK_CLIST(personeditdlg.clist_attrib); + gint row = personeditdlg.rowIndAttrib; + UserAttribute *attrib = gtk_clist_get_row_data( clist, row ); + if( attrib ) { + edit_person_attrib_edit( &errFlg, attrib ); + if( ! errFlg ) { + gtk_clist_set_text( clist, row, ATTRIB_COL_NAME, attrib->name ); + gtk_clist_set_text( clist, row, ATTRIB_COL_VALUE, attrib->value ); + edit_person_attrib_clear( NULL ); + } + } +} + +static void edit_person_attrib_add( gpointer data ) { + GtkCList *clist = GTK_CLIST(personeditdlg.clist_attrib); + gboolean errFlg = FALSE; + UserAttribute *attrib = NULL; + gint row = personeditdlg.rowIndAttrib; + if( gtk_clist_get_row_data( clist, row ) == NULL ) row = 0; + + attrib = edit_person_attrib_edit( &errFlg, NULL ); + if( ! errFlg ) { + gchar *text[ EMAIL_N_COLS ]; + text[ ATTRIB_COL_NAME ] = attrib->name; + text[ ATTRIB_COL_VALUE ] = attrib->value; + + row = gtk_clist_insert( clist, 1 + row, text ); + gtk_clist_set_row_data( clist, row, attrib ); + gtk_clist_select_row( clist, row, 0 ); + edit_person_attrib_clear( NULL ); + } +} + +static void addressbook_edit_person_dialog_create( gboolean *cancelled ) { + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *vnbox; + GtkWidget *notebook; + GtkWidget *hbbox; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *hsbox; + GtkWidget *statusbar; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request(window, EDITPERSON_WIDTH, EDITPERSON_HEIGHT ); + gtk_window_set_title(GTK_WINDOW(window), _("Edit Person Data")); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_widget_realize(window); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(edit_person_delete_event), + cancelled); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(edit_person_key_pressed), + cancelled); + + vbox = gtk_vbox_new(FALSE, 4); + gtk_widget_show(vbox); + gtk_container_add(GTK_CONTAINER(window), vbox); + + vnbox = gtk_vbox_new(FALSE, 4); + gtk_container_set_border_width(GTK_CONTAINER(vnbox), 4); + gtk_widget_show(vnbox); + gtk_box_pack_start(GTK_BOX(vbox), vnbox, TRUE, TRUE, 0); + + /* Notebook */ + notebook = gtk_notebook_new(); + gtk_widget_show(notebook); + gtk_box_pack_start(GTK_BOX(vnbox), notebook, TRUE, TRUE, 0); + gtk_container_set_border_width(GTK_CONTAINER(notebook), 6); + + /* Status line */ + hsbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, 0); + statusbar = gtk_statusbar_new(); + gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, 0); + + /* Button panel */ + gtkut_stock_button_set_create(&hbbox, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(vnbox), hbbox, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_btn); + + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(edit_person_ok), cancelled); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(edit_person_cancel), cancelled); + g_signal_connect(G_OBJECT(notebook), "switch_page", + G_CALLBACK(edit_person_switch_page), NULL); + + gtk_widget_show_all(vbox); + + personeditdlg.window = window; + personeditdlg.notebook = notebook; + personeditdlg.hbbox = hbbox; + personeditdlg.ok_btn = ok_btn; + personeditdlg.cancel_btn = cancel_btn; + personeditdlg.statusbar = statusbar; + personeditdlg.status_cid = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "Edit Person Dialog" ); + +} + +void addressbook_edit_person_page_basic( gint pageNum, gchar *pageLbl ) { + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *label; + GtkWidget *entry_name; + GtkWidget *entry_fn; + GtkWidget *entry_ln; + GtkWidget *entry_nn; + const gchar *locale; + gint top = 0; + + vbox = gtk_vbox_new( FALSE, 8 ); + gtk_widget_show( vbox ); + gtk_container_add( GTK_CONTAINER( personeditdlg.notebook ), vbox ); + gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH ); + + label = gtk_label_new( pageLbl ); + gtk_widget_show( label ); + gtk_notebook_set_tab_label( + GTK_NOTEBOOK( personeditdlg.notebook ), + gtk_notebook_get_nth_page( GTK_NOTEBOOK( personeditdlg.notebook ), pageNum ), label ); + + table = gtk_table_new( 4, 3, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 8 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + +#define ATTACH_ROW(text, entry) \ +{ \ + label = gtk_label_new(text); \ + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \ + GTK_FILL, 0, 0, 0); \ + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \ + \ + entry = gtk_entry_new(); \ + gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \ + GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \ + top++; \ +} + + ATTACH_ROW(_("Display Name"), entry_name); + locale = conv_get_current_locale(); + if (locale && + (!g_ascii_strncasecmp(locale, "ja", 2) || + !g_ascii_strncasecmp(locale, "ko", 2) || + !g_ascii_strncasecmp(locale, "zh", 2))) { + ATTACH_ROW(_("Last Name"), entry_ln); + ATTACH_ROW(_("First Name"), entry_fn); + } else { + ATTACH_ROW(_("First Name"), entry_fn); + ATTACH_ROW(_("Last Name"), entry_ln); + } + ATTACH_ROW(_("Nick Name"), entry_nn); + +#undef ATTACH_ROW + + gtk_widget_show_all(vbox); + + personeditdlg.entry_name = entry_name; + personeditdlg.entry_first = entry_fn; + personeditdlg.entry_last = entry_ln; + personeditdlg.entry_nick = entry_nn; +} + +void addressbook_edit_person_page_email( gint pageNum, gchar *pageLbl ) { + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *vboxl; + GtkWidget *vboxb; + GtkWidget *vbuttonbox; + GtkWidget *buttonUp; + GtkWidget *buttonDown; + GtkWidget *buttonDel; + GtkWidget *buttonMod; + GtkWidget *buttonAdd; + GtkWidget *buttonClr; + + GtkWidget *table; + GtkWidget *label; + GtkWidget *clist_swin; + GtkWidget *clist; + GtkWidget *entry_email; + GtkWidget *entry_alias; + GtkWidget *entry_remarks; + gint top; + + gchar *titles[ EMAIL_N_COLS ]; + gint i; + + titles[ EMAIL_COL_EMAIL ] = _("E-Mail Address"); + titles[ EMAIL_COL_ALIAS ] = _("Alias"); + titles[ EMAIL_COL_REMARKS ] = _("Remarks"); + + vbox = gtk_vbox_new( FALSE, 8 ); + gtk_widget_show( vbox ); + gtk_container_add( GTK_CONTAINER( personeditdlg.notebook ), vbox ); + gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH ); + + label = gtk_label_new( pageLbl ); + gtk_widget_show( label ); + gtk_notebook_set_tab_label( + GTK_NOTEBOOK( personeditdlg.notebook ), + gtk_notebook_get_nth_page( GTK_NOTEBOOK( personeditdlg.notebook ), pageNum ), label ); + + /* Split into two areas */ + hbox = gtk_hbox_new( FALSE, 0 ); + gtk_container_add( GTK_CONTAINER( vbox ), hbox ); + + /* EMail list */ + vboxl = gtk_vbox_new( FALSE, 4 ); + gtk_container_add( GTK_CONTAINER( hbox ), vboxl ); + gtk_container_set_border_width( GTK_CONTAINER(vboxl), 4 ); + + /* Address list */ + clist_swin = gtk_scrolled_window_new( NULL, NULL ); + gtk_container_add( GTK_CONTAINER(vboxl), clist_swin ); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + + clist = gtk_clist_new_with_titles( EMAIL_N_COLS, titles ); + gtk_container_add( GTK_CONTAINER(clist_swin), clist ); + gtk_clist_set_selection_mode( GTK_CLIST(clist), GTK_SELECTION_BROWSE ); + gtk_clist_set_column_width( GTK_CLIST(clist), EMAIL_COL_EMAIL, EMAIL_COL_WIDTH_EMAIL ); + gtk_clist_set_column_width( GTK_CLIST(clist), EMAIL_COL_ALIAS, EMAIL_COL_WIDTH_ALIAS ); + gtkut_clist_set_redraw( GTK_CLIST(clist) ); + + for( i = 0; i < EMAIL_N_COLS; i++ ) + GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist)->column[i].button, GTK_CAN_FOCUS); + + /* Data entry area */ + table = gtk_table_new( 4, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vboxl), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 4 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 4); + gtk_table_set_col_spacings(GTK_TABLE(table), 4); + + /* First row */ + top = 0; + label = gtk_label_new(_("E-Mail Address")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + entry_email = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), entry_email, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* Next row */ + ++top; + label = gtk_label_new(_("Alias")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + entry_alias = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), entry_alias, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* Next row */ + ++top; + label = gtk_label_new(_("Remarks")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + entry_remarks = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), entry_remarks, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* Button box */ + vboxb = gtk_vbox_new( FALSE, 4 ); + gtk_box_pack_start(GTK_BOX(hbox), vboxb, FALSE, FALSE, 2); + + vbuttonbox = gtk_vbutton_box_new(); + gtk_button_box_set_layout( GTK_BUTTON_BOX(vbuttonbox), GTK_BUTTONBOX_START ); + gtk_box_set_spacing( GTK_BOX(vbuttonbox), 8 ); + gtk_container_set_border_width( GTK_CONTAINER(vbuttonbox), 4 ); + gtk_container_add( GTK_CONTAINER(vboxb), vbuttonbox ); + + /* Buttons */ + buttonUp = gtk_button_new_with_label( _( "Move Up" ) ); + gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonUp ); + + buttonDown = gtk_button_new_with_label( _( "Move Down" ) ); + gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonDown ); + + buttonDel = gtk_button_new_with_label( _( "Delete" ) ); + gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonDel ); + + buttonMod = gtk_button_new_with_label( _( "Modify" ) ); + gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonMod ); + + buttonAdd = gtk_button_new_with_label( _( "Add" ) ); + gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonAdd ); + + buttonClr = gtk_button_new_with_label( _( "Clear" ) ); + gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonClr ); + + gtk_widget_show_all(vbox); + + /* Event handlers */ + g_signal_connect( G_OBJECT(clist), "select_row", + G_CALLBACK( edit_person_email_list_selected), NULL ); + g_signal_connect( G_OBJECT(buttonUp), "clicked", + G_CALLBACK( edit_person_email_move_up ), NULL ); + g_signal_connect( G_OBJECT(buttonDown), "clicked", + G_CALLBACK( edit_person_email_move_down ), NULL ); + g_signal_connect( G_OBJECT(buttonDel), "clicked", + G_CALLBACK( edit_person_email_delete ), NULL ); + g_signal_connect( G_OBJECT(buttonMod), "clicked", + G_CALLBACK( edit_person_email_modify ), NULL ); + g_signal_connect( G_OBJECT(buttonAdd), "clicked", + G_CALLBACK( edit_person_email_add ), NULL ); + g_signal_connect( G_OBJECT(buttonClr), "clicked", + G_CALLBACK( edit_person_email_clear ), NULL ); + + personeditdlg.clist_email = clist; + personeditdlg.entry_email = entry_email; + personeditdlg.entry_alias = entry_alias; + personeditdlg.entry_remarks = entry_remarks; +} + +void addressbook_edit_person_page_attrib( gint pageNum, gchar *pageLbl ) { + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *vboxl; + GtkWidget *vboxb; + GtkWidget *vbuttonbox; + GtkWidget *buttonDel; + GtkWidget *buttonMod; + GtkWidget *buttonAdd; + GtkWidget *buttonClr; + + GtkWidget *table; + GtkWidget *label; + GtkWidget *clist_swin; + GtkWidget *clist; + GtkWidget *entry_name; + GtkWidget *entry_value; + gint top; + + gchar *titles[ ATTRIB_N_COLS ]; + gint i; + + titles[ ATTRIB_COL_NAME ] = _("Name"); + titles[ ATTRIB_COL_VALUE ] = _("Value"); + + vbox = gtk_vbox_new( FALSE, 8 ); + gtk_widget_show( vbox ); + gtk_container_add( GTK_CONTAINER( personeditdlg.notebook ), vbox ); + gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH ); + + label = gtk_label_new( pageLbl ); + gtk_widget_show( label ); + gtk_notebook_set_tab_label( + GTK_NOTEBOOK( personeditdlg.notebook ), + gtk_notebook_get_nth_page( GTK_NOTEBOOK( personeditdlg.notebook ), pageNum ), label ); + + /* Split into two areas */ + hbox = gtk_hbox_new( FALSE, 0 ); + gtk_container_add( GTK_CONTAINER( vbox ), hbox ); + + /* Attribute list */ + vboxl = gtk_vbox_new( FALSE, 4 ); + gtk_container_add( GTK_CONTAINER( hbox ), vboxl ); + gtk_container_set_border_width( GTK_CONTAINER(vboxl), 4 ); + + /* Address list */ + clist_swin = gtk_scrolled_window_new( NULL, NULL ); + gtk_container_add( GTK_CONTAINER(vboxl), clist_swin ); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + + clist = gtk_clist_new_with_titles( ATTRIB_N_COLS, titles ); + gtk_container_add( GTK_CONTAINER(clist_swin), clist ); + gtk_clist_set_selection_mode( GTK_CLIST(clist), GTK_SELECTION_BROWSE ); + gtk_clist_set_column_width( GTK_CLIST(clist), ATTRIB_COL_NAME, ATTRIB_COL_WIDTH_NAME ); + gtk_clist_set_column_width( GTK_CLIST(clist), ATTRIB_COL_VALUE, ATTRIB_COL_WIDTH_VALUE ); + gtkut_clist_set_redraw( GTK_CLIST(clist) ); + + for( i = 0; i < ATTRIB_N_COLS; i++ ) + GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist)->column[i].button, GTK_CAN_FOCUS); + + /* Data entry area */ + table = gtk_table_new( 4, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vboxl), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 4 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 4); + gtk_table_set_col_spacings(GTK_TABLE(table), 4); + + /* First row */ + top = 0; + label = gtk_label_new(_("Name")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + entry_name = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), entry_name, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* Next row */ + ++top; + label = gtk_label_new(_("Value")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + entry_value = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), entry_value, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* Button box */ + vboxb = gtk_vbox_new( FALSE, 4 ); + gtk_box_pack_start(GTK_BOX(hbox), vboxb, FALSE, FALSE, 2); + + vbuttonbox = gtk_vbutton_box_new(); + gtk_button_box_set_layout( GTK_BUTTON_BOX(vbuttonbox), GTK_BUTTONBOX_START ); + gtk_box_set_spacing( GTK_BOX(vbuttonbox), 8 ); + gtk_container_set_border_width( GTK_CONTAINER(vbuttonbox), 4 ); + gtk_container_add( GTK_CONTAINER(vboxb), vbuttonbox ); + + /* Buttons */ + buttonDel = gtk_button_new_with_label( _( "Delete" ) ); + gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonDel ); + + buttonMod = gtk_button_new_with_label( _( "Modify" ) ); + gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonMod ); + + buttonAdd = gtk_button_new_with_label( _( "Add" ) ); + gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonAdd ); + + buttonClr = gtk_button_new_with_label( _( "Clear" ) ); + gtk_container_add( GTK_CONTAINER(vbuttonbox), buttonClr ); + + gtk_widget_show_all(vbox); + + /* Event handlers */ + g_signal_connect( G_OBJECT(clist), "select_row", + G_CALLBACK( edit_person_attrib_list_selected), NULL ); + g_signal_connect( G_OBJECT(buttonDel), "clicked", + G_CALLBACK( edit_person_attrib_delete ), NULL ); + g_signal_connect( G_OBJECT(buttonMod), "clicked", + G_CALLBACK( edit_person_attrib_modify ), NULL ); + g_signal_connect( G_OBJECT(buttonAdd), "clicked", + G_CALLBACK( edit_person_attrib_add ), NULL ); + g_signal_connect( G_OBJECT(buttonClr), "clicked", + G_CALLBACK( edit_person_attrib_clear ), NULL ); + + personeditdlg.clist_attrib = clist; + personeditdlg.entry_atname = entry_name; + personeditdlg.entry_atvalue = entry_value; +} + +static void addressbook_edit_person_create( gboolean *cancelled ) { + addressbook_edit_person_dialog_create( cancelled ); + addressbook_edit_person_page_basic( PAGE_BASIC, _( "Basic Data" ) ); + addressbook_edit_person_page_email( PAGE_EMAIL, _( "E-Mail Address" ) ); + addressbook_edit_person_page_attrib( PAGE_ATTRIBUTES, _( "User Attributes" ) ); + //gtk_widget_show_all( personeditdlg.window ); +} + +/* +* Return list of email items. +*/ +static GList *edit_person_build_email_list() { + GtkCList *clist = GTK_CLIST(personeditdlg.clist_email); + GList *listEMail = NULL; + ItemEMail *email; + gint row = 0; + while( (email = gtk_clist_get_row_data( clist, row )) ) { + listEMail = g_list_append( listEMail, email ); + row++; + } + return listEMail; +} + +/* +* Return list of attributes. +*/ +static GList *edit_person_build_attrib_list() { + GtkCList *clist = GTK_CLIST(personeditdlg.clist_attrib); + GList *listAttrib = NULL; + UserAttribute *attrib; + gint row = 0; + while( (attrib = gtk_clist_get_row_data( clist, row )) ) { + listAttrib = g_list_append( listAttrib, attrib ); + row++; + } + return listAttrib; +} + +/* +* Edit person. +* Enter: abf Address book. +* parent Parent folder for person (or NULL if adding to root folder). Argument is +* only required for new objects). +* person Person to edit, or NULL for a new person object. +* pgMail If TRUE, E-Mail page will be activated. +* Return: Edited object, or NULL if cancelled. +*/ +ItemPerson *addressbook_edit_person( AddressBookFile *abf, ItemFolder *parent, ItemPerson *person, gboolean pgMail ) { + static gboolean cancelled; + GList *listEMail = NULL; + GList *listAttrib = NULL; + gchar *cn = NULL; + + if (!personeditdlg.window) + addressbook_edit_person_create(&cancelled); + gtkut_box_set_reverse_order(GTK_BOX(personeditdlg.hbbox), + !prefs_common.comply_gnome_hig); + gtk_widget_grab_focus(personeditdlg.ok_btn); + gtk_widget_grab_focus(personeditdlg.entry_name); + manage_window_set_transient(GTK_WINDOW(personeditdlg.window)); + gtk_widget_show(personeditdlg.window); + + /* Clear all fields */ + personeditdlg.rowIndEMail = -1; + personeditdlg.rowIndAttrib = -1; + edit_person_status_show( "" ); + gtk_clist_clear( GTK_CLIST(personeditdlg.clist_email) ); + gtk_clist_clear( GTK_CLIST(personeditdlg.clist_attrib) ); + gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_name), "" ); + gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_first), "" ); + gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_last), "" ); + gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_nick), "" ); + + personeditdlg.editNew = FALSE; + if( person ) { + if( ADDRITEM_NAME(person) ) + gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_name), ADDRITEM_NAME(person) ); + if( person->firstName ) + gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_first), person->firstName ); + if( person->lastName ) + gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_last), person->lastName ); + if( person->nickName ) + gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_nick), person->nickName ); + edit_person_load_email( person ); + edit_person_load_attrib( person ); + } + else { + personeditdlg.editNew = TRUE; + } + + /* Select appropriate start page */ + if( pgMail ) { + gtk_notebook_set_current_page( GTK_NOTEBOOK(personeditdlg.notebook), PAGE_EMAIL ); + } + else { + gtk_notebook_set_current_page( GTK_NOTEBOOK(personeditdlg.notebook), PAGE_BASIC ); + } + + gtk_clist_select_row( GTK_CLIST(personeditdlg.clist_email), 0, 0 ); + gtk_clist_select_row( GTK_CLIST(personeditdlg.clist_attrib), 0, 0 ); + edit_person_email_clear( NULL ); + edit_person_attrib_clear( NULL ); + + gtk_main(); + gtk_widget_hide( personeditdlg.window ); + + listEMail = edit_person_build_email_list(); + listAttrib = edit_person_build_attrib_list(); + if( cancelled ) { + addritem_free_list_email( listEMail ); + gtk_clist_clear( GTK_CLIST(personeditdlg.clist_email) ); + gtk_clist_clear( GTK_CLIST(personeditdlg.clist_attrib) ); + return NULL; + } + + cn = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_name), 0, -1 ); + if( person ) { + /* Update email/attribute list */ + addrbook_update_address_list( abf, person, listEMail ); + addrbook_update_attrib_list( abf, person, listAttrib ); + } + else { + /* Create new person and email/attribute list */ + if( cn == NULL || *cn == '\0' ) { + /* Wasting our time */ + if( listEMail == NULL && listAttrib == NULL ) cancelled = TRUE; + } + if( ! cancelled ) { + person = addrbook_add_address_list( abf, parent, listEMail ); + addrbook_add_attrib_list( abf, person, listAttrib ); + } + } + + if( !cancelled ) { + /* Set person stuff */ + gchar *name; + addritem_person_set_common_name( person, cn ); + name = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_first), 0, -1 ); + addritem_person_set_first_name( person, name ); + g_free( name ); + name = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_last), 0, -1 ); + addritem_person_set_last_name( person, name ); + g_free( name ); + name = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_nick), 0, -1 ); + addritem_person_set_nick_name( person, name ); + g_free( name ); + } + g_free( cn ); + + listEMail = NULL; + + gtk_clist_clear( GTK_CLIST(personeditdlg.clist_email) ); + gtk_clist_clear( GTK_CLIST(personeditdlg.clist_attrib) ); + + return person; +} + +/* +* End of Source. +*/ + diff --git a/src/editaddress.h b/src/editaddress.h new file mode 100644 index 0000000..0d8d38d --- /dev/null +++ b/src/editaddress.h @@ -0,0 +1,29 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999,2000 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Edit address item data. + */ + +#ifndef __EDITADDRESS_H__ +#define __EDITADDRESS_H__ + +ItemPerson *addressbook_edit_person( AddressBookFile *abf, ItemFolder *parent, ItemPerson *person, gboolean pgMail ); + +#endif /* __EDITADDRESS_H__ */ diff --git a/src/editbook.c b/src/editbook.c new file mode 100644 index 0000000..4e42e43 --- /dev/null +++ b/src/editbook.c @@ -0,0 +1,360 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Edit new address book entry. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "prefs_common.h" +#include "mgutils.h" +#include "addressbook.h" +#include "addressitem.h" +#include "addrindex.h" +#include "addrbook.h" +#include "manage_window.h" +#include "gtkutils.h" + +#define ADDRESSBOOK_GUESS_BOOK "MyAddressBook" + +static struct _AddrBookEdit_Dlg { + GtkWidget *window; + GtkWidget *name_entry; + GtkWidget *file_label; + GtkWidget *hbbox; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *check_btn; + /* GtkWidget *file_btn; */ + GtkWidget *statusbar; + gint status_cid; + AddressBookFile *bookFile; +} addrbookedit_dlg; + +/* static struct _AddressFileSelection vcard_file_selector; */ + +/* +* Edit functions. +*/ +void edit_book_status_show( gchar *msg ) { + if( addrbookedit_dlg.statusbar != NULL ) { + gtk_statusbar_pop( GTK_STATUSBAR(addrbookedit_dlg.statusbar), addrbookedit_dlg.status_cid ); + if( msg ) { + gtk_statusbar_push( + GTK_STATUSBAR(addrbookedit_dlg.statusbar), addrbookedit_dlg.status_cid, msg ); + } + else { + gtk_statusbar_push( + GTK_STATUSBAR(addrbookedit_dlg.statusbar), addrbookedit_dlg.status_cid, "" ); + } + } +} + +static void edit_book_ok( GtkWidget *widget, gboolean *cancelled ) { + *cancelled = FALSE; + gtk_main_quit(); +} + +static void edit_book_cancel( GtkWidget *widget, gboolean *cancelled ) { + *cancelled = TRUE; + gtk_main_quit(); +} + +static gint edit_book_delete_event( GtkWidget *widget, GdkEventAny *event, gboolean *cancelled ) { + *cancelled = TRUE; + gtk_main_quit(); + return TRUE; +} + +static gboolean edit_book_key_pressed( GtkWidget *widget, GdkEventKey *event, gboolean *cancelled ) { + if (event && event->keyval == GDK_Escape) { + *cancelled = TRUE; + gtk_main_quit(); + } + return FALSE; +} + +static void edit_book_file_check( void ) { + gint t; + gchar *sMsg; + AddressBookFile *abf = addrbookedit_dlg.bookFile; + + t = addrbook_test_read_file( abf, abf->fileName ); + if( t == MGU_SUCCESS ) { + sMsg = _("File appears to be Ok."); + } + else if( t == MGU_BAD_FORMAT ) { + sMsg = _("File does not appear to be a valid address book format."); + } + else { + sMsg = _("Could not read file."); + } + edit_book_status_show( sMsg ); +} + +static void edit_book_enable_buttons( gboolean enable ) { + gtk_widget_set_sensitive( addrbookedit_dlg.check_btn, enable ); + /* gtk_widget_set_sensitive( addrbookedit_dlg.file_btn, enable ); */ +} + +#if 0 +static void edit_book_name_focus( GtkWidget *widget, GdkEventFocus *event, gpointer data) { + edit_book_status_show( "" ); +} +#endif + +static gchar *edit_book_guess_file( AddressBookFile *abf ) { + gchar *newFile = NULL; + GList *fileList = NULL; + gint fileNum = 1; + fileList = addrbook_get_bookfile_list( abf ); + if( fileList ) { + fileNum = 1 + abf->maxValue; + } + newFile = addrbook_gen_new_file_name( fileNum ); + g_list_free( fileList ); + fileList = NULL; + return newFile; +} + +static void addressbook_edit_book_create( gboolean *cancelled ) { + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *label; + GtkWidget *name_entry; + GtkWidget *file_label; + GtkWidget *hbbox; + GtkWidget *hsep; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *check_btn; + /* GtkWidget *file_btn; */ + GtkWidget *statusbar; + GtkWidget *hsbox; + gint top; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request(window, 450, -1); + gtk_container_set_border_width( GTK_CONTAINER(window), 0 ); + gtk_window_set_title(GTK_WINDOW(window), _("Edit Address Book")); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(edit_book_delete_event), + cancelled); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(edit_book_key_pressed), + cancelled); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add(GTK_CONTAINER(window), vbox); + gtk_container_set_border_width( GTK_CONTAINER(vbox), 0 ); + + table = gtk_table_new(2, 3, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 8 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8 ); + + /* First row */ + top = 0; + label = gtk_label_new(_("Name")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + name_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), name_entry, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + check_btn = gtk_button_new_with_label( _(" Check File ")); + gtk_table_attach(GTK_TABLE(table), check_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0); + + /* Second row */ + top = 1; + label = gtk_label_new(_("File")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + file_label = gtk_label_new( "" ); + gtk_misc_set_alignment(GTK_MISC(file_label), 0, 0.5); + gtk_table_attach(GTK_TABLE(table), file_label, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* file_btn = gtk_button_new_with_label( _(" ... ")); */ + /* gtk_table_attach(GTK_TABLE(table), file_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0); */ + + /* Status line */ + hsbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, 0); + statusbar = gtk_statusbar_new(); + gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, 0); + + /* Button panel */ + gtkut_stock_button_set_create(&hbbox, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(hbbox), 0 ); + gtk_widget_grab_default(ok_btn); + + hsep = gtk_hseparator_new(); + gtk_box_pack_end(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); + + /* g_signal_connect(G_OBJECT(name_entry), "focus_in_event", + G_CALLBACK(edit_book_name_focus), NULL ); */ + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(edit_book_ok), cancelled); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(edit_book_cancel), cancelled); +/* g_signal_connect(G_OBJECT(file_btn), "clicked", */ +/* G_CALLBACK(edit_book_file_select), NULL); */ + g_signal_connect(G_OBJECT(check_btn), "clicked", + G_CALLBACK(edit_book_file_check), NULL); + + gtk_widget_show_all(vbox); + + addrbookedit_dlg.window = window; + addrbookedit_dlg.name_entry = name_entry; + addrbookedit_dlg.file_label = file_label; + addrbookedit_dlg.hbbox = hbbox; + addrbookedit_dlg.ok_btn = ok_btn; + addrbookedit_dlg.cancel_btn = cancel_btn; + addrbookedit_dlg.check_btn = check_btn; + /* addrbookedit_dlg.file_btn = file_btn; */ + addrbookedit_dlg.statusbar = statusbar; + addrbookedit_dlg.status_cid = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "Edit Address Book Dialog" ); +} + +AdapterDSource *addressbook_edit_book( AddressIndex *addrIndex, AdapterDSource *ads ) { + static gboolean cancelled; + gchar *sName; + AddressDataSource *ds = NULL; + AddressBookFile *abf; + gboolean fin; + gboolean newBook = FALSE; + gchar *newFile = NULL; + + if (!addrbookedit_dlg.window) + addressbook_edit_book_create(&cancelled); + gtkut_box_set_reverse_order(GTK_BOX(addrbookedit_dlg.hbbox), + !prefs_common.comply_gnome_hig); + gtk_widget_grab_focus(addrbookedit_dlg.ok_btn); + gtk_widget_grab_focus(addrbookedit_dlg.name_entry); + gtk_widget_show(addrbookedit_dlg.window); + manage_window_set_transient(GTK_WINDOW(addrbookedit_dlg.window)); + + edit_book_status_show( "" ); + gtk_label_set_text( GTK_LABEL(addrbookedit_dlg.file_label), "" ); + if( ads ) { + ds = ads->dataSource; + abf = ds->rawDataSource; + if (abf->name) + gtk_entry_set_text(GTK_ENTRY(addrbookedit_dlg.name_entry), abf->name); + if( abf->fileName ) + gtk_label_set_text(GTK_LABEL(addrbookedit_dlg.file_label), abf->fileName); + gtk_window_set_title( GTK_WINDOW(addrbookedit_dlg.window), _("Edit Address Book")); + edit_book_enable_buttons( TRUE ); + } + else { + gchar *tmp = NULL; + newBook = TRUE; + abf = addrbook_create_book(); + addrbook_set_path( abf, addrIndex->filePath ); + + /* Take initial guess at file name */ + newFile = edit_book_guess_file( abf ); + if( newFile ) { + tmp = g_strdup_printf( "<%s>", newFile ); + gtk_label_set_text(GTK_LABEL(addrbookedit_dlg.file_label), tmp ); + g_free( tmp ); + } + g_free( newFile ); + + gtk_entry_set_text( GTK_ENTRY(addrbookedit_dlg.name_entry), ADDRESSBOOK_GUESS_BOOK ); + gtk_window_set_title( GTK_WINDOW(addrbookedit_dlg.window), _("Add New Address Book") ); + edit_book_enable_buttons( FALSE ); + } + + addrbookedit_dlg.bookFile = abf; + + gtk_main(); + gtk_widget_hide(addrbookedit_dlg.window); + + if( cancelled == TRUE ) { + if( newBook ) { + addrbook_free_book( abf ); + abf = NULL; + } + return NULL; + } + + fin = FALSE; + sName = gtk_editable_get_chars( GTK_EDITABLE(addrbookedit_dlg.name_entry), 0, -1 ); + g_strstrip(sName); + if( *sName == '\0' || *sName == '@' ) fin = TRUE; + + if( fin ) { + if( newBook ) { + addrbook_free_book( abf ); + abf = NULL; + } + } + else { + if( newBook ) { + /* Get final file name in case it changed */ + newFile = edit_book_guess_file( abf ); + addrbook_set_file( abf, newFile ); + g_free( newFile ); + ds = addrindex_index_add_datasource( addrIndex, ADDR_IF_BOOK, abf ); + ads = addressbook_create_ds_adapter( ds, ADDR_BOOK, NULL ); + } + addressbook_ads_set_name( ads, sName ); + addrbook_set_name( abf, sName ); + abf->dirtyFlag = TRUE; + } + g_free( sName ); + + /* Save data */ + if( abf ) addrbook_save_data( abf ); + + return ads; +} + +/* +* End of Source. +*/ diff --git a/src/editbook.h b/src/editbook.h new file mode 100644 index 0000000..13e4965 --- /dev/null +++ b/src/editbook.h @@ -0,0 +1,29 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Edit new address book data. + */ + +#ifndef __EDITBOOK_H__ +#define __EDITBOOK_H__ + +AdapterDSource *addressbook_edit_book( AddressIndex *addrIndex, AdapterDSource *ads ); + +#endif /* __EDITBOOK_H__ */ diff --git a/src/editgroup.c b/src/editgroup.c new file mode 100644 index 0000000..21fcfcd --- /dev/null +++ b/src/editgroup.c @@ -0,0 +1,902 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2017 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include + +#include "addressbook.h" +#include "addressitem.h" +#include "addrbook.h" +#include "addritem.h" + +#include "mgutils.h" + +#include "prefs_common.h" + +#include "alertpanel.h" +#include "inputdialog.h" +#include "manage_window.h" +#include "gtkutils.h" +#include "stock_pixmap.h" + +#define ADDRESSBOOK_GUESS_FOLDER_NAME "NewFolder" +#define ADDRESSBOOK_GUESS_GROUP_NAME "NewGroup" + +#define EDITGROUP_WIDTH 680 +#define EDITGROUP_HEIGHT 380 + +typedef enum { + GROUP_COL_NAME, + GROUP_COL_EMAIL, + GROUP_COL_REMARKS, + GROUP_COL_DATA, + GROUP_COL_PIXBUF, + GROUP_COL_PIXBUF_OPEN, + GROUP_N_COLS +} GroupEditEMailColumns; + +#define GROUP_COL_WIDTH_NAME 160 +#define GROUP_COL_WIDTH_EMAIL 140 +#define GROUP_COL_WIDTH_REMARKS 80 + +static struct _GroupEdit_dlg { + GtkWidget *window; + GtkWidget *hbbox; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *statusbar; + gint status_cid; + + /* Basic data tab */ + GtkWidget *entry_name; + GtkTreeView *treeview_avail; + GtkTreeView *treeview_group; +} groupeditdlg; + + +static GdkPixbuf *folderpix; +static GdkPixbuf *folderopenpix; + +static gchar *_edit_group_dfl_message_ = NULL; + +static void edit_group_status_show( gchar *msg ) { + if( groupeditdlg.statusbar != NULL ) { + gtk_statusbar_pop( GTK_STATUSBAR(groupeditdlg.statusbar), groupeditdlg.status_cid ); + if( msg ) { + gtk_statusbar_push( GTK_STATUSBAR(groupeditdlg.statusbar), groupeditdlg.status_cid, msg ); + } + } +} + +static void edit_group_ok(GtkWidget *widget, gboolean *cancelled) { + gchar *sName; + gboolean errFlag = TRUE; + + sName = gtk_editable_get_chars( GTK_EDITABLE(groupeditdlg.entry_name), 0, -1 ); + if( sName ) { + g_strstrip( sName ); + if( *sName != '\0' ) { + gtk_entry_set_text(GTK_ENTRY(groupeditdlg.entry_name), sName ); + *cancelled = FALSE; + gtk_main_quit(); + errFlag = FALSE; + } + } + if( errFlag ) { + edit_group_status_show( _( "A Group Name must be supplied." ) ); + } + g_free( sName ); +} + +static void edit_group_cancel(GtkWidget *widget, gboolean *cancelled) { + *cancelled = TRUE; + gtk_main_quit(); +} + +static gint edit_group_delete_event(GtkWidget *widget, GdkEventAny *event, gboolean *cancelled) { + *cancelled = TRUE; + gtk_main_quit(); + return TRUE; +} + +static gboolean edit_group_key_pressed(GtkWidget *widget, GdkEventKey *event, gboolean *cancelled) { + if (event && event->keyval == GDK_Escape) { + *cancelled = TRUE; + gtk_main_quit(); + } + return FALSE; +} + +static gchar *edit_group_format_item( ItemPerson *person, ItemEMail *email ) { + gchar *str = NULL; + gchar *aName = ADDRITEM_NAME(email); + if( aName == NULL || *aName == '\0' ) return str; + if( person ) { + str = g_strdup_printf( "%s - %s", ADDRITEM_NAME(person), aName ); + } + else { + str = g_strdup( aName ); + } + return str; +} + +static void edit_group_list_add_email(GtkTreeStore *store, GtkTreeIter *parent, ItemEMail *email) { + ItemPerson *person = (ItemPerson *)ADDRITEM_PARENT(email); + gchar *name; + GtkTreeIter iter; + + name = edit_group_format_item(person, email); + if (!name) + name = g_strdup(ADDRITEM_NAME(person)); + + gtk_tree_store_append(store, &iter, parent); + gtk_tree_store_set(store, &iter, + GROUP_COL_NAME, name, + GROUP_COL_EMAIL, email->address, + GROUP_COL_REMARKS, email->remarks, + GROUP_COL_DATA, email, -1); + if (parent && GTK_TREE_MODEL(store) == gtk_tree_view_get_model(groupeditdlg.treeview_avail)) { + gtkut_tree_view_expand_parent_all(groupeditdlg.treeview_avail, &iter); + } + + g_free(name); +} + +/* +* Build available email list with folder tree excluding ones in already +* the group +*/ +static void edit_group_load_available_list_recurse(ItemFolder *folder, GtkTreeIter *parent, GHashTable *table) { + GtkTreeModel *model; + GtkTreeStore *store; + GtkTreeIter iter; + GList *node; + + model = gtk_tree_view_get_model(groupeditdlg.treeview_avail); + store = GTK_TREE_STORE(model); + + if (ADDRITEM_PARENT(folder)) { + gtk_tree_store_append(store, &iter, parent); + gtk_tree_store_set(store, &iter, + GROUP_COL_NAME, ADDRITEM_NAME(folder), + GROUP_COL_EMAIL, "", + GROUP_COL_REMARKS, "", + GROUP_COL_DATA, folder, + GROUP_COL_PIXBUF, folderpix, + GROUP_COL_PIXBUF_OPEN, folderopenpix, -1); + } + + node = folder->listFolder; + while (node) { + if (ADDRITEM_TYPE(node->data) == ITEMTYPE_FOLDER) { + ItemFolder *child = (ItemFolder *)node->data; + edit_group_load_available_list_recurse(child, ADDRITEM_PARENT(folder) ? &iter : NULL, table); + } + node = g_list_next(node); + } + + node = folder->listPerson; + while (node) { + if (ADDRITEM_TYPE(node->data) == ITEMTYPE_PERSON) { + GList *cur; + ItemPerson *person = (ItemPerson *)node->data; + cur = person->listEMail; + while (cur) { + ItemEMail *email = (ItemEMail *)cur->data; + if (!g_hash_table_lookup(table, ADDRITEM_ID(email))) { + edit_group_list_add_email(store, ADDRITEM_PARENT(folder) ? &iter : NULL, email); + } + cur = g_list_next(cur); + } + } + node = g_list_next(node); + } +} + +static void edit_group_load_available_list(AddressBookFile *abf, ItemGroup *group) { + GHashTable *table; + + g_return_if_fail(abf != NULL); + + table = g_hash_table_new(g_str_hash, g_str_equal); + if (group) { + GList *list = group->listEMail; + while (list) { + ItemEMail *email = list->data; + g_hash_table_insert(table, ADDRITEM_ID(email), email); + list = g_list_next(list); + } + } + + edit_group_load_available_list_recurse(abf->addressCache->rootFolder, NULL, table); + + g_hash_table_destroy(table); + table = NULL; +} + +static void edit_group_load_group_list( GtkTreeView *treeview, GList *listEMail ) { + GList *node = listEMail; + GtkTreeModel *model = gtk_tree_view_get_model(treeview); + + while (node) { + ItemEMail *email = node->data; + edit_group_list_add_email(GTK_TREE_STORE(model), NULL, email); + node = g_list_next(node); + } +} + +static gboolean edit_group_find_parent_folder(GtkTreeModel *model, GtkTreeIter *parent, ItemEMail *email) { + ItemPerson *person; + ItemFolder *folder; + + person = (ItemPerson *)ADDRITEM_PARENT(email); + folder = (ItemFolder *)ADDRITEM_PARENT(person); + if (ADDRITEM_TYPE(folder) != ITEMTYPE_FOLDER) { + return FALSE; + } + if (ADDRITEM_PARENT(folder) == NULL) { + /* Root folder */ + return FALSE; + } + + if (gtkut_tree_model_find_by_column_data(model, parent, NULL, GROUP_COL_DATA, folder)) { + return TRUE; + } + + return FALSE; +} + +static gboolean edit_group_find_next_selection(GtkTreeModel *model, GtkTreeIter *next) { + gboolean valid; + GtkTreeIter iter = *next; + AddrItemObject *obj; + + while ((valid = gtkut_tree_model_next(model, &iter)) != FALSE) { + gtk_tree_model_get(model, &iter, GROUP_COL_DATA, &obj, -1); + if (!obj || ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER) { + continue; + } + *next = iter; + return TRUE; + } + + return FALSE; +} + +static gboolean edit_group_find_last_selection(GtkTreeModel *model, GtkTreeIter *next) { + gboolean valid; + GtkTreeIter iter; + GtkTreeIter last; + AddrItemObject *obj; + gboolean found = FALSE; + + valid = gtk_tree_model_get_iter_first(model, &iter); + while (valid) { + gtk_tree_model_get(model, &iter, GROUP_COL_DATA, &obj, -1); + if (obj && ADDRITEM_TYPE(obj) != ITEMTYPE_FOLDER) { + last = iter; + found = TRUE; + } + valid = gtkut_tree_model_next(model, &iter); + } + + if (found) { + *next = last; + } + return found; +} + +static void edit_group_move_email( GtkTreeView *treeview_from, GtkTreeView *treeview_to ) { + GtkTreeModel *model; + GtkTreeModel *model_to; + GtkTreeSelection *selection; + GtkTreeIter iter; + GtkTreeIter next; + gboolean next_valid; + GList *rows; + GList *cur; + GtkTreePath *path; + AddrItemObject *obj; + ItemEMail *email; + GtkTreeIter parent; + + model = gtk_tree_view_get_model(treeview_from); + selection = gtk_tree_view_get_selection(treeview_from); + rows = gtk_tree_selection_get_selected_rows(selection, NULL); + if (!rows) { + return; + } + rows = g_list_reverse(rows); + + path = (GtkTreePath *)rows->data; + gtk_tree_model_get_iter(model, &next, path); + next_valid = edit_group_find_next_selection(model, &next); + + model_to = gtk_tree_view_get_model(treeview_to); + + for (cur = rows; cur != NULL; cur = cur->next) { + path = (GtkTreePath *)cur->data; + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, GROUP_COL_DATA, &obj, -1); + if (obj && ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER) { + continue; + } + email = (ItemEMail *)obj; + if (treeview_to == groupeditdlg.treeview_avail && + edit_group_find_parent_folder(model_to, &parent, email)) { + edit_group_list_add_email(GTK_TREE_STORE(model_to), &parent, email); + } else { + edit_group_list_add_email(GTK_TREE_STORE(model_to), NULL, email); + } + gtk_tree_store_remove(GTK_TREE_STORE(model), &iter); + gtk_tree_path_free(path); + } + g_list_free(rows); + + gtk_tree_selection_unselect_all(selection); + if (!next_valid) { + next_valid = edit_group_find_last_selection(model, &next); + } + if (next_valid) { + gtk_tree_selection_select_iter(selection, &next); + } +} + +static void edit_group_to_group( GtkWidget *widget, gpointer data ) { + edit_group_move_email( groupeditdlg.treeview_avail, groupeditdlg.treeview_group ); +} + +static void edit_group_to_avail( GtkWidget *widget, gpointer data ) { + edit_group_move_email( groupeditdlg.treeview_group, groupeditdlg.treeview_avail ); +} + +static gboolean edit_group_list_group_button( GtkWidget *treeview, GdkEventButton *event, gpointer data ) { + if( ! event ) return FALSE; + if( event->button == 1 ) { + if( event->type == GDK_2BUTTON_PRESS ) { + edit_group_to_avail( NULL, NULL ); + } + } + return FALSE; +} + +static gboolean edit_group_list_avail_button( GtkWidget *treeview, GdkEventButton *event, gpointer data ) { + if( ! event ) return FALSE; + if( event->button == 1 ) { + if( event->type == GDK_2BUTTON_PRESS ) { + edit_group_to_group( NULL, NULL ); + } + } + return FALSE; +} + +static gint edit_group_list_col_compare(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gint col) +{ + gchar *name1 = NULL, *name2 = NULL; + gint ret; + + gtk_tree_model_get(model, a, col, &name1, -1); + gtk_tree_model_get(model, b, col, &name2, -1); + + if (!name1) { + name1 = g_strdup(""); + } + if (!name2) { + name2 = g_strdup(""); + } + + ret = g_ascii_strcasecmp(name1, name2); + g_free(name2); + g_free(name1); + + return ret; +} + +static gint edit_group_list_name_compare_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data) { + gint ret; + + ret = edit_group_list_col_compare(model, a, b, GROUP_COL_NAME); + if (ret == 0) + ret = edit_group_list_col_compare(model, a, b, GROUP_COL_EMAIL); + if (ret == 0) + ret = edit_group_list_col_compare(model, a, b, GROUP_COL_REMARKS); + + return ret; +} + +static gint edit_group_list_email_compare_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data) { + gint ret; + + ret = edit_group_list_col_compare(model, a, b, GROUP_COL_EMAIL); + if (ret == 0) + ret = edit_group_list_col_compare(model, a, b, GROUP_COL_NAME); + if (ret == 0) + ret = edit_group_list_col_compare(model, a, b, GROUP_COL_REMARKS); + + return ret; +} + +static gint edit_group_list_remarks_compare_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data) { + gint ret; + + ret = edit_group_list_col_compare(model, a, b, GROUP_COL_REMARKS); + if (ret == 0) + ret = edit_group_list_col_compare(model, a, b, GROUP_COL_NAME); + if (ret == 0) + ret = edit_group_list_col_compare(model, a, b, GROUP_COL_EMAIL); + + return ret; +} + +static void addressbook_edit_group_create( gboolean *cancelled ) { + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *vbox1; + GtkWidget *hbbox; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *hsbox; + GtkWidget *statusbar; + + GtkWidget *hboxg; + GtkWidget *table; + GtkWidget *label; + GtkWidget *entry_name; + GtkWidget *hboxl; + GtkWidget *vboxl; + GtkWidget *hboxh; + GtkWidget *vboxb; + GtkWidget *vboxb1; + GtkWidget *hboxb; + + GtkWidget *treeview_swin; + GtkWidget *treeview_avail; + GtkWidget *treeview_group; + GtkTreeStore *store; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + + GtkWidget *button_add; + GtkWidget *button_remove; + gint top; + + gchar *titles[ GROUP_N_COLS ]; + + titles[ GROUP_COL_NAME ] = _( "Name" ); + titles[ GROUP_COL_EMAIL ] = _("E-Mail Address"); + titles[ GROUP_COL_REMARKS ] = _("Remarks"); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request(window, EDITGROUP_WIDTH, EDITGROUP_HEIGHT); + gtk_window_set_title(GTK_WINDOW(window), _("Edit Group Data")); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_widget_realize(window); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(edit_group_delete_event), + cancelled); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(edit_group_key_pressed), + cancelled); + + vbox = gtk_vbox_new( FALSE, 4 ); + gtk_container_add( GTK_CONTAINER( window ), vbox ); + + vbox1 = gtk_vbox_new( FALSE, 10 ); + gtk_box_pack_start(GTK_BOX(vbox), vbox1, TRUE, TRUE, 0); + gtk_container_set_border_width(GTK_CONTAINER(vbox1), 6); + + /* Group area */ + hboxg = gtk_hbox_new( FALSE, 0 ); + gtk_box_pack_start(GTK_BOX(vbox1), hboxg, FALSE, FALSE, 0); + + /* Data entry area */ + table = gtk_table_new( 1, 2, FALSE); + gtk_box_pack_start(GTK_BOX(hboxg), table, TRUE, TRUE, 0); + gtk_table_set_row_spacings(GTK_TABLE(table), 0); + gtk_table_set_col_spacings(GTK_TABLE(table), 4); + + /* First row */ + top = 0; + label = gtk_label_new(_("Group Name")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + entry_name = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), entry_name, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* List area */ + hboxl = gtk_hbox_new( FALSE, 8 ); + gtk_box_pack_start(GTK_BOX(vbox1), hboxl, TRUE, TRUE, 0); + + /* Available list */ + vboxl = gtk_vbox_new( FALSE, 4 ); + gtk_box_pack_start(GTK_BOX(hboxl), vboxl, TRUE, TRUE, 0); + + hboxh = gtk_hbox_new( FALSE, 0 ); + gtk_box_pack_start(GTK_BOX(vboxl), hboxh, FALSE, FALSE, 0); + label = gtk_label_new(_("Available Addresses")); + gtk_box_pack_start(GTK_BOX(hboxh), label, FALSE, FALSE, 0); + + treeview_swin = gtk_scrolled_window_new( NULL, NULL ); + gtk_box_pack_start(GTK_BOX(vboxl), treeview_swin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(treeview_swin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + + store = gtk_tree_store_new(GROUP_N_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_PIXBUF, GDK_TYPE_PIXBUF); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), + GROUP_COL_NAME, + edit_group_list_name_compare_func, + NULL, NULL); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), + GROUP_COL_EMAIL, + edit_group_list_email_compare_func, + NULL, NULL); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), + GROUP_COL_REMARKS, + edit_group_list_remarks_compare_func, + NULL, NULL); + + treeview_avail = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview_avail), TRUE); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview_avail), FALSE); + gtk_container_add(GTK_CONTAINER(treeview_swin), treeview_avail); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview_avail)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + + column = gtk_tree_view_column_new(); + renderer = gtk_cell_renderer_pixbuf_new(); + g_object_set(renderer, "ypad", 0, NULL); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_set_attributes(column, renderer, "pixbuf", GROUP_COL_PIXBUF, "pixbuf-expander-open", GROUP_COL_PIXBUF_OPEN, "pixbuf-expander-closed", GROUP_COL_PIXBUF, NULL); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, "ypad", 0, NULL); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_set_attributes(column, renderer, "text", GROUP_COL_NAME, NULL); + + gtk_tree_view_column_set_title(column, titles[GROUP_COL_NAME]); + gtk_tree_view_column_set_spacing(column, 1); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(column, GROUP_COL_WIDTH_NAME); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, GROUP_COL_NAME); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview_avail), column); + gtk_tree_view_set_expander_column(GTK_TREE_VIEW(treeview_avail), column); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes(titles[GROUP_COL_EMAIL], renderer, "text", GROUP_COL_EMAIL, NULL); + gtk_tree_view_column_set_spacing(column, 1); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(column, GROUP_COL_WIDTH_EMAIL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, GROUP_COL_EMAIL); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview_avail), column); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes(titles[GROUP_COL_REMARKS], renderer, "text", GROUP_COL_REMARKS, NULL); + gtk_tree_view_column_set_spacing(column, 1); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(column, GROUP_COL_WIDTH_REMARKS); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, GROUP_COL_REMARKS); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview_avail), column); + + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), + GROUP_COL_NAME, GTK_SORT_ASCENDING); + + /* Add/Remove button */ + vboxb = gtk_vbox_new( FALSE, 0 ); + gtk_box_pack_start(GTK_BOX(hboxl), vboxb, FALSE, FALSE, 0); + + vboxb1 = gtk_vbox_new( FALSE, 8 ); + gtk_box_pack_start(GTK_BOX(vboxb), vboxb1, TRUE, FALSE, 0); + + button_add = gtk_button_new_with_label( _( " -> " ) ); + gtk_box_pack_start(GTK_BOX(vboxb1), button_add, FALSE, FALSE, 0); + + button_remove = gtk_button_new_with_label( _( " <- " ) ); + gtk_box_pack_start(GTK_BOX(vboxb1), button_remove, FALSE, FALSE, 0); + + /* Group list */ + vboxl = gtk_vbox_new( FALSE, 4 ); + gtk_box_pack_start(GTK_BOX(hboxl), vboxl, TRUE, TRUE, 0); + + hboxh = gtk_hbox_new( FALSE, 0 ); + gtk_box_pack_start(GTK_BOX(vboxl), hboxh, FALSE, FALSE, 0); + label = gtk_label_new(_("Addresses in Group")); + gtk_box_pack_start(GTK_BOX(hboxh), label, FALSE, FALSE, 0); + + treeview_swin = gtk_scrolled_window_new( NULL, NULL ); + gtk_box_pack_start(GTK_BOX(vboxl), treeview_swin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(treeview_swin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + + store = gtk_tree_store_new(GROUP_N_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_PIXBUF, GDK_TYPE_PIXBUF); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), + GROUP_COL_NAME, + edit_group_list_name_compare_func, + NULL, NULL); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), + GROUP_COL_EMAIL, + edit_group_list_email_compare_func, + NULL, NULL); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), + GROUP_COL_REMARKS, + edit_group_list_remarks_compare_func, + NULL, NULL); + + treeview_group = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview_group), TRUE); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview_group), FALSE); + gtk_container_add(GTK_CONTAINER(treeview_swin), treeview_group); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview_group)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes(titles[GROUP_COL_NAME], renderer, "text", GROUP_COL_NAME, NULL); + gtk_tree_view_column_set_spacing(column, 1); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(column, GROUP_COL_WIDTH_NAME); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, GROUP_COL_NAME); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview_group), column); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes(titles[GROUP_COL_EMAIL], renderer, "text", GROUP_COL_EMAIL, NULL); + gtk_tree_view_column_set_spacing(column, 1); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(column, GROUP_COL_WIDTH_EMAIL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, GROUP_COL_EMAIL); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview_group), column); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes(titles[GROUP_COL_REMARKS], renderer, "text", GROUP_COL_REMARKS, NULL); + gtk_tree_view_column_set_spacing(column, 1); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(column, GROUP_COL_WIDTH_REMARKS); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, GROUP_COL_REMARKS); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview_group), column); + + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), + GROUP_COL_NAME, GTK_SORT_ASCENDING); + /* Button panel */ + hboxb = gtk_hbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(vbox1), hboxb, FALSE, FALSE, 0); + + gtkut_stock_button_set_create(&hbbox, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(hboxb), hbbox, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_btn); + + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(edit_group_ok), cancelled); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(edit_group_cancel), cancelled); + + /* Status line */ + hsbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, 0); + statusbar = gtk_statusbar_new(); + gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, 0); + + gtk_widget_show_all(vbox); + + /* Event handlers */ + g_signal_connect(G_OBJECT(button_add), "clicked", + G_CALLBACK( edit_group_to_group ), NULL); + g_signal_connect(G_OBJECT(button_remove), "clicked", + G_CALLBACK( edit_group_to_avail ), NULL); + g_signal_connect(G_OBJECT(treeview_avail), "button_press_event", + G_CALLBACK(edit_group_list_avail_button), NULL); + g_signal_connect(G_OBJECT(treeview_group), "button_press_event", + G_CALLBACK(edit_group_list_group_button), NULL); + + groupeditdlg.window = window; + groupeditdlg.hbbox = hbbox; + groupeditdlg.ok_btn = ok_btn; + groupeditdlg.cancel_btn = cancel_btn; + groupeditdlg.statusbar = statusbar; + groupeditdlg.status_cid = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "Edit Group Dialog" ); + + groupeditdlg.entry_name = entry_name; + groupeditdlg.treeview_group = GTK_TREE_VIEW( treeview_group ); + groupeditdlg.treeview_avail = GTK_TREE_VIEW( treeview_avail ); + + stock_pixbuf_gdk(window, STOCK_PIXMAP_FOLDER_CLOSE, &folderpix); + stock_pixbuf_gdk(window, STOCK_PIXMAP_FOLDER_OPEN, &folderopenpix); + + if( ! _edit_group_dfl_message_ ) { + _edit_group_dfl_message_ = _( "Move E-Mail Addresses to or from Group with arrow buttons" ); + } +} + +/* +* Return list of email items. +*/ +static GList *edit_group_build_email_list() { + GtkTreeView *treeview = groupeditdlg.treeview_group; + GtkTreeModel *model; + GtkTreeIter iter; + gboolean valid; + GList *listEMail = NULL; + ItemEMail *email; + + model = gtk_tree_view_get_model(treeview); + valid = gtk_tree_model_get_iter_first(model, &iter); + while (valid) { + gtk_tree_model_get(model, &iter, GROUP_COL_DATA, &email, -1); + listEMail = g_list_append( listEMail, email ); + valid = gtk_tree_model_iter_next(model, &iter); + } + + return listEMail; +} + +/* +* Edit group. +* Enter: abf Address book. +* folder Parent folder for group (or NULL if adding to root folder). Argument is +* only required for new objects). +* group Group to edit, or NULL for a new group object. +* Return: Edited object, or NULL if cancelled. +*/ +ItemGroup *addressbook_edit_group( AddressBookFile *abf, ItemFolder *parent, ItemGroup *group ) { + static gboolean cancelled; + GList *listEMail = NULL; + gchar *name; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + + if (!groupeditdlg.window) + addressbook_edit_group_create(&cancelled); + gtkut_box_set_reverse_order(GTK_BOX(groupeditdlg.hbbox), + !prefs_common.comply_gnome_hig); + gtk_widget_grab_focus(groupeditdlg.ok_btn); + gtk_widget_grab_focus(groupeditdlg.entry_name); + manage_window_set_transient(GTK_WINDOW(groupeditdlg.window)); + gtk_widget_show(groupeditdlg.window); + + /* Clear all fields */ + edit_group_status_show( "" ); + model = gtk_tree_view_get_model(groupeditdlg.treeview_group); + gtk_tree_store_clear(GTK_TREE_STORE(model)); + model = gtk_tree_view_get_model(groupeditdlg.treeview_avail); + gtk_tree_store_clear(GTK_TREE_STORE(model)); + + if( group ) { + if( ADDRITEM_NAME(group) ) + gtk_entry_set_text(GTK_ENTRY(groupeditdlg.entry_name), ADDRITEM_NAME(group) ); + edit_group_load_group_list( groupeditdlg.treeview_group, group->listEMail ); + gtk_window_set_title( GTK_WINDOW(groupeditdlg.window), _("Edit Group Details")); + } + else { + gtk_window_set_title( GTK_WINDOW(groupeditdlg.window), _("Add New Group")); + gtk_entry_set_text(GTK_ENTRY(groupeditdlg.entry_name), ADDRESSBOOK_GUESS_GROUP_NAME ); + } + + edit_group_load_available_list(abf, group); + gtk_tree_view_expand_all(groupeditdlg.treeview_avail); + + model = gtk_tree_view_get_model(groupeditdlg.treeview_group); + selection = gtk_tree_view_get_selection(groupeditdlg.treeview_group); + if (gtk_tree_model_get_iter_first(model, &iter)) { + gtk_tree_selection_select_iter(selection, &iter); + } + model = gtk_tree_view_get_model(groupeditdlg.treeview_avail); + selection = gtk_tree_view_get_selection(groupeditdlg.treeview_avail); + if (gtk_tree_model_get_iter_first(model, &iter)) { + gtk_tree_selection_select_iter(selection, &iter); + } + + edit_group_status_show( _edit_group_dfl_message_ ); + + gtk_main(); + gtk_widget_hide( groupeditdlg.window ); + + if( cancelled ) { + return NULL; + } + + listEMail = edit_group_build_email_list(); + if( group ) { + /* Update email list */ + addrbook_update_group_list( abf, group, listEMail ); + } + else { + /* Create new person and email list */ + group = addrbook_add_group_list( abf, parent, listEMail ); + } + name = gtk_editable_get_chars( GTK_EDITABLE(groupeditdlg.entry_name), 0, -1 ); + addritem_group_set_name( group, name ); + g_free( name ); + + listEMail = NULL; + return group; +} + +/* +* Edit folder. +* Enter: abf Address book. +* parent Parent folder for folder (or NULL if adding to root folder). Argument is +* only required for new objects). +* folder Folder to edit, or NULL for a new folder object. +* Return: Edited object, or NULL if cancelled. +*/ +ItemFolder *addressbook_edit_folder( AddressBookFile *abf, ItemFolder *parent, ItemFolder *folder ) { + gchar *name = NULL; + + if( folder ) { + name = g_strdup( ADDRITEM_NAME(folder) ); + name = input_dialog( _("Edit folder"), _("Input the new name of folder:"), name ); + } + else { + name = input_dialog( _("New folder"), + _("Input the name of new folder:"), + _(ADDRESSBOOK_GUESS_FOLDER_NAME) ); + } + if( ! name ) return NULL; + g_strstrip( name ); + if( *name == '\0' ) { + g_free( name ); + return NULL; + } + if( folder ) { + if( g_ascii_strcasecmp( name, ADDRITEM_NAME(folder) ) == 0 ) { + g_free( name ); + return NULL; + } + } + + if( ! folder ) { + folder = addrbook_add_new_folder( abf, parent ); + } + addritem_folder_set_name( folder, name ); + g_free( name ); + return folder; +} + +/* +* End of Source. +*/ + diff --git a/src/editgroup.h b/src/editgroup.h new file mode 100644 index 0000000..6a3571d --- /dev/null +++ b/src/editgroup.h @@ -0,0 +1,26 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EDITGROUP_H__ +#define __EDITGROUP_H__ + +ItemGroup *addressbook_edit_group( AddressBookFile *abf, ItemFolder *folder, ItemGroup *group ); +ItemFolder *addressbook_edit_folder( AddressBookFile *abf, ItemFolder *parent, ItemFolder *folder ); + +#endif /* __EDITGROUP_H__ */ diff --git a/src/editjpilot.c b/src/editjpilot.c new file mode 100644 index 0000000..436d181 --- /dev/null +++ b/src/editjpilot.c @@ -0,0 +1,429 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Edit JPilot address book data. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef USE_JPILOT + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "addressbook.h" +#include "prefs_common.h" +#include "addressitem.h" +#include "jpilot.h" +#include "mgutils.h" +#include "filesel.h" +#include "gtkutils.h" +#include "codeconv.h" +#include "manage_window.h" + +#define ADDRESSBOOK_GUESS_JPILOT "MyJPilot" +#define JPILOT_NUM_CUSTOM_LABEL 4 + +static struct _JPilotEdit { + GtkWidget *window; + GtkWidget *name_entry; + GtkWidget *file_entry; + GtkWidget *custom_check[JPILOT_NUM_CUSTOM_LABEL]; + GtkWidget *custom_label[JPILOT_NUM_CUSTOM_LABEL]; + GtkWidget *hbbox; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *statusbar; + gint status_cid; +} jpilotedit; + +/* +* Edit functions. +*/ +void edit_jpilot_status_show( gchar *msg ) { + if( jpilotedit.statusbar != NULL ) { + gtk_statusbar_pop( GTK_STATUSBAR(jpilotedit.statusbar), jpilotedit.status_cid ); + if( msg ) { + gtk_statusbar_push( GTK_STATUSBAR(jpilotedit.statusbar), jpilotedit.status_cid, msg ); + } + } +} + +static gint edit_jpilot_delete_event( GtkWidget *widget, GdkEventAny *event, gboolean *cancelled ) { + *cancelled = TRUE; + gtk_main_quit(); + return TRUE; +} + +static gboolean edit_jpilot_key_pressed( GtkWidget *widget, GdkEventKey *event, gboolean *cancelled ) { + if (event && event->keyval == GDK_Escape) { + *cancelled = TRUE; + gtk_main_quit(); + } + return FALSE; +} + +static void edit_jpilot_ok( GtkWidget *widget, gboolean *cancelled ) { + *cancelled = FALSE; + gtk_main_quit(); +} + +static void edit_jpilot_cancel( GtkWidget *widget, gboolean *cancelled ) { + *cancelled = TRUE; + gtk_main_quit(); +} + +static void edit_jpilot_fill_check_box( JPilotFile *jpf ) { + gint i; + GList *node, *customLbl = NULL; + gchar *labelName; + gboolean done, checked; + for( i = 0; i < JPILOT_NUM_CUSTOM_LABEL; i++ ) { + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( jpilotedit.custom_check[i] ), FALSE ); + gtk_label_set_text( GTK_LABEL( jpilotedit.custom_label[i] ), "" ); + } + + done = FALSE; + i = 0; + customLbl = jpilot_load_custom_label( jpf, customLbl ); + node = customLbl; + while( ! done ) { + if( node ) { + labelName = node->data; + gtk_label_set_text( GTK_LABEL( jpilotedit.custom_label[i] ), labelName ); + checked = jpilot_test_custom_label( jpf, labelName ); + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( jpilotedit.custom_check[i] ), checked ); + i++; + if( i >= JPILOT_NUM_CUSTOM_LABEL ) done = TRUE; + node = g_list_next( node ); + } + else { + done = TRUE; + } + } + mgu_free_dlist( customLbl ); + customLbl = NULL; +} + +static void edit_jpilot_fill_check_box_new() { + gint i; + for( i = 0; i < JPILOT_NUM_CUSTOM_LABEL; i++ ) { + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( jpilotedit.custom_check[i] ), FALSE ); + gtk_label_set_text( GTK_LABEL( jpilotedit.custom_label[i] ), "" ); + } +} + +static void edit_jpilot_read_check_box( JPilotFile *pilotFile ) { + gint i; + gchar *labelName; + jpilot_clear_custom_labels( pilotFile ); + for( i = 0; i < JPILOT_NUM_CUSTOM_LABEL; i++ ) { + if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(jpilotedit.custom_check[i]) ) ) { + labelName = GTK_LABEL(jpilotedit.custom_label[i])->label; + jpilot_add_custom_label( pilotFile, labelName ); + } + } +} + +static void edit_jpilot_file_check( void ) { + gint t = -1; + gchar *sFile; + gchar *sFSFile; + gchar *sMsg; + gboolean flg; + + flg = FALSE; + sFile = gtk_editable_get_chars( GTK_EDITABLE(jpilotedit.file_entry), 0, -1 ); + if( sFile ) { + sFSFile = conv_filename_from_utf8( sFile ); + if( sFSFile && *sFSFile != '\0' ) { + /* Attempt to read file */ + JPilotFile *jpf; + jpf = jpilot_create_path( sFSFile ); + t = jpilot_read_data( jpf ); + if( t == MGU_SUCCESS ) { + /* Set check boxes */ + edit_jpilot_fill_check_box( jpf ); + flg = TRUE; + } + jpilot_free( jpf ); + jpf = NULL; + } + g_free( sFSFile ); + g_free( sFile ); + } + if( ! flg ) { + /* Clear all check boxes */ + edit_jpilot_fill_check_box_new(); + } + + /* Display appropriate message */ + if( t == MGU_SUCCESS ) { + sMsg = ""; + } + else if( t == MGU_BAD_FORMAT || t == MGU_OO_MEMORY ) { + sMsg = _("File does not appear to be JPilot format."); + } + else { + sMsg = _("Could not read file."); + } + edit_jpilot_status_show( sMsg ); +} + +static void edit_jpilot_file_select( void ) { + gchar *sFile; + gchar *sUTF8File; + + sFile = filesel_select_file( _("Select JPilot File"), NULL, GTK_FILE_CHOOSER_ACTION_OPEN ); + + if( sFile ) { + sUTF8File = conv_filename_to_utf8( sFile ); + gtk_entry_set_text( GTK_ENTRY(jpilotedit.file_entry), sUTF8File ); + g_free( sUTF8File ); + g_free( sFile ); + edit_jpilot_file_check(); + } +} + +static void addressbook_edit_jpilot_create( gboolean *cancelled ) { + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *label; + GtkWidget *name_entry; + GtkWidget *file_entry; + GtkWidget *vbox_custom; + GtkWidget *frame_custom; + GtkWidget *custom_check[JPILOT_NUM_CUSTOM_LABEL]; + GtkWidget *custom_label[JPILOT_NUM_CUSTOM_LABEL]; + GtkWidget *hlbox; + GtkWidget *hbbox; + GtkWidget *hsep; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *check_btn; + GtkWidget *file_btn; + GtkWidget *hsbox; + GtkWidget *statusbar; + gint top, i; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request(window, 450, -1); + gtk_container_set_border_width(GTK_CONTAINER(window), 0); + gtk_window_set_title(GTK_WINDOW(window), _("Edit JPilot Entry")); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(edit_jpilot_delete_event), + cancelled); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(edit_jpilot_key_pressed), + cancelled); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add(GTK_CONTAINER(window), vbox); + gtk_container_set_border_width( GTK_CONTAINER(vbox), 0 ); + + table = gtk_table_new(2 + JPILOT_NUM_CUSTOM_LABEL, 3, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 8 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + + /* First row */ + top = 0; + label = gtk_label_new(_("Name")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + name_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), name_entry, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + check_btn = gtk_button_new_with_label( _(" Check File ")); + gtk_table_attach(GTK_TABLE(table), check_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0); + + /* Second row */ + top = 1; + label = gtk_label_new(_("File")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + file_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), file_entry, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + file_btn = gtk_button_new_with_label( _(" ... ")); + gtk_table_attach(GTK_TABLE(table), file_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0); + + /* Third row */ + top = 2; + frame_custom = gtk_frame_new(_("Additional e-Mail address item(s)")); + gtk_table_attach(GTK_TABLE(table), frame_custom, 1, 2, top, (top + JPILOT_NUM_CUSTOM_LABEL), GTK_FILL, 0, 0, 0); + + /* Now do custom labels. */ + vbox_custom = gtk_vbox_new (FALSE, 8); + for( i = 0; i < JPILOT_NUM_CUSTOM_LABEL; i++ ) { + hlbox = gtk_hbox_new( FALSE, 0 ); + custom_check[i] = gtk_check_button_new(); + custom_label[i] = gtk_label_new( "" ); + gtk_box_pack_start( GTK_BOX(hlbox), custom_check[i], FALSE, FALSE, 0 ); + gtk_box_pack_start( GTK_BOX(hlbox), custom_label[i], TRUE, TRUE, 0 ); + gtk_box_pack_start( GTK_BOX(vbox_custom), hlbox, TRUE, TRUE, 0 ); + gtk_misc_set_alignment(GTK_MISC(custom_label[i]), 0, 0.5); + top++; + } + gtk_container_add (GTK_CONTAINER (frame_custom), vbox_custom); + gtk_container_set_border_width( GTK_CONTAINER(vbox_custom), 8 ); + + /* Status line */ + hsbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, 0); + statusbar = gtk_statusbar_new(); + gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, 0); + + /* Button panel */ + gtkut_stock_button_set_create(&hbbox, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(hbbox), 0 ); + gtk_widget_grab_default(ok_btn); + + hsep = gtk_hseparator_new(); + gtk_box_pack_end(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(edit_jpilot_ok), cancelled); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(edit_jpilot_cancel), cancelled); + g_signal_connect(G_OBJECT(file_btn), "clicked", + G_CALLBACK(edit_jpilot_file_select), NULL); + g_signal_connect(G_OBJECT(check_btn), "clicked", + G_CALLBACK(edit_jpilot_file_check), NULL); + + gtk_widget_show_all(vbox); + + jpilotedit.window = window; + jpilotedit.name_entry = name_entry; + jpilotedit.file_entry = file_entry; + jpilotedit.hbbox = hbbox; + jpilotedit.ok_btn = ok_btn; + jpilotedit.cancel_btn = cancel_btn; + jpilotedit.statusbar = statusbar; + jpilotedit.status_cid = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "Edit JPilot Dialog" ); + for( i = 0; i < JPILOT_NUM_CUSTOM_LABEL; i++ ) { + jpilotedit.custom_check[i] = custom_check[i]; + jpilotedit.custom_label[i] = custom_label[i]; + } +} + +AdapterDSource *addressbook_edit_jpilot( AddressIndex *addrIndex, AdapterDSource *ads ) { + static gboolean cancelled; + gchar *sName; + gchar *sFile; + gchar *sFSFile; + AddressDataSource *ds = NULL; + JPilotFile *jpf = NULL; + gboolean fin; + + if( ! jpilotedit.window ) + addressbook_edit_jpilot_create(&cancelled); + gtkut_box_set_reverse_order(GTK_BOX(jpilotedit.hbbox), + !prefs_common.comply_gnome_hig); + gtk_widget_grab_focus(jpilotedit.ok_btn); + gtk_widget_grab_focus(jpilotedit.name_entry); + gtk_widget_show(jpilotedit.window); + manage_window_set_transient(GTK_WINDOW(jpilotedit.window)); + + edit_jpilot_status_show( "" ); + if( ads ) { + ds = ads->dataSource; + jpf = ds->rawDataSource; + if (jpf->name) + gtk_entry_set_text(GTK_ENTRY(jpilotedit.name_entry), jpf->name); + if (jpf->path) + gtk_entry_set_text(GTK_ENTRY(jpilotedit.file_entry), jpf->path); + gtk_window_set_title( GTK_WINDOW(jpilotedit.window), _("Edit JPilot Entry")); + edit_jpilot_fill_check_box( jpf ); + } + else { + gchar *guessFile = jpilot_find_pilotdb(); + gtk_entry_set_text(GTK_ENTRY(jpilotedit.name_entry), ADDRESSBOOK_GUESS_JPILOT ); + gtk_entry_set_text(GTK_ENTRY(jpilotedit.file_entry), guessFile ); + gtk_window_set_title( GTK_WINDOW(jpilotedit.window), _("Add New JPilot Entry")); + edit_jpilot_fill_check_box_new(); + if( *guessFile != '\0' ) { + edit_jpilot_file_check(); + } + } + + gtk_main(); + gtk_widget_hide(jpilotedit.window); + if (cancelled == TRUE) return NULL; + + fin = FALSE; + sName = gtk_editable_get_chars( GTK_EDITABLE(jpilotedit.name_entry), 0, -1 ); + sFile = gtk_editable_get_chars( GTK_EDITABLE(jpilotedit.file_entry), 0, -1 ); + sFSFile = conv_filename_from_utf8( sFile ); + if( *sName == '\0' ) fin = TRUE; + if( *sFile == '\0' ) fin = TRUE; + if( ! sFSFile || *sFSFile == '\0' ) fin = TRUE; + + if( ! fin ) { + if( ! ads ) { + jpf = jpilot_create(); + ds = addrindex_index_add_datasource( addrIndex, ADDR_IF_JPILOT, jpf ); + ads = addressbook_create_ds_adapter( ds, ADDR_JPILOT, NULL ); + } + addressbook_ads_set_name( ads, sName ); + jpilot_set_name( jpf, sName ); + jpilot_set_file( jpf, sFSFile ); + edit_jpilot_read_check_box( jpf ); + } + g_free( sFSFile ); + g_free( sFile ); + g_free( sName ); + + return ads; +} + +#endif /* USE_JPILOT */ + +/* +* End of Source. +*/ + diff --git a/src/editjpilot.h b/src/editjpilot.h new file mode 100644 index 0000000..e866255 --- /dev/null +++ b/src/editjpilot.h @@ -0,0 +1,33 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Edit JPilot address book data. + */ + +#ifndef __EDITJPILOT_H__ +#define __EDITJPILOT_H__ + +#ifdef USE_JPILOT + +AdapterDSource *addressbook_edit_jpilot( AddressIndex *addrIndex, AdapterDSource *ads ); + +#endif /* USE_JPILOT */ + +#endif /* __EDITJPILOT_H__ */ diff --git a/src/editldap.c b/src/editldap.c new file mode 100644 index 0000000..ebb7a25 --- /dev/null +++ b/src/editldap.c @@ -0,0 +1,611 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Edit LDAP address book data. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef USE_LDAP + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "addressbook.h" +#include "prefs_common.h" +#include "addressitem.h" +#include "mgutils.h" +#include "syldap.h" +#include "editldap_basedn.h" +#include "manage_window.h" +#include "gtkutils.h" + +#define ADDRESSBOOK_GUESS_LDAP_NAME "MyServer" +#define ADDRESSBOOK_GUESS_LDAP_SERVER "localhost" + +#define LDAPEDIT_TABLE_ROWS 6 +#define LDAPEDIT_TABLE_COLS 3 + +static struct _LDAPEdit { + GtkWidget *window; + GtkWidget *notebook; + GtkWidget *hbbox; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *statusbar; + gint status_cid; + GtkWidget *entry_name; + GtkWidget *entry_server; + GtkWidget *spinbtn_port; + GtkWidget *entry_baseDN; + GtkWidget *spinbtn_timeout; + GtkWidget *entry_bindDN; + GtkWidget *entry_bindPW; + GtkWidget *entry_criteria; + GtkWidget *spinbtn_maxentry; +} ldapedit; + +/* +* Edit functions. +*/ +static void edit_ldap_status_show( gchar *msg ) { + if( ldapedit.statusbar != NULL ) { + gtk_statusbar_pop( GTK_STATUSBAR(ldapedit.statusbar), ldapedit.status_cid ); + if( msg ) { + gtk_statusbar_push( GTK_STATUSBAR(ldapedit.statusbar), ldapedit.status_cid, msg ); + } + } +} + +static void edit_ldap_ok( GtkWidget *widget, gboolean *cancelled ) { + *cancelled = FALSE; + gtk_main_quit(); +} + +static void edit_ldap_cancel( GtkWidget *widget, gboolean *cancelled ) { + *cancelled = TRUE; + gtk_main_quit(); +} + +static gint edit_ldap_delete_event( GtkWidget *widget, GdkEventAny *event, gboolean *cancelled ) { + *cancelled = TRUE; + gtk_main_quit(); + return TRUE; +} + +static gboolean edit_ldap_key_pressed( GtkWidget *widget, GdkEventKey *event, gboolean *cancelled ) { + if (event && event->keyval == GDK_Escape) { + *cancelled = TRUE; + gtk_main_quit(); + } + return FALSE; +} + +static void edit_ldap_switch_page( GtkWidget *widget ) { + edit_ldap_status_show( "" ); +} + +static void edit_ldap_server_check( void ) { + gchar *sHost, *sBind, *sPass; + gint iPort, iTime; + gchar *sMsg; + gchar *sBaseDN = NULL; + gint iBaseDN = 0; + gboolean flg; + + edit_ldap_status_show( "" ); + flg = FALSE; + sHost = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_server), 0, -1 ); + sBind = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_bindDN), 0, -1 ); + sPass = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_bindPW), 0, -1 ); + iPort = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( ldapedit.spinbtn_port ) ); + iTime = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( ldapedit.spinbtn_timeout ) ); + g_strchomp( sHost ); g_strchug( sHost ); + g_strchomp( sBind ); g_strchug( sBind ); + g_strchomp( sPass ); g_strchug( sPass ); + if( *sHost != '\0' ) { + /* Test connection to server */ + if( syldap_test_connect_s( sHost, iPort ) ) { + /* Attempt to read base DN */ + GList *baseDN = syldap_read_basedn_s( sHost, iPort, sBind, sPass, iTime ); + if( baseDN ) { + GList *node = baseDN; + while( node ) { + ++iBaseDN; + if( ! sBaseDN ) { + sBaseDN = g_strdup( node->data ); + } + node = g_list_next( node ); + } + mgu_free_dlist( baseDN ); + baseDN = node = NULL; + } + flg = TRUE; + } + } + g_free( sHost ); + g_free( sBind ); + g_free( sPass ); + + if( sBaseDN ) { + /* Load search DN */ + gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_baseDN), sBaseDN); + g_free( sBaseDN ); + } + + /* Display appropriate message */ + if( flg ) { + sMsg = _( "Connected successfully to server" ); + } + else { + sMsg = _( "Could not connect to server" ); + } + edit_ldap_status_show( sMsg ); +} + +static void edit_ldap_basedn_select( void ) { + gchar *sHost, *sBind, *sPass, *sBase; + gint iPort, iTime; + gchar *selectDN; + + sHost = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_server), 0, -1 ); + sBase = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_baseDN), 0, -1 ); + sBind = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_bindDN), 0, -1 ); + sPass = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_bindPW), 0, -1 ); + iPort = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( ldapedit.spinbtn_port ) ); + iTime = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( ldapedit.spinbtn_timeout ) ); + g_strchomp( sHost ); g_strchug( sHost ); + g_strchomp( sBind ); g_strchug( sBind ); + g_strchomp( sPass ); g_strchug( sPass ); + selectDN = edit_ldap_basedn_selection( sHost, iPort, sBase, iTime, sBind, sPass ); + if( selectDN ) { + gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_baseDN), selectDN); + g_free( selectDN ); + selectDN = NULL; + } + g_free( sHost ); + g_free( sBase ); + g_free( sBind ); + g_free( sPass ); +} + +static void edit_ldap_search_reset( void ) { + gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_criteria), SYLDAP_DFL_CRITERIA ); +} + +static void addressbook_edit_ldap_dialog_create( gboolean *cancelled ) { + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *notebook; + GtkWidget *hbbox; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *hsbox; + GtkWidget *statusbar; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request(window, 450, -1); + gtk_container_set_border_width(GTK_CONTAINER(window), 0); + gtk_window_set_title(GTK_WINDOW(window), _("Edit LDAP Server")); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(edit_ldap_delete_event), + cancelled); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(edit_ldap_key_pressed), + cancelled); + + vbox = gtk_vbox_new( FALSE, 6 ); + /* gtk_container_set_border_width(GTK_CONTAINER(vbox), BORDER_WIDTH); */ + gtk_widget_show( vbox ); + gtk_container_add( GTK_CONTAINER( window ), vbox ); + + /* Notebook */ + notebook = gtk_notebook_new(); + gtk_widget_show( notebook ); + gtk_box_pack_start( GTK_BOX( vbox ), notebook, TRUE, TRUE, 0 ); + gtk_container_set_border_width( GTK_CONTAINER( notebook ), 6 ); + + /* Status line */ + hsbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, 0); + statusbar = gtk_statusbar_new(); + gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, 0); + + /* Button panel */ + gtkut_stock_button_set_create(&hbbox, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_btn); + + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(edit_ldap_ok), cancelled); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(edit_ldap_cancel), cancelled); + g_signal_connect(G_OBJECT(notebook), "switch_page", + G_CALLBACK(edit_ldap_switch_page), NULL ); + + gtk_widget_show_all(vbox); + + ldapedit.window = window; + ldapedit.notebook = notebook; + ldapedit.hbbox = hbbox; + ldapedit.ok_btn = ok_btn; + ldapedit.cancel_btn = cancel_btn; + ldapedit.statusbar = statusbar; + ldapedit.status_cid = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "Edit LDAP Server Dialog" ); +} + +void addressbook_edit_ldap_page_basic( gint pageNum, gchar *pageLbl ) { + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *label; + GtkWidget *entry_name; + GtkWidget *entry_server; + GtkWidget *hbox_spin; + GtkObject *spinbtn_port_adj; + GtkWidget *spinbtn_port; + GtkWidget *entry_baseDN; + GtkWidget *check_btn; + GtkWidget *lookdn_btn; + gint top; + + vbox = gtk_vbox_new( FALSE, 8 ); + gtk_widget_show( vbox ); + gtk_container_add( GTK_CONTAINER( ldapedit.notebook ), vbox ); + /* gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH ); */ + + label = gtk_label_new( pageLbl ); + gtk_widget_show( label ); + gtk_notebook_set_tab_label( + GTK_NOTEBOOK( ldapedit.notebook ), + gtk_notebook_get_nth_page( GTK_NOTEBOOK( ldapedit.notebook ), pageNum ), label ); + + table = gtk_table_new( LDAPEDIT_TABLE_ROWS, LDAPEDIT_TABLE_COLS, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 8 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + + /* First row */ + top = 0; + label = gtk_label_new(_("Name")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + entry_name = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), entry_name, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* Next row */ + ++top; + label = gtk_label_new(_("Hostname")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + entry_server = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), entry_server, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* Next row */ + ++top; + label = gtk_label_new(_("Port")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + hbox_spin = gtk_hbox_new (FALSE, 8); + spinbtn_port_adj = gtk_adjustment_new (389, 1, 65535, 100, 1000, 0); + spinbtn_port = gtk_spin_button_new(GTK_ADJUSTMENT (spinbtn_port_adj), 1, 0); + gtk_box_pack_start (GTK_BOX (hbox_spin), spinbtn_port, FALSE, FALSE, 0); + gtk_widget_set_size_request (spinbtn_port, 64, -1); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_port), TRUE); + gtk_table_attach(GTK_TABLE(table), hbox_spin, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + check_btn = gtk_button_new_with_label( _(" Check Server ")); + gtk_table_attach(GTK_TABLE(table), check_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0); + + /* Next row */ + ++top; + label = gtk_label_new(_("Search Base")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + entry_baseDN = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), entry_baseDN, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + lookdn_btn = gtk_button_new_with_label( _(" ... ")); + gtk_table_attach(GTK_TABLE(table), lookdn_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0); + + g_signal_connect(G_OBJECT(check_btn), "clicked", + G_CALLBACK(edit_ldap_server_check), NULL); + g_signal_connect(G_OBJECT(lookdn_btn), "clicked", + G_CALLBACK(edit_ldap_basedn_select), NULL); + + gtk_widget_show_all(vbox); + + ldapedit.entry_name = entry_name; + ldapedit.entry_server = entry_server; + ldapedit.spinbtn_port = spinbtn_port; + ldapedit.entry_baseDN = entry_baseDN; +} + +void addressbook_edit_ldap_page_extended( gint pageNum, gchar *pageLbl ) { + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *label; + GtkWidget *entry_bindDN; + GtkWidget *entry_bindPW; + GtkWidget *entry_criteria; + GtkWidget *hbox_spin; + GtkObject *spinbtn_timeout_adj; + GtkWidget *spinbtn_timeout; + GtkObject *spinbtn_maxentry_adj; + GtkWidget *spinbtn_maxentry; + GtkWidget *reset_btn; + gint top; + + vbox = gtk_vbox_new( FALSE, 8 ); + gtk_widget_show( vbox ); + gtk_container_add( GTK_CONTAINER( ldapedit.notebook ), vbox ); + /* gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH ); */ + + label = gtk_label_new( pageLbl ); + gtk_widget_show( label ); + gtk_notebook_set_tab_label( + GTK_NOTEBOOK( ldapedit.notebook ), + gtk_notebook_get_nth_page( GTK_NOTEBOOK( ldapedit.notebook ), pageNum ), label ); + + table = gtk_table_new( LDAPEDIT_TABLE_ROWS, LDAPEDIT_TABLE_COLS, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 8 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + + /* First row */ + top = 0; + label = gtk_label_new(_("Search Criteria")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + entry_criteria = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), entry_criteria, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + reset_btn = gtk_button_new_with_label( _(" Reset ")); + gtk_table_attach(GTK_TABLE(table), reset_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0); + + /* Next row */ + ++top; + label = gtk_label_new(_("Bind DN")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + entry_bindDN = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), entry_bindDN, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* Next row */ + ++top; + label = gtk_label_new(_("Bind Password")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + entry_bindPW = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), entry_bindPW, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + gtk_entry_set_visibility(GTK_ENTRY(entry_bindPW), FALSE); + + /* Next row */ + ++top; + label = gtk_label_new(_("Timeout (secs)")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + hbox_spin = gtk_hbox_new (FALSE, 8); + spinbtn_timeout_adj = gtk_adjustment_new (0, 0, 300, 1, 10, 0); + spinbtn_timeout = gtk_spin_button_new(GTK_ADJUSTMENT (spinbtn_timeout_adj), 1, 0); + gtk_box_pack_start (GTK_BOX (hbox_spin), spinbtn_timeout, FALSE, FALSE, 0); + gtk_widget_set_size_request (spinbtn_timeout, 64, -1); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_timeout), TRUE); + gtk_table_attach(GTK_TABLE(table), hbox_spin, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* Next row */ + ++top; + label = gtk_label_new(_("Maximum Entries")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + hbox_spin = gtk_hbox_new (FALSE, 8); + spinbtn_maxentry_adj = gtk_adjustment_new (0, 0, 500, 1, 10, 0); + spinbtn_maxentry = gtk_spin_button_new(GTK_ADJUSTMENT (spinbtn_maxentry_adj), 1, 0); + gtk_box_pack_start (GTK_BOX (hbox_spin), spinbtn_maxentry, FALSE, FALSE, 0); + gtk_widget_set_size_request (spinbtn_maxentry, 64, -1); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_maxentry), TRUE); + gtk_table_attach(GTK_TABLE(table), hbox_spin, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + g_signal_connect(G_OBJECT(reset_btn), "clicked", + G_CALLBACK(edit_ldap_search_reset), NULL); + + gtk_widget_show_all(vbox); + + ldapedit.entry_criteria = entry_criteria; + ldapedit.entry_bindDN = entry_bindDN; + ldapedit.entry_bindPW = entry_bindPW; + ldapedit.spinbtn_timeout = spinbtn_timeout; + ldapedit.spinbtn_maxentry = spinbtn_maxentry; +} + +static void addressbook_edit_ldap_create( gboolean *cancelled ) { + gint page = 0; + addressbook_edit_ldap_dialog_create( cancelled ); + addressbook_edit_ldap_page_basic( page++, _( "Basic" ) ); + addressbook_edit_ldap_page_extended( page++, _( "Extended" ) ); + gtk_widget_show_all( ldapedit.window ); +} + +void edit_ldap_set_optmenu( GtkOptionMenu *optmenu, const gint value ) { + GList *cur; + GtkWidget *menu; + GtkWidget *menuitem; + gint menuVal; + gint n = 0; + + g_return_if_fail(optmenu != NULL); + + menu = gtk_option_menu_get_menu(optmenu); + for( cur = GTK_MENU_SHELL(menu)->children; cur != NULL; cur = cur->next ) { + menuitem = GTK_WIDGET(cur->data); + menuVal = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), "user_data")); + if( menuVal == value ) { + gtk_option_menu_set_history(optmenu, n); + return; + } + n++; + } + gtk_option_menu_set_history(optmenu, 0); +} + +gint edit_ldap_get_optmenu( GtkOptionMenu *optmenu ) { + GtkWidget *menu; + GtkWidget *menuitem; + + g_return_val_if_fail(optmenu != NULL, -1); + + menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu)); + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + return GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), "user_data")); +} + +AdapterDSource *addressbook_edit_ldap( AddressIndex *addrIndex, AdapterDSource *ads ) { + static gboolean cancelled; + gchar *sName, *sHost, *sBase, *sBind, *sPass, *sCrit; + gint iPort, iMaxE, iTime; + AddressDataSource *ds = NULL; + SyldapServer *server = NULL; + gboolean fin; + + if (!ldapedit.window) + addressbook_edit_ldap_create(&cancelled); + gtkut_box_set_reverse_order(GTK_BOX(ldapedit.hbbox), + !prefs_common.comply_gnome_hig); + gtk_notebook_set_current_page( GTK_NOTEBOOK(ldapedit.notebook), 0 ); + gtk_widget_grab_focus(ldapedit.ok_btn); + gtk_widget_grab_focus(ldapedit.entry_name); + gtk_widget_show(ldapedit.window); + manage_window_set_transient(GTK_WINDOW(ldapedit.window)); + + edit_ldap_status_show( "" ); + if( ads ) { + ds = ads->dataSource; + server = ds->rawDataSource; + if (server->name) + gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_name), server->name); + if (server->hostName) + gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_server), server->hostName); + gtk_spin_button_set_value( GTK_SPIN_BUTTON( ldapedit.spinbtn_port ), server->port ); + gtk_spin_button_set_value( GTK_SPIN_BUTTON( ldapedit.spinbtn_timeout ), server->timeOut ); + if (server->baseDN) + gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_baseDN), server->baseDN); + if (server->searchCriteria) + gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_criteria), server->searchCriteria); + if (server->bindDN) + gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_bindDN), server->bindDN); + if (server->bindPass) + gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_bindPW), server->bindPass); + gtk_spin_button_set_value( GTK_SPIN_BUTTON( ldapedit.spinbtn_maxentry ), server->maxEntries ); + gtk_window_set_title( GTK_WINDOW(ldapedit.window), _("Edit LDAP Server")); + } + else { + gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_name), ADDRESSBOOK_GUESS_LDAP_NAME ); + gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_server), ADDRESSBOOK_GUESS_LDAP_SERVER ); + gtk_spin_button_set_value( GTK_SPIN_BUTTON( ldapedit.spinbtn_port ), SYLDAP_DFL_PORT ); + gtk_spin_button_set_value( GTK_SPIN_BUTTON( ldapedit.spinbtn_timeout ), SYLDAP_DFL_TIMEOUT ); + gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_baseDN), ""); + gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_criteria), SYLDAP_DFL_CRITERIA ); + gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_bindDN), ""); + gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_bindPW), ""); + gtk_spin_button_set_value( GTK_SPIN_BUTTON( ldapedit.spinbtn_maxentry ), SYLDAP_MAX_ENTRIES ); + gtk_window_set_title( GTK_WINDOW(ldapedit.window), _("Add New LDAP Server")); + } + + gtk_main(); + gtk_widget_hide(ldapedit.window); + if (cancelled == TRUE) return NULL; + + fin = FALSE; + sName = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_name), 0, -1 ); + sHost = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_server), 0, -1 ); + iPort = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( ldapedit.spinbtn_port ) ); + iTime = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( ldapedit.spinbtn_timeout ) ); + sBase = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_baseDN), 0, -1 ); + sCrit = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_criteria), 0, -1 ); + sBind = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_bindDN), 0, -1 ); + sPass = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_bindPW), 0, -1 ); + iMaxE = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( ldapedit.spinbtn_maxentry ) ); + + if( *sName == '\0' ) fin = TRUE; + if( *sHost == '\0' ) fin = TRUE; + if( *sBase == '\0' ) fin = TRUE; + + if( ! fin ) { + if( ! ads ) { + server = syldap_create(); + ds = addrindex_index_add_datasource( addrIndex, ADDR_IF_LDAP, server ); + ads = addressbook_create_ds_adapter( ds, ADDR_LDAP, NULL ); + } + addressbook_ads_set_name( ads, sName ); + syldap_set_name( server, sName ); + syldap_set_host( server, sHost ); + syldap_set_port( server, iPort ); + syldap_set_base_dn( server, sBase ); + syldap_set_bind_dn( server, sBind ); + syldap_set_bind_password( server, sPass ); + syldap_set_search_criteria( server, sCrit ); + syldap_set_max_entries( server, iMaxE ); + syldap_set_timeout( server, iTime ); + } + g_free( sName ); + g_free( sHost ); + g_free( sBase ); + g_free( sBind ); + g_free( sPass ); + g_free( sCrit ); + + return ads; +} + +#endif /* USE_LDAP */ + +/* +* End of Source. +*/ diff --git a/src/editldap.h b/src/editldap.h new file mode 100644 index 0000000..d1bab62 --- /dev/null +++ b/src/editldap.h @@ -0,0 +1,29 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EDITLDAP_H__ +#define __EDITLDAP_H__ + +#ifdef USE_LDAP + +AdapterDSource *addressbook_edit_ldap( AddressIndex *addrIndex, AdapterDSource *ads ); + +#endif /* USE_LDAP */ + +#endif /* __EDITLDAP_H__ */ diff --git a/src/editldap_basedn.c b/src/editldap_basedn.c new file mode 100644 index 0000000..2e59b25 --- /dev/null +++ b/src/editldap_basedn.c @@ -0,0 +1,342 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * LDAP Base DN selection dialog. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef USE_LDAP + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prefs_common.h" +#include "syldap.h" +#include "mgutils.h" +#include "gtkutils.h" +#include "manage_window.h" + +static struct _LDAPEdit_basedn { + GtkWidget *window; + GtkWidget *host_label; + GtkWidget *port_label; + GtkWidget *basedn_entry; + GtkWidget *basedn_list; + GtkWidget *hbbox; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *statusbar; + gint status_cid; +} ldapedit_basedn; + +static gboolean ldapedit_basedn_cancelled; +static gboolean ldapedit_basedn_bad_server; + +/* +* Edit functions. +*/ +static void edit_ldap_bdn_status_show( gchar *msg ) { + if( ldapedit_basedn.statusbar != NULL ) { + gtk_statusbar_pop( GTK_STATUSBAR(ldapedit_basedn.statusbar), ldapedit_basedn.status_cid ); + if( msg ) { + gtk_statusbar_push( GTK_STATUSBAR(ldapedit_basedn.statusbar), ldapedit_basedn.status_cid, msg ); + } + } +} + +static gint edit_ldap_bdn_delete_event( GtkWidget *widget, GdkEventAny *event, gboolean *cancelled ) { + ldapedit_basedn_cancelled = TRUE; + gtk_main_quit(); + return TRUE; +} + +static gboolean edit_ldap_bdn_key_pressed( GtkWidget *widget, GdkEventKey *event, gboolean *cancelled ) { + if (event && event->keyval == GDK_Escape) { + ldapedit_basedn_cancelled = TRUE; + gtk_main_quit(); + } + return FALSE; +} + +static void edit_ldap_bdn_ok( GtkWidget *widget, gboolean *cancelled ) { + ldapedit_basedn_cancelled = FALSE; + gtk_main_quit(); +} + +static void edit_ldap_bdn_cancel( GtkWidget *widget, gboolean *cancelled ) { + ldapedit_basedn_cancelled = TRUE; + gtk_main_quit(); +} + +static void edit_ldap_bdn_list_select( GtkCList *clist, gint row, gint column, GdkEvent *event, gpointer data ) { + gchar *text = NULL; + + if( gtk_clist_get_text( clist, row, 0, &text ) ) { + if( text ) { + gtk_entry_set_text(GTK_ENTRY(ldapedit_basedn.basedn_entry), text ); + } + } +} + +static gboolean edit_ldap_bdn_list_button( GtkCList *clist, GdkEventButton *event, gpointer data ) { + if( ! event ) return FALSE; + if( event->button == 1 ) { + if( event->type == GDK_2BUTTON_PRESS ) { + ldapedit_basedn_cancelled = FALSE; + gtk_main_quit(); + } + } + return FALSE; +} + +static void edit_ldap_bdn_create(void) { + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *label; + GtkWidget *host_label; + GtkWidget *port_label; + GtkWidget *basedn_list; + GtkWidget *vlbox; + GtkWidget *lwindow; + GtkWidget *basedn_entry; + GtkWidget *hbbox; + GtkWidget *hsep; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *hsbox; + GtkWidget *statusbar; + gint top; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request(window, 300, 270); + gtk_container_set_border_width(GTK_CONTAINER(window), 0); + gtk_window_set_title(GTK_WINDOW(window), _("Edit LDAP - Select Search Base")); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(edit_ldap_bdn_delete_event), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(edit_ldap_bdn_key_pressed), NULL); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add(GTK_CONTAINER(window), vbox); + gtk_container_set_border_width( GTK_CONTAINER(vbox), 0 ); + + table = gtk_table_new(3, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 8 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + + /* First row */ + top = 0; + label = gtk_label_new(_("Hostname")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + host_label = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), host_label, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(host_label), 0, 0.5); + + /* Second row */ + top = 1; + label = gtk_label_new(_("Port")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + + port_label = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), port_label, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(port_label), 0, 0.5); + + /* Third row */ + top = 2; + label = gtk_label_new(_("Search Base")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + basedn_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), basedn_entry, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* Basedn list */ + vlbox = gtk_vbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(vbox), vlbox, TRUE, TRUE, 0); + gtk_container_set_border_width( GTK_CONTAINER(vlbox), 8 ); + + lwindow = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(lwindow), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + gtk_box_pack_start(GTK_BOX(vlbox), lwindow, TRUE, TRUE, 0); + + basedn_list = gtk_clist_new(1); + gtk_container_add(GTK_CONTAINER(lwindow), basedn_list); + gtk_clist_column_titles_show( GTK_CLIST(basedn_list) ); + gtk_clist_set_column_title( GTK_CLIST(basedn_list), 0, _( "Available Search Base(s)" ) ); + gtk_clist_set_selection_mode(GTK_CLIST(basedn_list), GTK_SELECTION_BROWSE); + gtkut_clist_set_redraw( GTK_CLIST(basedn_list) ); + + /* Status line */ + hsbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, 0); + statusbar = gtk_statusbar_new(); + gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, 0); + + /* Button panel */ + gtkut_stock_button_set_create(&hbbox, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(hbbox), 0 ); + gtk_widget_grab_default(ok_btn); + + hsep = gtk_hseparator_new(); + gtk_box_pack_end(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(edit_ldap_bdn_ok), NULL); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(edit_ldap_bdn_cancel), NULL); + g_signal_connect(G_OBJECT(basedn_list), "select_row", + G_CALLBACK(edit_ldap_bdn_list_select), NULL); + g_signal_connect(G_OBJECT(basedn_list), "button_press_event", + G_CALLBACK(edit_ldap_bdn_list_button), NULL); + + gtk_widget_show_all(vbox); + + ldapedit_basedn.window = window; + ldapedit_basedn.host_label = host_label; + ldapedit_basedn.port_label = port_label; + ldapedit_basedn.basedn_entry = basedn_entry; + ldapedit_basedn.basedn_list = basedn_list; + ldapedit_basedn.hbbox = hbbox; + ldapedit_basedn.ok_btn = ok_btn; + ldapedit_basedn.cancel_btn = cancel_btn; + ldapedit_basedn.statusbar = statusbar; + ldapedit_basedn.status_cid = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "Edit LDAP Select Base DN" ); +} + +void edit_ldap_bdn_load_data( const gchar *hostName, const gint iPort, const gint tov, const gchar* bindDN, + const gchar *bindPW ) { + gchar *sHost; + gchar *sMsg = NULL; + gchar sPort[20]; + gboolean flgConn; + gboolean flgDN; + + edit_ldap_bdn_status_show( "" ); + gtk_clist_clear(GTK_CLIST(ldapedit_basedn.basedn_list)); + ldapedit_basedn_bad_server = TRUE; + flgConn = flgDN = FALSE; + sHost = g_strdup( hostName ); + sprintf( sPort, "%d", iPort ); + gtk_label_set_text(GTK_LABEL(ldapedit_basedn.host_label), hostName); + gtk_label_set_text(GTK_LABEL(ldapedit_basedn.port_label), sPort); + if( *sHost != '\0' ) { + /* Test connection to server */ + if( syldap_test_connect_s( sHost, iPort ) ) { + /* Attempt to read base DN */ + GList *baseDN = syldap_read_basedn_s( sHost, iPort, bindDN, bindPW, tov ); + if( baseDN ) { + GList *node = baseDN; + gchar *text[2] = { NULL, NULL }; + + while( node ) { + text[0] = (gchar *)node->data; + gtk_clist_append(GTK_CLIST(ldapedit_basedn.basedn_list), text); + node = g_list_next( node ); + flgDN = TRUE; + } + mgu_free_dlist( baseDN ); + baseDN = node = NULL; + } + ldapedit_basedn_bad_server = FALSE; + flgConn = TRUE; + } + } + g_free( sHost ); + + /* Display appropriate message */ + if( flgConn ) { + if( ! flgDN ) { + sMsg = _( "Could not read Search Base(s) from server - please set manually" ); + } + } + else { + sMsg = _( "Could not connect to server" ); + } + edit_ldap_bdn_status_show( sMsg ); +} + +gchar *edit_ldap_basedn_selection( const gchar *hostName, const gint port, gchar *baseDN, const gint tov, + const gchar* bindDN, const gchar *bindPW ) { + gchar *retVal = NULL; + + ldapedit_basedn_cancelled = FALSE; + if( ! ldapedit_basedn.window ) edit_ldap_bdn_create(); + gtkut_box_set_reverse_order(GTK_BOX(ldapedit_basedn.hbbox), + !prefs_common.comply_gnome_hig); + gtk_widget_grab_focus(ldapedit_basedn.ok_btn); + gtk_widget_show(ldapedit_basedn.window); + manage_window_set_transient(GTK_WINDOW(ldapedit_basedn.window)); + + edit_ldap_bdn_status_show( "" ); + edit_ldap_bdn_load_data( hostName, port, tov, bindDN, bindPW ); + gtk_widget_show(ldapedit_basedn.window); + + gtk_entry_set_text(GTK_ENTRY(ldapedit_basedn.basedn_entry), baseDN); + + gtk_main(); + gtk_widget_hide(ldapedit_basedn.window); + if( ldapedit_basedn_cancelled ) return NULL; + if( ldapedit_basedn_bad_server ) return NULL; + + retVal = gtk_editable_get_chars( GTK_EDITABLE(ldapedit_basedn.basedn_entry), 0, -1 ); + g_strchomp( retVal ); g_strchug( retVal ); + if( *retVal == '\0' ) { + g_free( retVal ); + retVal = NULL; + } + return retVal; +} + +#endif /* USE_LDAP */ + +/* +* End of Source. +*/ + diff --git a/src/editldap_basedn.h b/src/editldap_basedn.h new file mode 100644 index 0000000..a4affd0 --- /dev/null +++ b/src/editldap_basedn.h @@ -0,0 +1,30 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EDITLDAP_BASEDN_H__ +#define __EDITLDAP_BASEDN_H__ + +#ifdef USE_LDAP + +gchar *edit_ldap_basedn_selection( const gchar *hostName, const gint port, gchar *baseDN, const gint tov, + const gchar* bindDN, const gchar *bindPW ); + +#endif /* USE_LDAP */ + +#endif /* __EDITLDAP_BASEDN_H__ */ diff --git a/src/editvcard.c b/src/editvcard.c new file mode 100644 index 0000000..b2b020d --- /dev/null +++ b/src/editvcard.c @@ -0,0 +1,309 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Edit vCard address book data. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "addressbook.h" +#include "prefs_common.h" +#include "addressitem.h" +#include "vcard.h" +#include "mgutils.h" +#include "filesel.h" +#include "gtkutils.h" +#include "codeconv.h" +#include "manage_window.h" + +#define ADDRESSBOOK_GUESS_VCARD "MyGnomeCard" + +static struct _VCardEdit { + GtkWidget *window; + GtkWidget *name_entry; + GtkWidget *file_entry; + GtkWidget *hbbox; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *statusbar; + gint status_cid; +} vcardedit; + +/* +* Edit functions. +*/ +void edit_vcard_status_show( gchar *msg ) { + if( vcardedit.statusbar != NULL ) { + gtk_statusbar_pop( GTK_STATUSBAR(vcardedit.statusbar), vcardedit.status_cid ); + if( msg ) { + gtk_statusbar_push( GTK_STATUSBAR(vcardedit.statusbar), vcardedit.status_cid, msg ); + } + } +} + +static void edit_vcard_ok( GtkWidget *widget, gboolean *cancelled ) { + *cancelled = FALSE; + gtk_main_quit(); +} + +static void edit_vcard_cancel( GtkWidget *widget, gboolean *cancelled ) { + *cancelled = TRUE; + gtk_main_quit(); +} + +static void edit_vcard_file_check( void ) { + gint t; + gchar *sFile; + gchar *sFSFile; + gchar *sMsg; + + sFile = gtk_editable_get_chars( GTK_EDITABLE(vcardedit.file_entry), 0, -1 ); + sFSFile = conv_filename_from_utf8( sFile ); + t = vcard_test_read_file( sFSFile ); + g_free( sFSFile ); + g_free( sFile ); + if( t == MGU_SUCCESS ) { + sMsg = ""; + } + else if( t == MGU_BAD_FORMAT ) { + sMsg = _("File does not appear to be vCard format."); + } + else { + sMsg = _("Could not read file."); + } + edit_vcard_status_show( sMsg ); +} + +static void edit_vcard_file_select( void ) { + gchar *sFile; + gchar *sUTF8File; + + sFile = filesel_select_file( _("Select vCard File"), NULL, GTK_FILE_CHOOSER_ACTION_OPEN ); + + if( sFile ) { + sUTF8File = conv_filename_to_utf8( sFile ); + gtk_entry_set_text( GTK_ENTRY(vcardedit.file_entry), sUTF8File ); + g_free( sUTF8File ); + g_free( sFile ); + edit_vcard_file_check(); + } +} + +static gint edit_vcard_delete_event( GtkWidget *widget, GdkEventAny *event, gboolean *cancelled ) { + *cancelled = TRUE; + gtk_main_quit(); + return TRUE; +} + +static gboolean edit_vcard_key_pressed( GtkWidget *widget, GdkEventKey *event, gboolean *cancelled ) { + if (event && event->keyval == GDK_Escape) { + *cancelled = TRUE; + gtk_main_quit(); + } + return FALSE; +} + +static void addressbook_edit_vcard_create( gboolean *cancelled ) { + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *label; + GtkWidget *name_entry; + GtkWidget *file_entry; + GtkWidget *hbbox; + GtkWidget *hsep; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *check_btn; + GtkWidget *file_btn; + GtkWidget *statusbar; + GtkWidget *hsbox; + gint top; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request(window, 450, -1); + gtk_container_set_border_width( GTK_CONTAINER(window), 0 ); + gtk_window_set_title(GTK_WINDOW(window), _("Edit vCard Entry")); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(edit_vcard_delete_event), + cancelled); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(edit_vcard_key_pressed), + cancelled); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add(GTK_CONTAINER(window), vbox); + gtk_container_set_border_width( GTK_CONTAINER(vbox), 0 ); + + table = gtk_table_new(2, 3, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 8 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8 ); + + /* First row */ + top = 0; + label = gtk_label_new(_("Name")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + name_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), name_entry, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + check_btn = gtk_button_new_with_label( _(" Check File ")); + gtk_table_attach(GTK_TABLE(table), check_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0); + + /* Second row */ + top = 1; + label = gtk_label_new(_("File")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + file_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), file_entry, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + file_btn = gtk_button_new_with_label( _(" ... ")); + gtk_table_attach(GTK_TABLE(table), file_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0); + + /* Status line */ + hsbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, 0); + statusbar = gtk_statusbar_new(); + gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, 0); + + /* Button panel */ + gtkut_stock_button_set_create(&hbbox, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(hbbox), 0 ); + gtk_widget_grab_default(ok_btn); + + hsep = gtk_hseparator_new(); + gtk_box_pack_end(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(edit_vcard_ok), cancelled); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(edit_vcard_cancel), cancelled); + g_signal_connect(G_OBJECT(file_btn), "clicked", + G_CALLBACK(edit_vcard_file_select), NULL); + g_signal_connect(G_OBJECT(check_btn), "clicked", + G_CALLBACK(edit_vcard_file_check), NULL); + + gtk_widget_show_all(vbox); + + vcardedit.window = window; + vcardedit.name_entry = name_entry; + vcardedit.file_entry = file_entry; + vcardedit.hbbox = hbbox; + vcardedit.ok_btn = ok_btn; + vcardedit.cancel_btn = cancel_btn; + vcardedit.statusbar = statusbar; + vcardedit.status_cid = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "Edit vCard Dialog" ); +} + +AdapterDSource *addressbook_edit_vcard( AddressIndex *addrIndex, AdapterDSource *ads ) { + static gboolean cancelled; + gchar *sName; + gchar *sFile; + gchar *sFSFile; + AddressDataSource *ds = NULL; + VCardFile *vcf = NULL; + gboolean fin; + + if( ! vcardedit.window ) + addressbook_edit_vcard_create(&cancelled); + gtkut_box_set_reverse_order(GTK_BOX(vcardedit.hbbox), + !prefs_common.comply_gnome_hig); + gtk_widget_grab_focus(vcardedit.ok_btn); + gtk_widget_grab_focus(vcardedit.name_entry); + gtk_widget_show(vcardedit.window); + manage_window_set_transient(GTK_WINDOW(vcardedit.window)); + + edit_vcard_status_show( "" ); + if( ads ) { + ds = ads->dataSource; + vcf = ds->rawDataSource; + if (vcf->name) + gtk_entry_set_text(GTK_ENTRY(vcardedit.name_entry), vcf->name); + if (vcf->path) + gtk_entry_set_text(GTK_ENTRY(vcardedit.file_entry), vcf->path); + gtk_window_set_title( GTK_WINDOW(vcardedit.window), _("Edit vCard Entry")); + } + else { + gtk_entry_set_text(GTK_ENTRY(vcardedit.name_entry), ADDRESSBOOK_GUESS_VCARD ); + gtk_entry_set_text(GTK_ENTRY(vcardedit.file_entry), vcard_find_gnomecard() ); + gtk_window_set_title( GTK_WINDOW(vcardedit.window), _("Add New vCard Entry")); + } + + gtk_main(); + gtk_widget_hide(vcardedit.window); + if (cancelled == TRUE) return NULL; + + fin = FALSE; + sName = gtk_editable_get_chars( GTK_EDITABLE(vcardedit.name_entry), 0, -1 ); + sFile = gtk_editable_get_chars( GTK_EDITABLE(vcardedit.file_entry), 0, -1 ); + sFSFile = conv_filename_from_utf8( sFile ); + if( *sName == '\0' ) fin = TRUE; + if( *sFile == '\0' ) fin = TRUE; + if( ! sFSFile || *sFSFile == '\0' ) fin = TRUE; + + if( ! fin ) { + if( ! ads ) { + vcf = vcard_create(); + ds = addrindex_index_add_datasource( addrIndex, ADDR_IF_VCARD, vcf ); + ads = addressbook_create_ds_adapter( ds, ADDR_VCARD, NULL ); + } + addressbook_ads_set_name( ads, sName ); + vcard_set_name( vcf, sName ); + vcard_set_file( vcf, sFSFile ); + } + g_free( sFSFile ); + g_free( sFile ); + g_free( sName ); + + return ads; +} + +/* +* End of Source. +*/ + diff --git a/src/editvcard.h b/src/editvcard.h new file mode 100644 index 0000000..1dd0979 --- /dev/null +++ b/src/editvcard.h @@ -0,0 +1,29 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Edit vCard address book data. + */ + +#ifndef __EDITVCARD_H__ +#define __EDITVCARD_H__ + +AdapterDSource *addressbook_edit_vcard( AddressIndex *addrIndex, AdapterDSource *ads ); + +#endif /* __EDITVCARD_H__ */ diff --git a/src/eggtrayicon.c b/src/eggtrayicon.c new file mode 100644 index 0000000..d913736 --- /dev/null +++ b/src/eggtrayicon.c @@ -0,0 +1,583 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* eggtrayicon.c + * Copyright (C) 2002 Anders Carlsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* Added WINDOWING ifdef 2005-07-09 by Yoichi Imai */ + +#include + +#include "eggtrayicon.h" + +#if !GTK_CHECK_VERSION(2, 10, 0) && defined(GDK_WINDOWING_X11) +#include +#include +#include +#include + +#ifndef EGG_COMPILATION +#ifndef _ +#define _(x) dgettext (GETTEXT_PACKAGE, x) +#define N_(x) x +#endif +#else +#define _(x) x +#define N_(x) x +#endif + +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +#define SYSTEM_TRAY_ORIENTATION_HORZ 0 +#define SYSTEM_TRAY_ORIENTATION_VERT 1 + +enum { + PROP_0, + PROP_ORIENTATION +}; + +static GtkPlugClass *parent_class = NULL; + +static void egg_tray_icon_init (EggTrayIcon *icon); +static void egg_tray_icon_class_init (EggTrayIconClass *klass); + +static void egg_tray_icon_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void egg_tray_icon_realize (GtkWidget *widget); +static void egg_tray_icon_unrealize (GtkWidget *widget); + +static void egg_tray_icon_add (GtkContainer *container, + GtkWidget *widget); + +static void egg_tray_icon_update_manager_window (EggTrayIcon *icon, + gboolean dock_if_realized); +static void egg_tray_icon_manager_window_destroyed (EggTrayIcon *icon); + +GType +egg_tray_icon_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + { + our_type = g_type_from_name("EggTrayIcon"); + + if (our_type == 0) + { + static const GTypeInfo our_info = + { + sizeof (EggTrayIconClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) egg_tray_icon_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (EggTrayIcon), + 0, /* n_preallocs */ + (GInstanceInitFunc) egg_tray_icon_init, + NULL /* value_table */ + }; + + our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, 0); + } + else if (parent_class == NULL) + { + /* we're reheating the old class from a previous instance - engage ugly hack =( */ + egg_tray_icon_class_init((EggTrayIconClass *)g_type_class_peek(our_type)); + } + } + + return our_type; +} + +static void +egg_tray_icon_init (EggTrayIcon *icon) +{ + icon->stamp = 1; + icon->orientation = GTK_ORIENTATION_HORIZONTAL; + + gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK); +} + +static void +egg_tray_icon_class_init (EggTrayIconClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *)klass; + GtkWidgetClass *widget_class = (GtkWidgetClass *)klass; + GtkContainerClass *container_class = (GtkContainerClass *)klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->get_property = egg_tray_icon_get_property; + + widget_class->realize = egg_tray_icon_realize; + widget_class->unrealize = egg_tray_icon_unrealize; + + container_class->add = egg_tray_icon_add; + + g_object_class_install_property (gobject_class, + PROP_ORIENTATION, + g_param_spec_enum ("orientation", + _("Orientation"), + _("The orientation of the tray."), + GTK_TYPE_ORIENTATION, + GTK_ORIENTATION_HORIZONTAL, + G_PARAM_READABLE)); +} + +static void +egg_tray_icon_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EggTrayIcon *icon = EGG_TRAY_ICON (object); + + switch (prop_id) + { + case PROP_ORIENTATION: + g_value_set_enum (value, icon->orientation); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +egg_tray_icon_get_orientation_property (EggTrayIcon *icon) +{ + Display *xdisplay; + Atom type; + int format; + union { + gulong *prop; + guchar *prop_ch; + } prop = { NULL }; + gulong nitems; + gulong bytes_after; + int error, result; + + g_return_if_fail(icon->manager_window != None); + +#if GTK_CHECK_VERSION(2,1,0) + xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); +#else + xdisplay = gdk_display; +#endif + + gdk_error_trap_push (); + type = None; + result = XGetWindowProperty (xdisplay, + icon->manager_window, + icon->orientation_atom, + 0, G_MAXLONG, FALSE, + XA_CARDINAL, + &type, &format, &nitems, + &bytes_after, &(prop.prop_ch)); + error = gdk_error_trap_pop (); + + if (error || result != Success) + return; + + if (type == XA_CARDINAL) + { + GtkOrientation orientation; + + orientation = (prop.prop [0] == SYSTEM_TRAY_ORIENTATION_HORZ) ? + GTK_ORIENTATION_HORIZONTAL : + GTK_ORIENTATION_VERTICAL; + + if (icon->orientation != orientation) + { + icon->orientation = orientation; + + g_object_notify (G_OBJECT (icon), "orientation"); + } + } + + if (prop.prop) + XFree (prop.prop); +} + +static GdkFilterReturn +egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_data) +{ + EggTrayIcon *icon = user_data; + XEvent *xev = (XEvent *)xevent; + + if (xev->xany.type == ClientMessage && + xev->xclient.message_type == icon->manager_atom && + xev->xclient.data.l[1] == icon->selection_atom) + { + egg_tray_icon_update_manager_window (icon, TRUE); + } + else if (xev->xany.window == icon->manager_window) + { + if (xev->xany.type == PropertyNotify && + xev->xproperty.atom == icon->orientation_atom) + { + egg_tray_icon_get_orientation_property (icon); + } + if (xev->xany.type == DestroyNotify) + { + egg_tray_icon_manager_window_destroyed (icon); + } + } + + return GDK_FILTER_CONTINUE; +} + +static void +egg_tray_icon_unrealize (GtkWidget *widget) +{ + EggTrayIcon *icon = EGG_TRAY_ICON (widget); + GdkWindow *root_window; + + if (icon->manager_window != None) + { + GdkWindow *gdkwin; + +#if GTK_CHECK_VERSION(2,1,0) + gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget), + icon->manager_window); +#else + gdkwin = gdk_window_lookup (icon->manager_window); +#endif + + gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon); + } + +#if GTK_CHECK_VERSION(2,1,0) + root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget)); +#else + root_window = gdk_window_lookup (gdk_x11_get_default_root_xwindow ()); +#endif + + gdk_window_remove_filter (root_window, egg_tray_icon_manager_filter, icon); + + if (GTK_WIDGET_CLASS (parent_class)->unrealize) + (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); +} + +static void +egg_tray_icon_send_manager_message (EggTrayIcon *icon, + long message, + Window window, + long data1, + long data2, + long data3) +{ + XClientMessageEvent ev; + Display *display; + + ev.type = ClientMessage; + ev.window = window; + ev.message_type = icon->system_tray_opcode_atom; + ev.format = 32; + ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window); + ev.data.l[1] = message; + ev.data.l[2] = data1; + ev.data.l[3] = data2; + ev.data.l[4] = data3; + +#if GTK_CHECK_VERSION(2,1,0) + display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); +#else + display = gdk_display; +#endif + + gdk_error_trap_push (); + XSendEvent (display, + icon->manager_window, False, NoEventMask, (XEvent *)&ev); + XSync (display, False); + gdk_error_trap_pop (); +} + +static void +egg_tray_icon_send_dock_request (EggTrayIcon *icon) +{ + egg_tray_icon_send_manager_message (icon, + SYSTEM_TRAY_REQUEST_DOCK, + icon->manager_window, + gtk_plug_get_id (GTK_PLUG (icon)), + 0, 0); +} + +static void +egg_tray_icon_update_manager_window (EggTrayIcon *icon, + gboolean dock_if_realized) +{ + Display *xdisplay; + + if (icon->manager_window != None) + return; + +#if GTK_CHECK_VERSION(2,1,0) + xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); +#else + xdisplay = gdk_display; +#endif + + XGrabServer (xdisplay); + + icon->manager_window = XGetSelectionOwner (xdisplay, + icon->selection_atom); + + if (icon->manager_window != None) + XSelectInput (xdisplay, + icon->manager_window, StructureNotifyMask|PropertyChangeMask); + + XUngrabServer (xdisplay); + XFlush (xdisplay); + + if (icon->manager_window != None) + { + GdkWindow *gdkwin; + +#if GTK_CHECK_VERSION(2,1,0) + gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)), + icon->manager_window); +#else + gdkwin = gdk_window_lookup (icon->manager_window); +#endif + + gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon); + + if (dock_if_realized && GTK_WIDGET_REALIZED (icon)) + egg_tray_icon_send_dock_request (icon); + + egg_tray_icon_get_orientation_property (icon); + } +} + +static gboolean +transparent_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) +{ + gdk_window_clear_area (widget->window, event->area.x, event->area.y, + event->area.width, event->area.height); + return FALSE; +} + +static void +make_transparent_again (GtkWidget *widget, GtkStyle *previous_style, + gpointer user_data) +{ + gdk_window_set_back_pixmap (widget->window, NULL, TRUE); +} + +static void +make_transparent (GtkWidget *widget, gpointer user_data) +{ + if (GTK_WIDGET_NO_WINDOW (widget) || GTK_WIDGET_APP_PAINTABLE (widget)) + return; + + gtk_widget_set_app_paintable (widget, TRUE); + gtk_widget_set_double_buffered (widget, FALSE); + gdk_window_set_back_pixmap (widget->window, NULL, TRUE); + g_signal_connect (widget, "expose_event", + G_CALLBACK (transparent_expose_event), NULL); + g_signal_connect_after (widget, "style_set", + G_CALLBACK (make_transparent_again), NULL); +} + +static void +egg_tray_icon_add (GtkContainer *container, GtkWidget *widget) +{ + g_signal_connect (widget, "realize", + G_CALLBACK (make_transparent), NULL); + GTK_CONTAINER_CLASS (parent_class)->add (container, widget); +} + +static void +egg_tray_icon_manager_window_destroyed (EggTrayIcon *icon) +{ + GdkWindow *gdkwin; + + g_return_if_fail (icon->manager_window != None); + +#if GTK_CHECK_VERSION(2,1,0) + gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)), + icon->manager_window); +#else + gdkwin = gdk_window_lookup (icon->manager_window); +#endif + + gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon); + + icon->manager_window = None; + + egg_tray_icon_update_manager_window (icon, TRUE); +} + +static void +egg_tray_icon_realize (GtkWidget *widget) +{ + EggTrayIcon *icon = EGG_TRAY_ICON (widget); + gint screen; + Display *xdisplay; + char buffer[256]; + GdkWindow *root_window; + + if (GTK_WIDGET_CLASS (parent_class)->realize) + GTK_WIDGET_CLASS (parent_class)->realize (widget); + + make_transparent (widget, NULL); + +#if GTK_CHECK_VERSION(2,1,0) + screen = gdk_screen_get_number (gtk_widget_get_screen (widget)); + xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget)); +#else + screen = XScreenNumberOfScreen (DefaultScreenOfDisplay (gdk_display)); + xdisplay = gdk_display; +#endif + + /* Now see if there's a manager window around */ + g_snprintf (buffer, sizeof (buffer), + "_NET_SYSTEM_TRAY_S%d", + screen); + + icon->selection_atom = XInternAtom (xdisplay, buffer, False); + + icon->manager_atom = XInternAtom (xdisplay, "MANAGER", False); + + icon->system_tray_opcode_atom = XInternAtom (xdisplay, + "_NET_SYSTEM_TRAY_OPCODE", + False); + + icon->orientation_atom = XInternAtom (xdisplay, + "_NET_SYSTEM_TRAY_ORIENTATION", + False); + + egg_tray_icon_update_manager_window (icon, FALSE); + egg_tray_icon_send_dock_request (icon); + +#if GTK_CHECK_VERSION(2,1,0) + root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget)); +#else + root_window = gdk_window_lookup (gdk_x11_get_default_root_xwindow ()); +#endif + + /* Add a root window filter so that we get changes on MANAGER */ + gdk_window_add_filter (root_window, + egg_tray_icon_manager_filter, icon); +} + +#if GTK_CHECK_VERSION(2,1,0) +EggTrayIcon * +egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name) +{ + g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); + + return g_object_new (EGG_TYPE_TRAY_ICON, "screen", screen, "title", name, NULL); +} +#endif + +EggTrayIcon* +egg_tray_icon_new (const gchar *name) +{ + return g_object_new (EGG_TYPE_TRAY_ICON, "title", name, NULL); +} + +guint +egg_tray_icon_send_message (EggTrayIcon *icon, + gint timeout, + const gchar *message, + gint len) +{ + guint stamp; + + g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0); + g_return_val_if_fail (timeout >= 0, 0); + g_return_val_if_fail (message != NULL, 0); + + if (icon->manager_window == None) + return 0; + + if (len < 0) + len = strlen (message); + + stamp = icon->stamp++; + + /* Get ready to send the message */ + egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE, + (Window)gtk_plug_get_id (GTK_PLUG (icon)), + timeout, len, stamp); + + /* Now to send the actual message */ + gdk_error_trap_push (); + while (len > 0) + { + XClientMessageEvent ev; + Display *xdisplay; + +#if GTK_CHECK_VERSION(2,1,0) + xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); +#else + xdisplay = gdk_display; +#endif + + ev.type = ClientMessage; + ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon)); + ev.format = 8; + ev.message_type = XInternAtom (xdisplay, + "_NET_SYSTEM_TRAY_MESSAGE_DATA", False); + if (len > 20) + { + memcpy (&ev.data, message, 20); + len -= 20; + message += 20; + } + else + { + memcpy (&ev.data, message, len); + len = 0; + } + + XSendEvent (xdisplay, + icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev); + XSync (xdisplay, False); + } + gdk_error_trap_pop (); + + return stamp; +} + +void +egg_tray_icon_cancel_message (EggTrayIcon *icon, + guint id) +{ + g_return_if_fail (EGG_IS_TRAY_ICON (icon)); + g_return_if_fail (id > 0); + + egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE, + (Window)gtk_plug_get_id (GTK_PLUG (icon)), + id, 0, 0); +} + +GtkOrientation +egg_tray_icon_get_orientation (EggTrayIcon *icon) +{ + g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL); + + return icon->orientation; +} +#endif diff --git a/src/eggtrayicon.h b/src/eggtrayicon.h new file mode 100644 index 0000000..c0abfc0 --- /dev/null +++ b/src/eggtrayicon.h @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* eggtrayicon.h + * Copyright (C) 2002 Anders Carlsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EGG_TRAY_ICON_H__ +#define __EGG_TRAY_ICON_H__ + +#include +#include + +#if !GTK_CHECK_VERSION(2, 10, 0) && defined(GDK_WINDOWING_X11) +#include +#include + +G_BEGIN_DECLS + +#define EGG_TYPE_TRAY_ICON (egg_tray_icon_get_type ()) +#define EGG_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TRAY_ICON, EggTrayIcon)) +#define EGG_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_TRAY_ICON, EggTrayIconClass)) +#define EGG_IS_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TRAY_ICON)) +#define EGG_IS_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TRAY_ICON)) +#define EGG_TRAY_ICON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TRAY_ICON, EggTrayIconClass)) + +typedef struct _EggTrayIcon EggTrayIcon; +typedef struct _EggTrayIconClass EggTrayIconClass; + +struct _EggTrayIcon +{ + GtkPlug parent_instance; + + guint stamp; + + Atom selection_atom; + Atom manager_atom; + Atom system_tray_opcode_atom; + Atom orientation_atom; + Window manager_window; + + GtkOrientation orientation; +}; + +struct _EggTrayIconClass +{ + GtkPlugClass parent_class; +}; + +GType egg_tray_icon_get_type (void); + +#if GTK_CHECK_VERSION(2,1,0) +EggTrayIcon *egg_tray_icon_new_for_screen (GdkScreen *screen, + const gchar *name); +#endif + +EggTrayIcon *egg_tray_icon_new (const gchar *name); + +guint egg_tray_icon_send_message (EggTrayIcon *icon, + gint timeout, + const char *message, + gint len); +void egg_tray_icon_cancel_message (EggTrayIcon *icon, + guint id); + +GtkOrientation egg_tray_icon_get_orientation (EggTrayIcon *icon); + +G_END_DECLS + +#endif /* GDK_WINDOWING_X11 */ +#endif /* __EGG_TRAY_ICON_H__ */ diff --git a/src/export.c b/src/export.c new file mode 100644 index 0000000..85a27e9 --- /dev/null +++ b/src/export.c @@ -0,0 +1,551 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "inc.h" +#include "mbox.h" +#include "folder.h" +#include "procmsg.h" +#include "menu.h" +#include "filesel.h" +#include "foldersel.h" +#include "gtkutils.h" +#include "manage_window.h" +#include "folder.h" +#include "utils.h" +#include "progressdialog.h" +#include "alertpanel.h" +#include "mainwindow.h" +#include "summaryview.h" +#include "prefs_ui.h" + +enum +{ + EXPORT_MBOX, + EXPORT_EML, + EXPORT_MH +}; + +static GtkWidget *window; +static GtkWidget *format_optmenu; +static GtkWidget *desc_label; +static GtkWidget *file_label; +static GtkWidget *src_entry; +static GtkWidget *file_entry; +static GtkWidget *src_button; +static GtkWidget *file_button; +static GtkWidget *selected_only_chkbtn; +static GtkWidget *ok_button; +static GtkWidget *cancel_button; +static gboolean export_finished; +static gboolean export_ack; +static ProgressDialog *progress; + +static gboolean progress_cancel = FALSE; + +static void export_create (void); +static gint export_do (void); +static gint export_eml (FolderItem *src, + GSList *sel_mlist, + const gchar *path, + gint type); + +static void export_format_menu_cb (GtkWidget *widget, + gpointer data); + +static void export_ok_cb (GtkWidget *widget, + gpointer data); +static void export_cancel_cb (GtkWidget *widget, + gpointer data); +static void export_srcsel_cb (GtkWidget *widget, + gpointer data); +static void export_filesel_cb (GtkWidget *widget, + gpointer data); +static gint delete_event (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); + +static void export_progress_cancel_cb (GtkWidget *widget, + gpointer data); + + +static gboolean export_mbox_func(Folder *folder, FolderItem *item, guint count, guint total, gpointer data) +{ + gchar str[64]; + static GTimeVal tv_prev = {0, 0}; + GTimeVal tv_cur; + + g_get_current_time(&tv_cur); + g_snprintf(str, sizeof(str), "%u / %d", count, total); + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress->progressbar), str); + + if (tv_prev.tv_sec == 0 || + (tv_cur.tv_sec - tv_prev.tv_sec) * G_USEC_PER_SEC + + tv_cur.tv_usec - tv_prev.tv_usec > 100 * 1000) { + if (item->total > 0) + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress->progressbar), (gdouble)count / item->total); + else + gtk_progress_bar_pulse(GTK_PROGRESS_BAR(progress->progressbar)); + ui_update(); + tv_prev = tv_cur; + } + + if (progress_cancel) + return FALSE; + else + return TRUE; +} + +gint export_mail(FolderItem *default_src) +{ + gint ok = 0; + gchar *src_id = NULL; + + export_create(); + + change_dir(get_startup_dir()); + + if (default_src && default_src->path) + src_id = folder_item_get_identifier(default_src); + + if (src_id) { + gtk_entry_set_text(GTK_ENTRY(src_entry), src_id); + g_free(src_id); + } + gtk_widget_grab_focus(file_entry); + + manage_window_set_transient(GTK_WINDOW(window)); + + export_finished = FALSE; + export_ack = FALSE; + + inc_lock(); + + while (!export_finished) + gtk_main_iteration(); + + if (export_ack) + ok = export_do(); + + gtk_widget_destroy(window); + window = NULL; + src_entry = file_entry = NULL; + src_button = file_button = ok_button = cancel_button = NULL; + + inc_unlock(); + + return ok; +} + +static gint export_do(void) +{ + gint ok = 0; + const gchar *srcdir, *utf8mbox; + FolderItem *src; + gchar *mbox; + gchar *msg; + gint type; + gboolean selected_only; + MainWindow *mainwin; + GSList *mlist = NULL; + + type = menu_get_option_menu_active_index + (GTK_OPTION_MENU(format_optmenu)); + + srcdir = gtk_entry_get_text(GTK_ENTRY(src_entry)); + utf8mbox = gtk_entry_get_text(GTK_ENTRY(file_entry)); + + if (!utf8mbox || !*utf8mbox) + return -1; + + mbox = g_filename_from_utf8(utf8mbox, -1, NULL, NULL, NULL); + if (!mbox) { + g_warning("failed to convert character set."); + mbox = g_strdup(utf8mbox); + } + + selected_only = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(selected_only_chkbtn)); + + if (selected_only) { + mainwin = main_window_get(); + src = mainwin->summaryview->folder_item; + mlist = summary_get_selected_msg_list(mainwin->summaryview); + } else + src = folder_find_item_from_identifier(srcdir); + + if (!src) { + g_warning("Can't find the folder."); + g_free(mbox); + return -1; + } + + msg = g_strdup_printf(_("Exporting %s ..."), src->name); + progress = progress_dialog_simple_create(); + gtk_window_set_title(GTK_WINDOW(progress->window), _("Exporting")); + progress_dialog_set_label(progress, msg); + g_free(msg); + gtk_window_set_modal(GTK_WINDOW(progress->window), TRUE); + manage_window_set_transient(GTK_WINDOW(progress->window)); + g_signal_connect(G_OBJECT(progress->cancel_btn), "clicked", + G_CALLBACK(export_progress_cancel_cb), NULL); + g_signal_connect(G_OBJECT(progress->window), "delete_event", + G_CALLBACK(gtk_true), NULL); + gtk_widget_show(progress->window); + ui_update(); + + progress_cancel = FALSE; + + if (type == EXPORT_MBOX) { + folder_set_ui_func2(src->folder, export_mbox_func, NULL); + if (mlist) + ok = export_msgs_to_mbox(src, mlist, mbox); + else + ok = export_to_mbox(src, mbox); + folder_set_ui_func2(src->folder, NULL, NULL); + } else if (type == EXPORT_EML || type == EXPORT_MH) { + ok = export_eml(src, mlist, mbox, type); + } + + progress_dialog_destroy(progress); + progress = NULL; + + if (mlist) + g_slist_free(mlist); + g_free(mbox); + + if (ok == -1) + alertpanel_error(_("Error occurred on export.")); + + return ok; +} + +static gint export_eml(FolderItem *src, GSList *sel_mlist, const gchar *path, + gint type) +{ + const gchar *ext = ""; + GSList *mlist, *cur; + MsgInfo *msginfo; + gchar *file, *dest; + guint count = 0; + guint total; + gint ok = 0; + + g_return_val_if_fail(src != NULL, -1); + g_return_val_if_fail(path != NULL, -1); + + if (type == EXPORT_EML) + ext = ".eml"; + + if (!g_file_test(path, G_FILE_TEST_IS_DIR)) { + if (!g_file_test(path, G_FILE_TEST_EXISTS)) { + make_dir_hier(path); + if (!g_file_test(path, G_FILE_TEST_IS_DIR)) + return -1; + } else { + g_warning("export_eml(): directory %s already exists.", + path); + return -1; + } + } + + if (sel_mlist) + mlist = sel_mlist; + else { + mlist = folder_item_get_msg_list(src, TRUE); + if (!mlist) + return 0; + } + total = g_slist_length(mlist); + + for (cur = mlist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + + count++; + if (export_mbox_func(src->folder, src, count, total, NULL) == FALSE) { + ok = -2; + break; + } + + file = folder_item_fetch_msg(src, msginfo->msgnum); + if (!file) { + ok = -1; + break; + } + dest = g_strdup_printf("%s%c%u%s", path, G_DIR_SEPARATOR, + count, ext); + if (g_file_test(dest, G_FILE_TEST_EXISTS)) { + g_warning("export_eml(): %s already exists.", dest); + g_free(dest); + g_free(file); + ok = -1; + break; + } + if (copy_file(file, dest, FALSE) < 0) { + g_free(dest); + g_free(file); + ok = -1; + break; + } + g_free(dest); + g_free(file); + } + + if (!sel_mlist) + procmsg_msg_list_free(mlist); + + return ok; +} + +static void export_create(void) +{ + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *menu; + GtkWidget *menuitem; + GtkWidget *format_label; + GtkWidget *src_label; + GtkWidget *confirm_area; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), _("Export")); + gtk_container_set_border_width(GTK_CONTAINER(window), 5); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, FALSE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(delete_event), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + + vbox = gtk_vbox_new(FALSE, 4); + gtk_container_add(GTK_CONTAINER(window), vbox); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 4); + + desc_label = gtk_label_new + (_("Specify source folder and destination file.")); + gtk_box_pack_start(GTK_BOX(hbox), desc_label, FALSE, FALSE, 0); + + table = gtk_table_new(2, 3, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(table), 8); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + gtk_widget_set_size_request(table, 420 * gtkut_get_dpi_multiplier(), -1); + + format_label = gtk_label_new(_("File format:")); + gtk_table_attach(GTK_TABLE(table), format_label, 0, 1, 0, 1, + GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(format_label), 1, 0.5); + + src_label = gtk_label_new(_("Source folder:")); + gtk_table_attach(GTK_TABLE(table), src_label, 0, 1, 1, 2, + GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(src_label), 1, 0.5); + + file_label = gtk_label_new(_("Destination:")); + gtk_table_attach(GTK_TABLE(table), file_label, 0, 1, 2, 3, + GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(file_label), 1, 0.5); + + format_optmenu = gtk_option_menu_new(); + gtk_table_attach(GTK_TABLE(table), format_optmenu, 1, 2, 0, 1, + GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + menu = gtk_menu_new(); + MENUITEM_ADD(menu, menuitem, _("UNIX mbox"), EXPORT_MBOX); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(export_format_menu_cb), NULL); + MENUITEM_ADD(menu, menuitem, _("eml (number + .eml)"), EXPORT_EML); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(export_format_menu_cb), NULL); + MENUITEM_ADD(menu, menuitem, _("MH (number only)"), EXPORT_MH); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(export_format_menu_cb), NULL); + + gtk_option_menu_set_menu(GTK_OPTION_MENU(format_optmenu), menu); + + src_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), src_entry, 1, 2, 1, 2, + GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + file_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), file_entry, 1, 2, 2, 3, + GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + src_button = gtk_button_new_with_label(_(" Select... ")); + gtk_table_attach(GTK_TABLE(table), src_button, 2, 3, 1, 2, + 0, 0, 0, 0); + g_signal_connect(G_OBJECT(src_button), "clicked", + G_CALLBACK(export_srcsel_cb), NULL); + + file_button = gtk_button_new_with_label(_(" Select... ")); + gtk_table_attach(GTK_TABLE(table), file_button, 2, 3, 2, 3, + 0, 0, 0, 0); + g_signal_connect(G_OBJECT(file_button), "clicked", + G_CALLBACK(export_filesel_cb), NULL); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 4); + + selected_only_chkbtn = gtk_check_button_new_with_label + (_("Export only selected messages")); + gtk_box_pack_start(GTK_BOX(hbox), selected_only_chkbtn, + FALSE, FALSE, 0); + + SET_TOGGLE_SENSITIVITY_REV(selected_only_chkbtn, src_entry); + SET_TOGGLE_SENSITIVITY_REV(selected_only_chkbtn, src_button); + + gtkut_stock_button_set_create(&confirm_area, + &ok_button, GTK_STOCK_OK, + &cancel_button, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_button); + + g_signal_connect(G_OBJECT(ok_button), "clicked", + G_CALLBACK(export_ok_cb), NULL); + g_signal_connect(G_OBJECT(cancel_button), "clicked", + G_CALLBACK(export_cancel_cb), NULL); + + gtk_widget_show_all(window); +} + +static void export_format_menu_cb(GtkWidget *widget, gpointer data) +{ + gint type; + + type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), + MENU_VAL_ID)); + if (type == EXPORT_MBOX) { + gtk_label_set_text(GTK_LABEL(desc_label), + _("Specify source folder and destination file.")); + } else { + gtk_label_set_text(GTK_LABEL(desc_label), + _("Specify source folder and destination folder.")); + } +} + +static void export_ok_cb(GtkWidget *widget, gpointer data) +{ + export_finished = TRUE; + export_ack = TRUE; +} + +static void export_cancel_cb(GtkWidget *widget, gpointer data) +{ + export_finished = TRUE; + export_ack = FALSE; +} + +static void export_filesel_cb(GtkWidget *widget, gpointer data) +{ + gchar *filename; + gchar *utf8_filename; + gint type; + + type = menu_get_option_menu_active_index + (GTK_OPTION_MENU(format_optmenu)); + + if (type == EXPORT_MBOX) + filename = filesel_select_file(_("Select destination file"), + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE); + else + filename = filesel_select_file(_("Select destination folder"), + NULL, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); + if (!filename) return; + + utf8_filename = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); + if (!utf8_filename) { + g_warning("export_filesel_cb(): failed to convert character set."); + utf8_filename = g_strdup(filename); + } + gtk_entry_set_text(GTK_ENTRY(file_entry), utf8_filename); + g_free(utf8_filename); + + g_free(filename); +} + +static void export_srcsel_cb(GtkWidget *widget, gpointer data) +{ + FolderItem *src; + gchar *src_id; + + src = foldersel_folder_sel(NULL, FOLDER_SEL_ALL, NULL); + if (src && src->path) { + src_id = folder_item_get_identifier(src); + if (src_id) { + gtk_entry_set_text(GTK_ENTRY(src_entry), src_id); + g_free(src_id); + } + } +} + +static gint delete_event(GtkWidget *widget, GdkEventAny *event, gpointer data) +{ + export_cancel_cb(NULL, NULL); + return TRUE; +} + +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + if (event && event->keyval == GDK_Escape) + export_cancel_cb(NULL, NULL); + return FALSE; +} + +static void export_progress_cancel_cb(GtkWidget *widget, gpointer data) +{ + progress_cancel = TRUE; +} diff --git a/src/export.h b/src/export.h new file mode 100644 index 0000000..db16466 --- /dev/null +++ b/src/export.h @@ -0,0 +1,29 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2009 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EXPORT_H__ +#define __EXPORT_H__ + +#include + +#include "folder.h" + +gint export_mail(FolderItem *default_src); + +#endif /* __EXPORT_H__ */ diff --git a/src/exportcsv.c b/src/exportcsv.c new file mode 100644 index 0000000..3fa35ee --- /dev/null +++ b/src/exportcsv.c @@ -0,0 +1,607 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2017 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "addrbook.h" +#include "addressbook.h" +#include "addressitem.h" +#include "filesel.h" +#include "gtkutils.h" +#include "stock_pixmap.h" +#include "prefs_common.h" +#include "manage_window.h" +#include "mgutils.h" +#include "exportcsv.h" +#include "utils.h" + +#define PAGE_FILE_INFO 0 +#define PAGE_FINISH 1 + +#define EXPORTCSV_WIDTH 480 +#define EXPORTCSV_HEIGHT 320 + +static struct _ExpCSVDlg { + GtkWidget *window; + GtkWidget *notebook; + GtkWidget *file_entry; + GtkWidget *comma_radiobtn; + GtkWidget *tab_radiobtn; + GtkWidget *labelFile; + GtkWidget *labelRecords; + GtkWidget *btnNext; + GtkWidget *btnCancel; + GtkWidget *statusbar; + gint status_cid; + gint rowCount; + gchar *fileName; + gchar delimiter; + gboolean cancelled; +} expcsv_dlg; + +static AddressObject *_exp_object_; +static gint exportCount = 0; +static gint result; + +static void exp_csv_status_show( gchar *msg ) { + if( expcsv_dlg.statusbar != NULL ) { + gtk_statusbar_pop( GTK_STATUSBAR(expcsv_dlg.statusbar), expcsv_dlg.status_cid ); + if( msg ) { + gtk_statusbar_push( GTK_STATUSBAR(expcsv_dlg.statusbar), expcsv_dlg.status_cid, msg ); + } + } +} + +static void exp_csv_message( void ) { + gchar *sMsg = NULL; + gint pageNum; + + pageNum = gtk_notebook_get_current_page( GTK_NOTEBOOK(expcsv_dlg.notebook) ); + if( pageNum == PAGE_FILE_INFO ) { + sMsg = _( "Please specify file to export." ); + } + else if( pageNum == PAGE_FINISH ) { + sMsg = _( "File exported." ); + } + exp_csv_status_show( sMsg ); +} + +static gint exp_csv_write_email_record( ItemPerson *person, ItemEMail *email, FILE *fp ) { + gchar *sName, *sAddress, *sAlias, *sNickName; + gchar *sFirstName, *sLastName; + gchar *sRemarks; + gchar *csv; + + g_return_val_if_fail(person != NULL, MGU_BAD_ARGS); + + sName = ADDRITEM_NAME(person) ? ADDRITEM_NAME(person) : ""; + sFirstName = person->firstName ? person->firstName : ""; + sLastName = person->lastName ? person->lastName : ""; + sNickName = person->nickName ? person->nickName : ""; + + if (email) { + sAddress = email->address ? email->address : ""; + sRemarks = email->remarks ? email->remarks : ""; + sAlias = ADDRITEM_NAME(email) ? ADDRITEM_NAME(email) : ""; + } else { + sAddress = ""; + sRemarks = ""; + sAlias = ""; + } + + csv = strconcat_csv(expcsv_dlg.delimiter, sFirstName, sLastName, sName, sNickName, sAddress, sRemarks, sAlias, NULL); + debug_print("%s\n", csv); + fputs(csv, fp); + fputc('\n', fp); + g_free(csv); + + return MGU_SUCCESS; +} + +static gint exp_csv_export_folder_recursive( ItemFolder *itemFolder, FILE *fp ) { + GList *items; + GList *cur; + gint count = 0; + + g_return_val_if_fail(itemFolder != NULL, MGU_BAD_ARGS); + + debug_print("exp_csv_export_folder_recursive: export folder: %s\n", ADDRITEM_NAME(itemFolder) ? ADDRITEM_NAME(itemFolder) : "(root)"); + + items = addritem_folder_get_person_list(itemFolder); + for (cur = items; cur != NULL; cur = cur->next) { + ItemPerson *person; + ItemEMail *email; + GList *node; + + person = (ItemPerson *)cur->data; + if (!person) { + continue; + } + + node = person->listEMail; + if (!node) { + if (exp_csv_write_email_record(person, NULL, fp) == MGU_SUCCESS) { + count++; + } + } else { + while (node) { + email = node->data; + node = g_list_next(node); + if (exp_csv_write_email_record(person, email, fp) == MGU_SUCCESS) { + count++; + } + } + } + } + g_list_free(items); + + exportCount += count; + + /* Export subfolders */ + items = itemFolder->listFolder; + for (cur = items; cur != NULL; cur = cur->next) { + ItemFolder *item = cur->data; + exp_csv_export_folder_recursive(item, fp); + } + + return MGU_SUCCESS; +} + +static gint exp_csv_export_data( gchar *csvFile ) { + FILE *fp; + ItemFolder *itemFolder = NULL; + ItemGroup *itemGroup = NULL; + GList *items; + GList *cur; + gchar *csv; + gint count = 0; + + g_return_val_if_fail(csvFile != NULL, MGU_BAD_ARGS); + + if (_exp_object_->type == ADDR_INTERFACE) { + return MGU_BAD_ARGS; + } + + if ((fp = g_fopen(csvFile, "w")) == NULL) { + return MGU_OPEN_FILE; + } + + if (_exp_object_->type == ADDR_DATASOURCE) { + AddressDataSource *ds = ADAPTER_DSOURCE(_exp_object_)->dataSource; + + if (ds) { + itemFolder = addrindex_ds_get_root_folder(ds); + } + } else if (_exp_object_->type == ADDR_ITEM_FOLDER) { + itemFolder = ADAPTER_FOLDER(_exp_object_)->itemFolder; + } else if (_exp_object_->type == ADDR_ITEM_GROUP) { + itemGroup = ADAPTER_GROUP(_exp_object_)->itemGroup; + } + + csv = strconcat_csv(expcsv_dlg.delimiter, _("First Name"), _("Last Name"), _("Display Name"), _("Nick Name"), _("E-Mail Address"), _("Remarks"), _("Alias"), NULL); + debug_print("%s\n", csv); + fputs(csv, fp); + fputc('\n', fp); + g_free(csv); + + if (itemFolder) { + exp_csv_export_folder_recursive(itemFolder, fp); + } else if (itemGroup) { + items = itemGroup->listEMail; + for (cur = items; cur != NULL; cur = cur->next) { + ItemEMail *email = (ItemEMail *)cur->data; + ItemPerson *person; + + if (!email) { + continue; + } + person = (ItemPerson *)ADDRITEM_PARENT(email); + if (!person) { + continue; + } + + if (exp_csv_write_email_record(person, email, fp) == MGU_SUCCESS) { + count++; + } + } + exportCount = count; + } + + fclose(fp); + + return MGU_SUCCESS; +} + +/* +* Move off file page. +* return: TRUE if OK to move off page. +*/ +static gboolean exp_csv_file_move() { + gboolean retVal = FALSE; + gchar *sFile; + gchar *sMsg = NULL; + gboolean errFlag = FALSE; + + sFile = gtk_editable_get_chars( GTK_EDITABLE(expcsv_dlg.file_entry), 0, -1 ); + g_strchug( sFile ); g_strchomp( sFile ); + + g_free( expcsv_dlg.fileName ); + expcsv_dlg.fileName = sFile; + + gtk_entry_set_text( GTK_ENTRY(expcsv_dlg.file_entry), sFile ); + + if (gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(expcsv_dlg.comma_radiobtn))) { + expcsv_dlg.delimiter = ','; + } else { + expcsv_dlg.delimiter = '\t'; + } + + if( *sFile == '\0'|| strlen( sFile ) < 1 ) { + sMsg = _( "Please select a file." ); + gtk_widget_grab_focus(expcsv_dlg.file_entry); + errFlag = TRUE; + } + + if( ! errFlag ) { + if( exp_csv_export_data( sFile ) == MGU_SUCCESS ) { + retVal = TRUE; + } else { + sMsg = _( "Cannot write to file." ); + } + } + exp_csv_status_show( sMsg ); + + return retVal; +} + +/* + * Display finish page. + */ +static void exp_csv_finish_show() { + gchar *sMsg; + + gtk_label_set_text( GTK_LABEL(expcsv_dlg.labelFile), expcsv_dlg.fileName ); + gtk_label_set_text( GTK_LABEL(expcsv_dlg.labelRecords), itos( exportCount ) ); + gtk_widget_set_sensitive( expcsv_dlg.btnNext, FALSE ); + if( result == MGU_SUCCESS ) { + sMsg = _( "CSV file exported successfully." ); + } + else { + sMsg = mgu_error2string( result ); + } + exp_csv_status_show( sMsg ); + gtk_button_set_label(GTK_BUTTON(expcsv_dlg.btnCancel), GTK_STOCK_CLOSE); + gtk_widget_grab_focus(expcsv_dlg.btnCancel); +} + +static void exp_csv_next( GtkWidget *widget ) { + gint pageNum; + + pageNum = gtk_notebook_get_current_page( GTK_NOTEBOOK(expcsv_dlg.notebook) ); + if( pageNum == PAGE_FILE_INFO ) { + if( exp_csv_file_move() ) { + gtk_notebook_set_current_page( + GTK_NOTEBOOK(expcsv_dlg.notebook), PAGE_FINISH ); + exp_csv_finish_show(); + } + } +} + +static void exp_csv_cancel( GtkWidget *widget, gpointer data ) { + gint pageNum; + + pageNum = gtk_notebook_get_current_page( GTK_NOTEBOOK(expcsv_dlg.notebook) ); + if( pageNum != PAGE_FINISH ) { + expcsv_dlg.cancelled = TRUE; + } + gtk_main_quit(); +} + +static void exp_csv_file_select( void ) { + gchar *sSelFile; + + sSelFile = filesel_select_file( _("Enter CSV File Name"), NULL, + GTK_FILE_CHOOSER_ACTION_SAVE ); + if (sSelFile) { + gchar *sUTF8File; + sUTF8File = conv_filename_to_utf8( sSelFile ); + gtk_entry_set_text( GTK_ENTRY(expcsv_dlg.file_entry), sUTF8File ); + g_free( sUTF8File ); + g_free( sSelFile ); + } +} + +static gint exp_csv_delete_event( GtkWidget *widget, GdkEventAny *event, gpointer data ) { + exp_csv_cancel( widget, data ); + return TRUE; +} + +static gboolean exp_csv_key_pressed( GtkWidget *widget, GdkEventKey *event, gpointer data ) { + if (event && event->keyval == GDK_Escape) { + exp_csv_cancel( widget, data ); + } + return FALSE; +} + +static void exp_csv_page_file( gint pageNum, gchar *pageLbl ) { + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *label; + GtkWidget *file_entry; + GtkWidget *file_btn; + GtkWidget *hbox; + GtkWidget *comma_radiobtn; + GtkWidget *tab_radiobtn; + gint top; + + vbox = gtk_vbox_new(FALSE, 4); + gtk_container_add( GTK_CONTAINER( expcsv_dlg.notebook ), vbox ); + gtk_container_set_border_width( GTK_CONTAINER (vbox), 4 ); + + label = gtk_label_new( pageLbl ); + gtk_widget_show( label ); + gtk_notebook_set_tab_label( + GTK_NOTEBOOK( expcsv_dlg.notebook ), + gtk_notebook_get_nth_page( + GTK_NOTEBOOK( expcsv_dlg.notebook ), pageNum ), + label ); + + table = gtk_table_new(2, 3, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 6 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + + /* First row */ + top = 0; + label = gtk_label_new(_("File Name")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + file_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), file_entry, 1, 2, top, (top + 1), + GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + file_btn = gtk_button_new_with_label(_(" ... ")); + gtk_table_attach(GTK_TABLE(table), file_btn, 2, 3, top, (top + 1), + GTK_FILL, 0, 3, 0); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER (hbox), 4 ); + + comma_radiobtn = gtk_radio_button_new_with_label + (NULL, _("Comma-separated")); + gtk_box_pack_start(GTK_BOX(hbox), comma_radiobtn, FALSE, FALSE, 0); + + tab_radiobtn = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON(comma_radiobtn), _("Tab-separated")); + gtk_box_pack_start(GTK_BOX(hbox), tab_radiobtn, FALSE, FALSE, 0); + + gtk_widget_show_all(vbox); + + /* Button handler */ + g_signal_connect(G_OBJECT(file_btn), "clicked", + G_CALLBACK(exp_csv_file_select), NULL); + + expcsv_dlg.file_entry = file_entry; + expcsv_dlg.comma_radiobtn = comma_radiobtn; + expcsv_dlg.tab_radiobtn = tab_radiobtn; +} + +static void exp_csv_page_finish( gint pageNum, gchar *pageLbl ) { + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *label; + GtkWidget *labelFile; + GtkWidget *labelRecs; + gint top; + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add( GTK_CONTAINER( expcsv_dlg.notebook ), vbox ); + gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH ); + + label = gtk_label_new( pageLbl ); + gtk_widget_show( label ); + gtk_notebook_set_tab_label( + GTK_NOTEBOOK( expcsv_dlg.notebook ), + gtk_notebook_get_nth_page( GTK_NOTEBOOK( expcsv_dlg.notebook ), pageNum ), label ); + + table = gtk_table_new(3, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 8 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8 ); + + /* First row */ + top = 0; + label = gtk_label_new(_("File Name :")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + labelFile = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), labelFile, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(labelFile), 0, 0.5); + gtk_label_set_line_wrap(GTK_LABEL(labelFile), TRUE); + + /* Secondrow */ + top++; + label = gtk_label_new(_("Records :")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + labelRecs = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), labelRecs, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(labelRecs), 0, 0.5); + + expcsv_dlg.labelFile = labelFile; + expcsv_dlg.labelRecords = labelRecs; +} + +static void exp_csv_dialog_create() { + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *vnbox; + GtkWidget *notebook; + GtkWidget *hbbox; + GtkWidget *btnNext; + GtkWidget *btnCancel; + GtkWidget *hsbox; + GtkWidget *statusbar; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request(window, EXPORTCSV_WIDTH, EXPORTCSV_HEIGHT ); + gtk_window_set_title( GTK_WINDOW(window), _("Export Address Book into CSV file") ); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(exp_csv_delete_event), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(exp_csv_key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + + vbox = gtk_vbox_new(FALSE, 4); + gtk_widget_show(vbox); + gtk_container_add(GTK_CONTAINER(window), vbox); + + vnbox = gtk_vbox_new(FALSE, 4); + gtk_container_set_border_width(GTK_CONTAINER(vnbox), 4); + gtk_widget_show(vnbox); + gtk_box_pack_start(GTK_BOX(vbox), vnbox, TRUE, TRUE, 0); + + /* Notebook */ + notebook = gtk_notebook_new(); + gtk_notebook_set_show_tabs( GTK_NOTEBOOK(notebook), FALSE ); + gtk_widget_show(notebook); + gtk_box_pack_start(GTK_BOX(vnbox), notebook, TRUE, TRUE, 0); + gtk_container_set_border_width(GTK_CONTAINER(notebook), 6); + + /* Status line */ + hsbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, 0); + statusbar = gtk_statusbar_new(); + gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, 0); + + /* Button panel */ + gtkut_stock_button_set_create(&hbbox, &btnNext, _("Next"), + &btnCancel, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(vnbox), hbbox, FALSE, FALSE, 0); + gtk_widget_grab_default(btnNext); + + /* Button handlers */ + g_signal_connect(G_OBJECT(btnNext), "clicked", + G_CALLBACK(exp_csv_next), NULL); + g_signal_connect(G_OBJECT(btnCancel), "clicked", + G_CALLBACK(exp_csv_cancel), NULL); + + gtk_widget_show_all(vbox); + + expcsv_dlg.window = window; + expcsv_dlg.notebook = notebook; + expcsv_dlg.btnNext = btnNext; + expcsv_dlg.btnCancel = btnCancel; + expcsv_dlg.statusbar = statusbar; + expcsv_dlg.status_cid = gtk_statusbar_get_context_id( + GTK_STATUSBAR(statusbar), "Export CSV Dialog" ); + +} + +static void exp_csv_create() { + exp_csv_dialog_create(); + exp_csv_page_file( PAGE_FILE_INFO, _( "File Info" ) ); + exp_csv_page_finish( PAGE_FINISH, _( "Finish" ) ); + gtk_widget_show_all( expcsv_dlg.window ); +} + +gint addressbook_exp_csv( AddressObject *obj ) { + g_return_val_if_fail(obj != NULL, -1); + + if (obj->type == ADDR_INTERFACE) + return -1; + + _exp_object_ = obj; + + if( ! expcsv_dlg.window ) + exp_csv_create(); + expcsv_dlg.cancelled = FALSE; + manage_window_set_transient(GTK_WINDOW(expcsv_dlg.window)); + gtk_widget_grab_default(expcsv_dlg.btnNext); + + gtk_entry_set_text( GTK_ENTRY(expcsv_dlg.file_entry), "" ); + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(expcsv_dlg.comma_radiobtn), TRUE ); + gtk_notebook_set_current_page( GTK_NOTEBOOK(expcsv_dlg.notebook), PAGE_FILE_INFO ); + gtk_widget_set_sensitive( expcsv_dlg.btnNext, TRUE ); + gtk_button_set_label( GTK_BUTTON(expcsv_dlg.btnCancel), GTK_STOCK_CANCEL ); + exp_csv_message(); + gtk_widget_grab_focus(expcsv_dlg.file_entry); + + expcsv_dlg.rowCount = 0; + g_free( expcsv_dlg.fileName ); + expcsv_dlg.fileName = NULL; + expcsv_dlg.delimiter = ','; + exportCount = 0; + + gtk_widget_show(expcsv_dlg.window); + + gtk_main(); + gtk_widget_hide(expcsv_dlg.window); + + if (expcsv_dlg.cancelled == FALSE && expcsv_dlg.fileName) { + debug_print("addressbook_exp_csv: exported to %s\n", expcsv_dlg.fileName); + } + + g_free( expcsv_dlg.fileName ); + expcsv_dlg.fileName = NULL; + _exp_object_ = NULL; + + if( expcsv_dlg.cancelled == TRUE ) return -1; + return 0; +} + +/* +* End of Source. +*/ + diff --git a/src/exportcsv.h b/src/exportcsv.h new file mode 100644 index 0000000..95c7c22 --- /dev/null +++ b/src/exportcsv.h @@ -0,0 +1,34 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2017 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Import CSV text data. + */ + +#ifndef __EXPORT_CSV_H__ +#define __EXPORT_CSV_H__ + +/* Function prototypes */ +gint addressbook_exp_csv( AddressObject *obj ); + +#endif /* __EXPORT_CSV_H__ */ + +/* +* End of Source. +*/ diff --git a/src/filesel.c b/src/filesel.c new file mode 100644 index 0000000..e4e3ecd --- /dev/null +++ b/src/filesel.c @@ -0,0 +1,611 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef G_OS_WIN32 +# define COBJMACROS +# include +# include +# include +# include +#endif + +#include "main.h" +#include "filesel.h" +#include "manage_window.h" +#include "alertpanel.h" +#include "utils.h" +#include "prefs_common.h" +#include "inc.h" + + +static GSList *filesel_select_file_full (const gchar *title, + const gchar *file, + GtkFileChooserAction action, + gboolean multiple, + FileselFileType *types, + gint default_type, + gint *selected_type); + +static GtkWidget *filesel_create (const gchar *title, + GtkFileChooserAction action); + +static void filesel_save_expander_set_expanded (GtkWidget *dialog, + gboolean expanded); +static gboolean filesel_save_expander_get_expanded (GtkWidget *dialog); + +static gchar *filesel_get_filename_with_ext (const gchar *filename, + const gchar *ext); + +static void filesel_combo_changed_cb (GtkComboBox *combo_box, + gpointer data); + +#if GTK_CHECK_VERSION(2, 8, 0) +static GtkFileChooserConfirmation filesel_confirm_overwrite_cb + (GtkFileChooser *chooser, + gpointer data); +#endif +#ifdef G_OS_WIN32 +static gboolean is_ext_lnk (const gchar *filename); +static gchar *filesel_get_link (const gchar *link_file); +static GSList *filesel_resolve_link (GtkFileChooser *chooser, + GSList *list, + gboolean *dir_selected); +#endif + + +gchar *filesel_select_file(const gchar *title, const gchar *file, + GtkFileChooserAction action) +{ + GSList *list; + gchar *selected = NULL; + + list = filesel_select_file_full(title, file, action, FALSE, NULL, 0, NULL); + if (list) { + selected = (gchar *)list->data; + slist_free_strings(list->next); + } + g_slist_free(list); + + return selected; +} + +GSList *filesel_select_files(const gchar *title, const gchar *file, + GtkFileChooserAction action) +{ + return filesel_select_file_full(title, file, action, TRUE, NULL, 0, NULL); +} + +static void filesel_change_dir_for_action(GtkFileChooserAction action) +{ + const gchar *cwd = NULL; + + switch (action) { + case GTK_FILE_CHOOSER_ACTION_OPEN: + if (prefs_common.prev_open_dir && + is_dir_exist(prefs_common.prev_open_dir)) + cwd = prefs_common.prev_open_dir; + else { + g_free(prefs_common.prev_open_dir); + prefs_common.prev_open_dir = NULL; + } + break; + case GTK_FILE_CHOOSER_ACTION_SAVE: + if (prefs_common.prev_save_dir && + is_dir_exist(prefs_common.prev_save_dir)) + cwd = prefs_common.prev_save_dir; + else { + g_free(prefs_common.prev_save_dir); + prefs_common.prev_save_dir = NULL; + } + break; + case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER: + if (prefs_common.prev_folder_dir && + is_dir_exist(prefs_common.prev_folder_dir)) + cwd = prefs_common.prev_folder_dir; + else { + g_free(prefs_common.prev_folder_dir); + prefs_common.prev_folder_dir = NULL; + } + break; + default: + break; + } + + if (cwd) + change_dir(cwd); + else + change_dir(get_document_dir()); +} + +static void filesel_save_dir_for_action(GtkFileChooserAction action, + const gchar *cwd) +{ + switch (action) { + case GTK_FILE_CHOOSER_ACTION_OPEN: + g_free(prefs_common.prev_open_dir); + prefs_common.prev_open_dir = g_strdup(cwd); + break; + case GTK_FILE_CHOOSER_ACTION_SAVE: + g_free(prefs_common.prev_save_dir); + prefs_common.prev_save_dir = g_strdup(cwd); + break; + case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER: + g_free(prefs_common.prev_folder_dir); + prefs_common.prev_folder_dir = g_strdup(cwd); + break; + default: + break; + } +} + +static GSList *filesel_select_file_full(const gchar *title, const gchar *file, + GtkFileChooserAction action, + gboolean multiple, + FileselFileType *types, + gint default_type, gint *selected_type) +{ + gchar *cwd; + GtkWidget *dialog; + gchar *prev_dir; + static gboolean save_expander_expanded = FALSE; + GSList *list = NULL; + GtkWidget *combo = NULL; + + prev_dir = g_get_current_dir(); + + filesel_change_dir_for_action(action); + + dialog = filesel_create(title, action); + + manage_window_set_transient(GTK_WINDOW(dialog)); + + cwd = g_get_current_dir(); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), cwd); + g_free(cwd); + cwd = NULL; + + if (file) + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), + file); + + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), + multiple); + + if (action == GTK_FILE_CHOOSER_ACTION_SAVE && save_expander_expanded) { + filesel_save_expander_set_expanded + (dialog, save_expander_expanded); + } + +#if GTK_CHECK_VERSION(2, 8, 0) + if (action == GTK_FILE_CHOOSER_ACTION_SAVE) { + gtk_file_chooser_set_do_overwrite_confirmation + (GTK_FILE_CHOOSER(dialog), TRUE); + g_signal_connect(GTK_FILE_CHOOSER(dialog), "confirm-overwrite", + G_CALLBACK(filesel_confirm_overwrite_cb), + NULL); + } +#endif + + /* create types combo box */ + if (types) { + GtkWidget *hbox; + GtkWidget *label; + gint i; + + hbox = gtk_hbox_new(FALSE, 12); + label = gtk_label_new(_("File type:")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + combo = gtk_combo_box_new_text(); + for (i = 0; types[i].type != NULL; i++) { + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), types[i].type); + } + gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0); + + gtk_widget_show_all(hbox); + gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), hbox); + if (default_type < 0 || default_type >= i) + default_type = 0; + gtk_combo_box_set_active(GTK_COMBO_BOX(combo), default_type); + g_object_set_data(G_OBJECT(combo), "types", types); + g_signal_connect(GTK_COMBO_BOX(combo), "changed", + G_CALLBACK(filesel_combo_changed_cb), dialog); + + if (file) { + gchar *newfile; + + newfile = filesel_get_filename_with_ext + (file, types[default_type].ext); + gtk_file_chooser_set_current_name + (GTK_FILE_CHOOSER(dialog), newfile); + g_free(newfile); + } + } + + gtk_widget_show(dialog); + + change_dir(prev_dir); + g_free(prev_dir); + + inc_lock(); + +#ifdef G_OS_WIN32 +again: +#endif + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); +#ifdef G_OS_WIN32 + if (list) { + /* resolve Windows shortcut */ + gboolean dir_selected = FALSE; + list = filesel_resolve_link(GTK_FILE_CHOOSER(dialog), + list, &dir_selected); + if (!list) { + if (!dir_selected) + alertpanel_error(_("The link target not found.")); + goto again; + } + } +#endif + if (list) { + cwd = gtk_file_chooser_get_current_folder + (GTK_FILE_CHOOSER(dialog)); + if (cwd) { + filesel_save_dir_for_action(action, cwd); + g_free(cwd); + } + } + } + + inc_unlock(); + + if (combo && selected_type) { + *selected_type = gtk_combo_box_get_active(GTK_COMBO_BOX(combo)); + } + + if (action == GTK_FILE_CHOOSER_ACTION_SAVE) + save_expander_expanded = + filesel_save_expander_get_expanded(dialog); + + manage_window_focus_out(dialog, NULL, NULL); + gtk_widget_destroy(dialog); + + return list; +} + +gchar *filesel_save_as(const gchar *file) +{ + gchar *filename; + + filename = filesel_select_file(_("Save as"), file, + GTK_FILE_CHOOSER_ACTION_SAVE); + +#if !GTK_CHECK_VERSION(2, 8, 0) + if (filename && is_file_exist(filename)) { + AlertValue aval; + + aval = alertpanel(_("Overwrite existing file"), + _("The file already exists. Do you want to replace it?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if (G_ALERTDEFAULT != aval) { + g_free(filename); + filename = NULL; + } + } +#endif + + return filename; +} + +gchar *filesel_save_as_type(const gchar *file, FileselFileType *types, + gint default_type, gint *selected_type) +{ + GSList *list; + gchar *filename = NULL; + + list = filesel_select_file_full(_("Save as"), file, + GTK_FILE_CHOOSER_ACTION_SAVE, FALSE, + types, + default_type, selected_type); + if (list) { + filename = (gchar *)list->data; + slist_free_strings(list->next); + } + g_slist_free(list); + +#if !GTK_CHECK_VERSION(2, 8, 0) + if (filename && is_file_exist(filename)) { + AlertValue aval; + + aval = alertpanel(_("Overwrite existing file"), + _("The file already exists. Do you want to replace it?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if (G_ALERTDEFAULT != aval) { + g_free(filename); + filename = NULL; + } + } +#endif + + return filename; +} + +gchar *filesel_select_dir(const gchar *dir) +{ + GSList *list; + gchar *selected = NULL; + + list = filesel_select_file_full(_("Select folder"), dir, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + FALSE, NULL, 0, NULL); + if (list) { + selected = (gchar *)list->data; + slist_free_strings(list->next); + } + g_slist_free(list); + + return selected; +} + +static GtkWidget *filesel_create(const gchar *title, + GtkFileChooserAction action) +{ + GtkWidget *dialog; + + if (prefs_common.comply_gnome_hig) + dialog = gtk_file_chooser_dialog_new + (title, NULL, action, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + (action == GTK_FILE_CHOOSER_ACTION_SAVE || + action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + else + dialog = gtk_file_chooser_dialog_new + (title, NULL, action, + (action == GTK_FILE_CHOOSER_ACTION_SAVE || + action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NULL); + gtk_window_set_position(GTK_WINDOW(dialog), + GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gtk_window_set_wmclass + (GTK_WINDOW(dialog), "file_selection", "Sylpheed"); + gtk_dialog_set_default_response + (GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); + + MANAGE_WINDOW_SIGNALS_CONNECT(dialog); + + return dialog; +} + +static void container_foreach_cb(GtkWidget *widget, gpointer data) +{ + GtkWidget **expander = (GtkWidget **)data; + + if (*expander == NULL) { + if (GTK_IS_EXPANDER(widget)) + *expander = widget; + else if (GTK_IS_CONTAINER(widget)) + gtk_container_foreach(GTK_CONTAINER(widget), + container_foreach_cb, data); + } +} + +static void filesel_save_expander_set_expanded(GtkWidget *dialog, + gboolean expanded) +{ + GtkWidget *expander = NULL; + + gtk_container_foreach(GTK_CONTAINER(dialog), container_foreach_cb, + &expander); + if (expander) + gtk_expander_set_expanded(GTK_EXPANDER(expander), expanded); +} + +static gboolean filesel_save_expander_get_expanded(GtkWidget *dialog) +{ + GtkWidget *expander = NULL; + + gtk_container_foreach(GTK_CONTAINER(dialog), container_foreach_cb, + &expander); + if (expander) + return gtk_expander_get_expanded(GTK_EXPANDER(expander)); + else + return FALSE; +} + +static gchar *filesel_get_filename_with_ext(const gchar *filename, const gchar *ext) +{ + gchar *base; + gchar *new_filename; + gchar *p; + + base = g_path_get_basename(filename); + p = strrchr(base, '.'); + if (p) + *p = '\0'; + new_filename = g_strconcat(base, ".", ext, NULL); + debug_print("new_filename: %s\n", new_filename); + g_free(base); + + return new_filename; +} + +static void filesel_combo_changed_cb(GtkComboBox *combo_box, gpointer data) +{ + GtkFileChooser *chooser = data; + gint active; + gchar *filename; + gchar *new_filename; + FileselFileType *types; + + active = gtk_combo_box_get_active(combo_box); + filename = gtk_file_chooser_get_filename(chooser); + if (!filename) + return; + types = g_object_get_data(G_OBJECT(combo_box), "types"); + debug_print("active: %d filename: %s\n", active, filename); + debug_print("type ext: %s\n", types[active].ext); + new_filename = filesel_get_filename_with_ext(filename, types[active].ext); + gtk_file_chooser_set_current_name(chooser, new_filename); + g_free(new_filename); + g_free(filename); +} + +#if GTK_CHECK_VERSION(2, 8, 0) +static GtkFileChooserConfirmation filesel_confirm_overwrite_cb + (GtkFileChooser *chooser, + gpointer data) +{ + gchar *filename; + GtkFileChooserConfirmation ret = + GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME; + + filename = gtk_file_chooser_get_filename(chooser); + + if (filename && is_file_exist(filename)) { + AlertValue aval; + +#ifdef G_OS_WIN32 + gchar *target = NULL; + + if (is_ext_lnk(filename) && + (target = filesel_get_link(filename)) != NULL && + is_dir_exist(target)) { + gtk_file_chooser_set_current_folder(chooser, target); + g_free(target); + g_free(filename); + return GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN; + } + g_free(target); +#endif + + aval = alertpanel(_("Overwrite existing file"), + _("The file already exists. Do you want to replace it?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if (G_ALERTDEFAULT == aval) + ret = GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME; + else + ret = GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN; + } + + g_free(filename); + + return ret; +} +#endif + +#ifdef G_OS_WIN32 +static gboolean is_ext_lnk(const gchar *filename) +{ + const gchar *ext; + + if (filename && (ext = strrchr(filename, '.')) && + g_ascii_strcasecmp(ext, ".lnk") == 0) + return TRUE; + + return FALSE; +} + +static gchar *filesel_get_link(const gchar *link_file) +{ + WIN32_FIND_DATAW wfd; + IShellLinkW *psl; + IPersistFile *ppf; + HRESULT hr; + wchar_t *wlink_file; + wchar_t wtarget[MAX_PATH]; + gchar *target = NULL; + + wtarget[0] = 0L; + + debug_print("link_file: %s\n", link_file); + + CoInitialize(NULL); + if (S_OK == CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (void **)&psl)) { + if (S_OK == IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (void **)&ppf)) { + wlink_file = g_utf8_to_utf16(link_file, -1, NULL, NULL, NULL); + if (S_OK == IPersistFile_Load(ppf, wlink_file, STGM_READ)) { + if (S_OK == IShellLinkW_GetPath(psl, wtarget, MAX_PATH, &wfd, SLGP_UNCPRIORITY)) { + target = g_utf16_to_utf8(wtarget, -1, NULL, NULL, NULL); + } + } + IPersistFile_Release(ppf); + g_free(wlink_file); + } + IShellLinkW_Release(psl); + } + CoUninitialize(); + + if (target) + debug_print("target: %s\n", target); + else + debug_print("target not found\n"); + + return target; +} + +static GSList *filesel_resolve_link(GtkFileChooser *chooser, GSList *list, gboolean *dir_selected) +{ + GSList *cur; + GSList *new_list = NULL; + gchar *target; + + for (cur = list; cur != NULL; cur = cur->next) { + gchar *selected = (gchar *)cur->data; + + if (is_ext_lnk(selected)) { + target = filesel_get_link(selected); + + if (is_dir_exist(target)) { + gtk_file_chooser_set_current_folder(chooser, target); + g_free(target); + slist_free_strings(new_list); + g_slist_free(new_list); + new_list = NULL; + *dir_selected = TRUE; + break; + } else if (is_file_exist(target)) { + new_list = g_slist_append(new_list, target); + } else { + g_free(target); + } + } else { + new_list = g_slist_append(new_list, g_strdup(selected)); + } + } + + slist_free_strings(list); + g_slist_free(list); + + return new_list; +} +#endif diff --git a/src/filesel.h b/src/filesel.h new file mode 100644 index 0000000..e14397a --- /dev/null +++ b/src/filesel.h @@ -0,0 +1,48 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2013 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __FILESEL_H__ +#define __FILESEL_H__ + +#include +#include + +typedef struct _FileselFileType FileselFileType; + +struct _FileselFileType +{ + gchar *type; + gchar *ext; +}; + +gchar *filesel_select_file (const gchar *title, + const gchar *file, + GtkFileChooserAction action); +GSList *filesel_select_files (const gchar *title, + const gchar *file, + GtkFileChooserAction action); + +gchar *filesel_save_as (const gchar *file); +gchar *filesel_save_as_type (const gchar *file, + FileselFileType *types, + gint default_type, + gint *selected_type); +gchar *filesel_select_dir (const gchar *dir); + +#endif /* __FILESEL_H__ */ diff --git a/src/foldersel.c b/src/foldersel.c new file mode 100644 index 0000000..cc5076d --- /dev/null +++ b/src/foldersel.c @@ -0,0 +1,765 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "utils.h" +#include "gtkutils.h" +#include "stock_pixmap.h" +#include "foldersel.h" +#include "alertpanel.h" +#include "manage_window.h" +#include "folderview.h" +#include "inputdialog.h" +#include "folder.h" +#include "prefs_common.h" + +enum { + FOLDERSEL_FOLDERNAME, + FOLDERSEL_FOLDERITEM, + FOLDERSEL_PIXBUF, + FOLDERSEL_PIXBUF_OPEN, + FOLDERSEL_FOREGROUND, + FOLDERSEL_BOLD, + N_FOLDERSEL_COLUMNS +}; + +typedef struct _FolderItemSearch FolderItemSearch; + +struct _FolderItemSearch +{ + FolderItem *item; + GtkTreePath *path; + GtkTreeIter iter; +}; + +static GdkPixbuf *folder_pixbuf; +static GdkPixbuf *folderopen_pixbuf; +static GdkPixbuf *foldernoselect_pixbuf; + +static GtkWidget *window; +static GtkWidget *label; +static GtkWidget *scrolledwin; +static GtkWidget *treeview; +static GtkWidget *entry; +static GtkWidget *confirm_area; +static GtkWidget *ok_button; +static GtkWidget *cancel_button; +static GtkWidget *new_button; + +static GtkTreeStore *tree_store; + +static FolderItem *folder_item; +static FolderItem *selected_item; + +FolderSelectionType sel_type; + +static gboolean cancelled; +static gboolean finished; + +static void foldersel_create (void); +static void foldersel_init (void); + +static void foldersel_append_item (GtkTreeStore *store, + FolderItem *item, + GtkTreeIter *iter, + GtkTreeIter *parent); + +static void foldersel_set_tree (Folder *cur_folder); + +static gboolean foldersel_selected (GtkTreeSelection *selection, + GtkTreeModel *model, + GtkTreePath *path, + gboolean currently_selected, + gpointer data); + +static void foldersel_ok (GtkButton *button, + gpointer data); +static void foldersel_cancel (GtkButton *button, + gpointer data); +static void foldersel_new_folder (GtkButton *button, + gpointer data); + +static void foldersel_entry_activated (GtkEntry *entry, + gpointer data); + +static void foldersel_tree_activated (GtkTreeView *treeview, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data); + +static gboolean foldersel_tree_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); + +static gint delete_event (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); + +static gint foldersel_folder_name_compare (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer context); + +static gboolean tree_view_folder_item_func (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + FolderItemSearch *data); + +FolderItem *foldersel_folder_sel(Folder *cur_folder, FolderSelectionType type, + const gchar *default_folder) +{ + return foldersel_folder_sel_full(cur_folder, type, default_folder, + NULL); +} + +FolderItem *foldersel_folder_sel_full(Folder *cur_folder, + FolderSelectionType type, + const gchar *default_folder, + const gchar *message) +{ + selected_item = NULL; + sel_type = type; + + if (!window) { + foldersel_create(); + foldersel_init(); + } + + if (message) { + gtk_widget_show(label); + gtk_label_set_text(GTK_LABEL(label), message); + } else + gtk_widget_hide(label); + + foldersel_set_tree(cur_folder); + + /* select current */ + if (folder_item) { + FolderItemSearch fis; + + fis.item = folder_item; + fis.path = NULL; + + /* find matching model entry */ + gtk_tree_model_foreach + (GTK_TREE_MODEL(tree_store), + (GtkTreeModelForeachFunc)tree_view_folder_item_func, + &fis); + + if (fis.path) { + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection + (GTK_TREE_VIEW(treeview)); + gtkut_tree_view_expand_parent_all + (GTK_TREE_VIEW(treeview), &fis.iter); + gtk_tree_selection_select_iter(selection, &fis.iter); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(treeview), + fis.path, NULL, FALSE); + gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeview), + fis.path, + NULL, TRUE, 0.5, 0.0); + gtk_tree_path_free(fis.path); + } else + gtk_tree_view_scroll_to_point + (GTK_TREE_VIEW(treeview), 0, 0); + } else + gtk_tree_view_scroll_to_point(GTK_TREE_VIEW(treeview), 0, 0); + + gtkut_box_set_reverse_order(GTK_BOX(confirm_area), + !prefs_common.comply_gnome_hig); + gtk_widget_grab_focus(ok_button); + gtk_widget_grab_focus(treeview); + + manage_window_set_transient(GTK_WINDOW(window)); + gtk_widget_show(window); + + cancelled = finished = FALSE; + + while (finished == FALSE) + gtk_main_iteration(); + + gtk_widget_hide(window); + gtk_label_set_text(GTK_LABEL(label), ""); + gtk_entry_set_text(GTK_ENTRY(entry), ""); + gtk_tree_store_clear(tree_store); + + if (cancelled || !selected_item) + return NULL; + + if (type == FOLDER_SEL_ALL || + (selected_item->stype != F_VIRTUAL && + (sel_type == FOLDER_SEL_MOVE_FOLDER || + (selected_item->path && !selected_item->no_select)))) { + folder_item = selected_item; + return folder_item; + } + + return NULL; +} + +static void foldersel_create(void) +{ + GtkWidget *vbox; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), _("Select folder")); + gtk_container_set_border_width(GTK_CONTAINER(window), 4); + gtk_window_set_position(GTK_WINDOW(window), + GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, FALSE); + gtk_window_set_wmclass + (GTK_WINDOW(window), "folder_selection", "Sylpheed"); + gtk_widget_realize(window); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(delete_event), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + + vbox = gtk_vbox_new(FALSE, 4); + gtk_widget_set_size_request(vbox, -1, 460 * gtkut_get_dpi_multiplier()); + gtk_container_add(GTK_CONTAINER(window), vbox); + + label = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 2); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_set_size_request(scrolledwin, 320 * gtkut_get_dpi_multiplier(), -1); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0); + + tree_store = gtk_tree_store_new(N_FOLDERSEL_COLUMNS, + G_TYPE_STRING, + G_TYPE_POINTER, + GDK_TYPE_PIXBUF, + GDK_TYPE_PIXBUF, + GDK_TYPE_COLOR, + G_TYPE_INT); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(tree_store), + FOLDERSEL_FOLDERNAME, + foldersel_folder_name_compare, + NULL, NULL); + + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tree_store)); + g_object_unref(G_OBJECT(tree_store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), + FOLDERSEL_FOLDERNAME); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); + gtk_tree_selection_set_select_function(selection, foldersel_selected, + NULL, NULL); + + g_signal_connect(G_OBJECT(treeview), "row-activated", + G_CALLBACK(foldersel_tree_activated), NULL); + g_signal_connect(G_OBJECT(treeview), "key_press_event", + G_CALLBACK(foldersel_tree_key_pressed), NULL); + + gtk_container_add(GTK_CONTAINER(scrolledwin), treeview); + + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_spacing(column, 1); + renderer = gtk_cell_renderer_pixbuf_new(); + g_object_set(renderer, "ypad", 0, NULL); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_set_attributes + (column, renderer, + "pixbuf", FOLDERSEL_PIXBUF, + "pixbuf-expander-open", FOLDERSEL_PIXBUF_OPEN, + "pixbuf-expander-closed", FOLDERSEL_PIXBUF, + NULL); + + /* create text renderer */ + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "ypad", 0, NULL); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_set_attributes + (column, renderer, + "text", FOLDERSEL_FOLDERNAME, + "foreground-gdk", FOLDERSEL_FOREGROUND, + "weight", FOLDERSEL_BOLD, + NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + entry = gtk_entry_new(); + gtk_entry_set_editable(GTK_ENTRY(entry), FALSE); + gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(entry), "activate", + G_CALLBACK(foldersel_entry_activated), NULL); + + gtkut_stock_button_set_create(&confirm_area, + &ok_button, GTK_STOCK_OK, + &cancel_button, GTK_STOCK_CANCEL, + NULL, NULL); + + gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_button); + + new_button = gtk_button_new_from_stock(GTK_STOCK_NEW); + gtk_widget_show(new_button); + gtk_box_pack_start(GTK_BOX(confirm_area), new_button, FALSE, FALSE, 0); + gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(confirm_area), + new_button, TRUE); + + g_signal_connect(G_OBJECT(ok_button), "clicked", + G_CALLBACK(foldersel_ok), NULL); + g_signal_connect(G_OBJECT(cancel_button), "clicked", + G_CALLBACK(foldersel_cancel), NULL); + g_signal_connect(G_OBJECT(new_button), "clicked", + G_CALLBACK(foldersel_new_folder), NULL); + + gtk_widget_show_all(vbox); +} + +static void foldersel_init(void) +{ + stock_pixbuf_gdk(treeview, STOCK_PIXMAP_FOLDER_CLOSE, + &folder_pixbuf); + stock_pixbuf_gdk(treeview, STOCK_PIXMAP_FOLDER_OPEN, + &folderopen_pixbuf); + stock_pixbuf_gdk(treeview, STOCK_PIXMAP_FOLDER_NOSELECT, + &foldernoselect_pixbuf); +} + +static void foldersel_append_item(GtkTreeStore *store, FolderItem *item, + GtkTreeIter *iter, GtkTreeIter *parent) +{ + gchar *name; + gchar *sub = ""; + GdkPixbuf *pixbuf, *pixbuf_open; + gboolean use_color; + gboolean no_select; + PangoWeight weight = PANGO_WEIGHT_NORMAL; + GdkColor *foreground = NULL; + static GdkColor color_noselect = {0, COLOR_DIM, COLOR_DIM, COLOR_DIM}; + static GdkColor color_new = {0, (guint16)55000, 15000, 15000}; + + name = item->name; + + if (item->stype != F_NORMAL && FOLDER_IS_LOCAL(item->folder)) { + switch (item->stype) { + case F_INBOX: + if (!strcmp2(item->name, INBOX_DIR)) + name = _("Inbox"); + break; + case F_OUTBOX: + if (!strcmp2(item->name, OUTBOX_DIR)) + name = _("Sent"); + break; + case F_QUEUE: + if (!strcmp2(item->name, QUEUE_DIR)) + name = _("Queue"); + break; + case F_TRASH: + if (!strcmp2(item->name, TRASH_DIR)) + name = _("Trash"); + break; + case F_DRAFT: + if (!strcmp2(item->name, DRAFT_DIR)) + name = _("Drafts"); + break; + case F_JUNK: + if (!strcmp2(item->name, JUNK_DIR)) + name = _("Junk"); + break; + default: + break; + } + } + + if (!item->parent) { + switch (FOLDER_TYPE(item->folder)) { + case F_MH: + sub = " (MH)"; break; + case F_IMAP: + sub = " (IMAP4)"; break; + case F_NEWS: + sub = " (News)"; break; + default: + break; + } + } + + if (item->stype == F_QUEUE && item->total > 0) { + name = g_strdup_printf("%s%s (%d)", name, sub, item->total); + } else if (item->unread > 0) { + name = g_strdup_printf("%s%s (%d)", name, sub, item->unread); + } else + name = g_strdup_printf("%s%s", name, sub); + + no_select = item->no_select || + (sel_type != FOLDER_SEL_ALL && item->stype == F_VIRTUAL); + + pixbuf = no_select ? foldernoselect_pixbuf : folder_pixbuf; + pixbuf_open = no_select ? foldernoselect_pixbuf : folderopen_pixbuf; + + if (item->stype == F_OUTBOX || item->stype == F_DRAFT || + item->stype == F_TRASH || item->stype == F_JUNK) { + use_color = FALSE; + } else if (item->stype == F_QUEUE) { + use_color = (item->total > 0); + if (item->total > 0) + weight = PANGO_WEIGHT_BOLD; + } else { + use_color = (item->new > 0); + if (item->unread > 0) + weight = PANGO_WEIGHT_BOLD; + } + + if (no_select) + foreground = &color_noselect; + else if (use_color) + foreground = &color_new; + + /* insert this node */ + gtk_tree_store_append(store, iter, parent); + gtk_tree_store_set(store, iter, + FOLDERSEL_FOLDERNAME, name, + FOLDERSEL_FOLDERITEM, item, + FOLDERSEL_PIXBUF, pixbuf, + FOLDERSEL_PIXBUF_OPEN, pixbuf_open, + FOLDERSEL_FOREGROUND, foreground, + FOLDERSEL_BOLD, weight, + -1); + g_free(name); +} + +static void foldersel_insert_gnode_in_store(GtkTreeStore *store, GNode *node, + GtkTreeIter *parent) +{ + FolderItem *item; + GtkTreeIter child; + GNode *iter; + + g_return_if_fail(node != NULL); + g_return_if_fail(node->data != NULL); + g_return_if_fail(store != NULL); + + item = FOLDER_ITEM(node->data); + foldersel_append_item(store, item, &child, parent); + + if (parent && item->parent && node->parent->children == node && + !item->parent->collapsed) { + GtkTreePath *path; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), parent); + gtk_tree_view_expand_row(GTK_TREE_VIEW(treeview), path, FALSE); + gtk_tree_path_free(path); + } + + /* insert its children (this node as parent) */ + for (iter = node->children; iter != NULL; iter = iter->next) + foldersel_insert_gnode_in_store(store, iter, &child); +} + +static void foldersel_set_tree(Folder *cur_folder) +{ + Folder *folder; + GList *list; + + for (list = folder_get_list(); list != NULL; list = list->next) { + folder = FOLDER(list->data); + g_return_if_fail(folder != NULL); + + if (sel_type != FOLDER_SEL_ALL) { + if (FOLDER_TYPE(folder) == F_NEWS) + continue; + } + if (sel_type == FOLDER_SEL_MOVE_FOLDER && folder != cur_folder) + continue; + + foldersel_insert_gnode_in_store(tree_store, folder->node, NULL); + } + + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(tree_store), + FOLDERSEL_FOLDERNAME, + GTK_SORT_ASCENDING); +} + +static gboolean foldersel_selected(GtkTreeSelection *selection, + GtkTreeModel *model, GtkTreePath *path, + gboolean currently_selected, gpointer data) +{ + GtkTreeIter iter; + FolderItem *item = NULL; + + if (currently_selected) + return TRUE; + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path)) + return TRUE; + + gtk_tree_model_get(GTK_TREE_MODEL(tree_store), &iter, + FOLDERSEL_FOLDERITEM, &item, -1); + + selected_item = item; + if (selected_item && + (sel_type == FOLDER_SEL_ALL || + (selected_item->stype != F_VIRTUAL && + (sel_type == FOLDER_SEL_MOVE_FOLDER || + (selected_item->path && !selected_item->no_select))))) { + gchar *id; + id = folder_item_get_identifier(selected_item); + gtk_entry_set_text(GTK_ENTRY(entry), id); + g_free(id); + } else + gtk_entry_set_text(GTK_ENTRY(entry), ""); + + return TRUE; +} + +static void foldersel_ok(GtkButton *button, gpointer data) +{ + finished = TRUE; +} + +static void foldersel_cancel(GtkButton *button, gpointer data) +{ + cancelled = TRUE; + finished = TRUE; +} + +static void foldersel_new_folder(GtkButton *button, gpointer data) +{ + FolderItem *new_item; + gchar *new_folder; + gchar *disp_name; + gchar *p; + GtkTreeIter selected, new_child; + GtkTreePath *selected_p, *new_child_p; + GtkTreeStore *store; + GtkTreeModel *model; + GtkTreeSelection *selection; + + if (!selected_item || FOLDER_TYPE(selected_item->folder) == F_NEWS) + return; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + if (!gtk_tree_selection_get_selected(selection, &model, &selected)) + return; + store = GTK_TREE_STORE(model); + + new_folder = input_dialog(_("New folder"), + _("Input the name of new folder:"), + _("NewFolder")); + if (!new_folder) return; + AUTORELEASE_STR(new_folder, {g_free(new_folder); return;}); + + p = strchr(new_folder, G_DIR_SEPARATOR); + if ((p && FOLDER_TYPE(selected_item->folder) != F_IMAP) || + (p && FOLDER_TYPE(selected_item->folder) == F_IMAP && + *(p + 1) != '\0')) { + alertpanel_error(_("`%c' can't be included in folder name."), + G_DIR_SEPARATOR); + return; + } + + disp_name = trim_string(new_folder, 32); + AUTORELEASE_STR(disp_name, {g_free(disp_name); return;}); + + /* find whether the directory already exists */ + if (folder_find_child_item_by_name(selected_item, new_folder)) { + alertpanel_error(_("The folder `%s' already exists."), + disp_name); + return; + } + + new_item = selected_item->folder->klass->create_folder + (selected_item->folder, selected_item, new_folder); + if (!new_item) { + alertpanel_error(_("Can't create the folder `%s'."), disp_name); + return; + } + + /* add new child */ + foldersel_append_item(store, new_item, &new_child, &selected); + selected_p = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &selected); + new_child_p = gtk_tree_model_get_path(GTK_TREE_MODEL(store), + &new_child); + + gtk_tree_view_expand_row(GTK_TREE_VIEW(treeview), selected_p, FALSE); + gtk_tree_selection_select_iter(selection, &new_child); + gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeview), new_child_p, + NULL, TRUE, 0.5, 0.0); + gtk_tree_path_free(new_child_p); + gtk_tree_path_free(selected_p); + + folderview_append_item(folderview_get(), NULL, new_item, TRUE); + folder_write_list(); +} + +static void foldersel_entry_activated(GtkEntry *entry, gpointer data) +{ + gtk_button_clicked(GTK_BUTTON(ok_button)); +} + +static void foldersel_tree_activated(GtkTreeView *treeview, GtkTreePath *path, + GtkTreeViewColumn *column, gpointer data) +{ + gtk_button_clicked(GTK_BUTTON(ok_button)); +} + +static gboolean foldersel_tree_key_pressed(GtkWidget *widget, + GdkEventKey *event, gpointer data) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *selected; + GtkAdjustment *adj; + gboolean moved; + + if (!event) + return FALSE; + + switch (event->keyval) { + case GDK_Left: + case GDK_KP_Left: + if ((event->state & + (GDK_SHIFT_MASK|GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) + return FALSE; + adj = gtk_scrolled_window_get_hadjustment + (GTK_SCROLLED_WINDOW(scrolledwin)); + if (adj->lower < adj->value) + return FALSE; + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + if (!gtk_tree_selection_get_selected(selection, &model, &iter)) + return FALSE; + selected = gtk_tree_model_get_path(model, &iter); + if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(treeview), selected)) { + gtk_tree_view_collapse_row(GTK_TREE_VIEW(treeview), selected); + gtk_tree_path_free(selected); + return TRUE; + } + gtk_tree_path_free(selected); + g_signal_emit_by_name(G_OBJECT(treeview), + "select-cursor-parent", &moved); + return TRUE; + case GDK_Right: + case GDK_KP_Right: + if ((event->state & + (GDK_SHIFT_MASK|GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) + return FALSE; + adj = gtk_scrolled_window_get_hadjustment + (GTK_SCROLLED_WINDOW(scrolledwin)); + if (adj->lower - adj->page_size > adj->value) + return FALSE; + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + if (!gtk_tree_selection_get_selected(selection, &model, &iter)) + return FALSE; + selected = gtk_tree_model_get_path(model, &iter); + if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(treeview), selected)) { + gtk_tree_view_expand_row(GTK_TREE_VIEW(treeview), selected, FALSE); + gtk_tree_path_free(selected); + return TRUE; + } + gtk_tree_path_free(selected); + break; + default: + break; + } + + return FALSE; +} + +static gint delete_event(GtkWidget *widget, GdkEventAny *event, gpointer data) +{ + foldersel_cancel(NULL, NULL); + return TRUE; +} + +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + if (event && event->keyval == GDK_Escape) + foldersel_cancel(NULL, NULL); + return FALSE; +} + +static gint foldersel_folder_name_compare(GtkTreeModel *model, GtkTreeIter *a, + GtkTreeIter *b, gpointer context) +{ + FolderItem *item_a = NULL, *item_b = NULL; + + gtk_tree_model_get(model, a, FOLDERSEL_FOLDERITEM, &item_a, -1); + gtk_tree_model_get(model, b, FOLDERSEL_FOLDERITEM, &item_b, -1); + + return folder_item_compare(item_a, item_b); +} + +static gboolean tree_view_folder_item_func(GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + FolderItemSearch *data) +{ + FolderItem *item = NULL; + + gtk_tree_model_get(model, iter, FOLDERSEL_FOLDERITEM, &item, -1); + + if (data->item == item) { + data->path = gtk_tree_path_copy(path); + data->iter = *iter; + return TRUE; + } + + return FALSE; +} diff --git a/src/foldersel.h b/src/foldersel.h new file mode 100644 index 0000000..80b078d --- /dev/null +++ b/src/foldersel.h @@ -0,0 +1,44 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2008 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __FOLDERSEL_H__ +#define __FOLDERSEL_H__ + +#include +#include + +#include "folder.h" + +typedef enum +{ + FOLDER_SEL_ALL, + FOLDER_SEL_MOVE, + FOLDER_SEL_COPY, + FOLDER_SEL_MOVE_FOLDER +} FolderSelectionType; + +FolderItem *foldersel_folder_sel (Folder *cur_folder, + FolderSelectionType type, + const gchar *default_folder); +FolderItem *foldersel_folder_sel_full(Folder *cur_folder, + FolderSelectionType type, + const gchar *default_folder, + const gchar *message); + +#endif /* __FOLDERSEL_H__ */ diff --git a/src/folderview.c b/src/folderview.c new file mode 100644 index 0000000..dc9582c --- /dev/null +++ b/src/folderview.c @@ -0,0 +1,3405 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "mainwindow.h" +#include "folderview.h" +#include "summaryview.h" +#include "query_search.h" +#include "inputdialog.h" +#include "subscribedialog.h" +#include "foldersel.h" +#include "manage_window.h" +#include "alertpanel.h" +#include "menu.h" +#include "stock_pixmap.h" +#include "statusbar.h" +#include "procmsg.h" +#include "utils.h" +#include "gtkutils.h" +#include "trayicon.h" +#include "prefs_common.h" +#include "prefs_account.h" +#include "prefs_folder_item.h" +#include "prefs_search_folder.h" +#include "filter.h" +#include "account.h" +#include "account_dialog.h" +#include "folder.h" +#include "inc.h" +#include "send_message.h" +#include "virtual.h" +#include "plugin.h" + +enum +{ + COL_FOLDER_NAME, + COL_NEW, + COL_UNREAD, + COL_TOTAL, + COL_FOLDER_ITEM, + COL_PIXBUF, + COL_PIXBUF_OPEN, + COL_FOREGROUND, + COL_BOLD, + N_COLS +}; + +#define COL_FOLDER_WIDTH 150 +#define COL_NUM_WIDTH 32 + +#define STATUSBAR_PUSH(mainwin, str) \ +{ \ + gtk_statusbar_push(GTK_STATUSBAR(mainwin->statusbar), \ + mainwin->folderview_cid, str); \ + gtkut_widget_draw_now(mainwin->statusbar); \ +} + +#define STATUSBAR_POP(mainwin) \ +{ \ + gtk_statusbar_pop(GTK_STATUSBAR(mainwin->statusbar), \ + mainwin->folderview_cid); \ +} + +static GList *folderview_list = NULL; + +static GdkPixbuf *inbox_pixbuf; +static GdkPixbuf *outbox_pixbuf; +static GdkPixbuf *folder_pixbuf; +static GdkPixbuf *folderopen_pixbuf; +static GdkPixbuf *foldernoselect_pixbuf; +static GdkPixbuf *draft_pixbuf; +static GdkPixbuf *trash_pixbuf; +static GdkPixbuf *junk_pixbuf; +static GdkPixbuf *virtual_pixbuf; + +static void folderview_set_columns (FolderView *folderview); + +static void folderview_select_row (FolderView *folderview, + GtkTreeIter *iter); +static void folderview_select_row_ref (FolderView *folderview, + GtkTreeRowReference *row); + +static void folderview_set_folders (FolderView *folderview); +static void folderview_append_folder (FolderView *folderview, + Folder *folder); + +static void folderview_update_row (FolderView *folderview, + GtkTreeIter *iter); +static void folderview_update_row_all (FolderView *folderview); + +static gint folderview_folder_name_compare (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); + +/* callback functions */ +static gboolean folderview_button_pressed (GtkWidget *treeview, + GdkEventButton *event, + FolderView *folderview); +static gboolean folderview_button_released (GtkWidget *treeview, + GdkEventButton *event, + FolderView *folderview); + +static gboolean folderview_key_pressed (GtkWidget *widget, + GdkEventKey *event, + FolderView *folderview); + +static void folderview_selection_changed(GtkTreeSelection *selection, + FolderView *folderview); + +static void folderview_row_expanded (GtkTreeView *treeview, + GtkTreeIter *iter, + GtkTreePath *path, + FolderView *folderview); +static void folderview_row_collapsed (GtkTreeView *treeview, + GtkTreeIter *iter, + GtkTreePath *path, + FolderView *folderview); + +static void folderview_popup_close (GtkMenuShell *menu_shell, + FolderView *folderview); + +static void folderview_col_resized (GtkWidget *widget, + GtkAllocation *allocation, + FolderView *folderview); + +static void folderview_download_cb (FolderView *folderview, + guint action, + GtkWidget *widget); + +static void folderview_update_tree_cb (FolderView *folderview, + guint action, + GtkWidget *widget); + +static void folderview_update_summary_cb(FolderView *folderview, + guint action, + GtkWidget *widget); + +static void folderview_mark_all_read_cb (FolderView *folderview, + guint action, + GtkWidget *widget); +static void folderview_send_queue_cb (FolderView *folderview, + guint action, + GtkWidget *widget); + +static void folderview_new_folder_cb (FolderView *folderview, + guint action, + GtkWidget *widget); +static void folderview_rename_folder_cb (FolderView *folderview, + guint action, + GtkWidget *widget); +static void folderview_move_folder_cb (FolderView *folderview, + guint action, + GtkWidget *widget); +static void folderview_delete_folder_cb (FolderView *folderview, + guint action, + GtkWidget *widget); +static void folderview_empty_trash_cb (FolderView *folderview, + guint action, + GtkWidget *widget); +static void folderview_remove_mailbox_cb(FolderView *folderview, + guint action, + GtkWidget *widget); + +static void folderview_rm_imap_server_cb (FolderView *folderview, + guint action, + GtkWidget *widget); + +static void folderview_new_news_group_cb(FolderView *folderview, + guint action, + GtkWidget *widget); +static void folderview_rm_news_group_cb (FolderView *folderview, + guint action, + GtkWidget *widget); +static void folderview_rm_news_server_cb(FolderView *folderview, + guint action, + GtkWidget *widget); + +static void folderview_search_cb (FolderView *folderview, + guint action, + GtkWidget *widget); + +static void folderview_property_cb (FolderView *folderview, + guint action, + GtkWidget *widget); + +static gboolean folderview_drag_motion_cb(GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time, + FolderView *folderview); +static void folderview_drag_leave_cb (GtkWidget *widget, + GdkDragContext *context, + guint time, + FolderView *folderview); +static void folderview_drag_received_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + FolderView *folderview); + +static GtkTargetEntry folderview_drag_types[] = +{ + {"text/plain", GTK_TARGET_SAME_APP, 0} +}; + +static GtkItemFactoryEntry folderview_mail_popup_entries[] = +{ + {N_("/Create _new folder..."), NULL, folderview_new_folder_cb, 0, NULL}, + {N_("/_Rename folder..."), NULL, folderview_rename_folder_cb, 0, NULL}, + {N_("/_Move folder..."), NULL, folderview_move_folder_cb, 0, NULL}, + {N_("/_Delete folder"), NULL, folderview_delete_folder_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/Empty _junk"), NULL, folderview_empty_trash_cb, 0, NULL}, + {N_("/Empty _trash"), NULL, folderview_empty_trash_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Check for new messages"), + NULL, folderview_update_tree_cb, 0, NULL}, + {N_("/R_ebuild folder tree"), NULL, folderview_update_tree_cb, 1, NULL}, + {N_("/_Update summary"), NULL, folderview_update_summary_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/Mar_k all read"), NULL, folderview_mark_all_read_cb, 0, NULL}, + {N_("/Send _queued messages"), NULL, folderview_send_queue_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Search messages..."), NULL, folderview_search_cb, 0, NULL}, + {N_("/Ed_it search condition..."), + NULL, folderview_search_cb, 0, NULL}, + {N_("/_Properties..."), NULL, folderview_property_cb, 0, NULL} +}; + +static GtkItemFactoryEntry folderview_imap_popup_entries[] = +{ + {N_("/Create _new folder..."), NULL, folderview_new_folder_cb, 0, NULL}, + {N_("/_Rename folder..."), NULL, folderview_rename_folder_cb, 0, NULL}, + {N_("/_Move folder..."), NULL, folderview_move_folder_cb, 0, NULL}, + {N_("/_Delete folder"), NULL, folderview_delete_folder_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/Empty _junk"), NULL, folderview_empty_trash_cb, 0, NULL}, + {N_("/Empty _trash"), NULL, folderview_empty_trash_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/Down_load"), NULL, folderview_download_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Check for new messages"), + NULL, folderview_update_tree_cb, 0, NULL}, + {N_("/R_ebuild folder tree"), NULL, folderview_update_tree_cb, 1, NULL}, + {N_("/_Update summary"), NULL, folderview_update_summary_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/Mar_k all read"), NULL, folderview_mark_all_read_cb, 0, NULL}, + {N_("/Send _queued messages"), NULL, folderview_send_queue_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Search messages..."), NULL, folderview_search_cb, 0, NULL}, + {N_("/Ed_it search condition..."), + NULL, folderview_search_cb, 0, NULL}, + {N_("/_Properties..."), NULL, folderview_property_cb, 0, NULL} +}; + +static GtkItemFactoryEntry folderview_news_popup_entries[] = +{ + {N_("/Su_bscribe to newsgroup..."), + NULL, folderview_new_news_group_cb, 0, NULL}, + {N_("/_Remove newsgroup"), NULL, folderview_rm_news_group_cb, 0, NULL}, + {N_("/_Rename folder..."), NULL, folderview_rename_folder_cb, 0, NULL}, + {N_("/_Delete folder"), NULL, folderview_delete_folder_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/Down_load"), NULL, folderview_download_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Check for new messages"), + NULL, folderview_update_tree_cb, 0, NULL}, + {N_("/_Update summary"), NULL, folderview_update_summary_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/Mar_k all read"), NULL, folderview_mark_all_read_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Search messages..."), NULL, folderview_search_cb, 0, NULL}, + {N_("/Ed_it search condition..."), + NULL, folderview_search_cb, 0, NULL}, + {N_("/_Properties..."), NULL, folderview_property_cb, 0, NULL} +}; + + +FolderView *folderview_create(void) +{ + FolderView *folderview; + GtkWidget *vbox; + GtkWidget *scrolledwin; + GtkWidget *treeview; + GtkTreeStore *store; + GtkTreeSelection *selection; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkWidget *mail_popup; + GtkWidget *news_popup; + GtkWidget *imap_popup; + GtkItemFactory *mail_factory; + GtkItemFactory *news_factory; + GtkItemFactory *imap_factory; + gint n_entries; + + debug_print(_("Creating folder view...\n")); + folderview = g_new0(FolderView, 1); + + vbox = gtk_vbox_new(FALSE, 1); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy + (GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0); + gtk_widget_set_size_request(scrolledwin, + prefs_common.folderview_width, + prefs_common.folderview_height); + + store = gtk_tree_store_new(N_COLS, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, + GDK_TYPE_PIXBUF, GDK_TYPE_PIXBUF, + GDK_TYPE_COLOR, G_TYPE_INT); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), + COL_FOLDER_NAME, + folderview_folder_name_compare, + NULL, NULL); + + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), FALSE); + gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), + COL_FOLDER_NAME); + gtk_tree_view_set_reorderable(GTK_TREE_VIEW(treeview), FALSE); +#if GTK_CHECK_VERSION(2, 12, 0) + g_object_set(treeview, "fixed-height-mode", TRUE, NULL); +#endif + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); + + gtk_container_add(GTK_CONTAINER(scrolledwin), treeview); + + /* create folder icon + name column */ + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_spacing(column, 1); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width + (column, prefs_common.folder_col_folder); + gtk_tree_view_column_set_resizable(column, TRUE); +#if GTK_CHECK_VERSION(2, 14, 0) + gtk_tree_view_column_set_expand(column, TRUE); +#endif + + renderer = gtk_cell_renderer_pixbuf_new(); + g_object_set(renderer, "ypad", 0, NULL); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_set_title(column, _("Folder")); + gtk_tree_view_column_set_attributes + (column, renderer, + "pixbuf", COL_PIXBUF, + "pixbuf-expander-open", COL_PIXBUF_OPEN, + "pixbuf-expander-closed", COL_PIXBUF, + NULL); + + renderer = gtk_cell_renderer_text_new(); +#if GTK_CHECK_VERSION(2, 6, 0) + g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, "ypad", 0, + NULL); +#else + g_object_set(renderer, "ypad", 0, NULL); +#endif + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_set_attributes(column, renderer, + "text", COL_FOLDER_NAME, + "foreground-gdk", COL_FOREGROUND, + "weight", COL_BOLD, + NULL); + + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + gtk_tree_view_set_expander_column(GTK_TREE_VIEW(treeview), column); + g_signal_connect(G_OBJECT(column->button), "size-allocate", + G_CALLBACK(folderview_col_resized), folderview); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "xalign", 1.0, "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes + (_("New"), renderer, "text", COL_NEW, + "foreground-gdk", COL_FOREGROUND, + "weight", COL_BOLD, NULL); + gtk_tree_view_column_set_alignment(column, 1.0); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width + (column, prefs_common.folder_col_new); + gtk_tree_view_column_set_min_width(column, 8); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + g_signal_connect(G_OBJECT(column->button), "size-allocate", + G_CALLBACK(folderview_col_resized), folderview); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "xalign", 1.0, "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes + (_("Unread"), renderer, "text", COL_UNREAD, + "foreground-gdk", COL_FOREGROUND, + "weight", COL_BOLD, NULL); + gtk_tree_view_column_set_alignment(column, 1.0); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width + (column, prefs_common.folder_col_unread); + gtk_tree_view_column_set_min_width(column, 8); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + g_signal_connect(G_OBJECT(column->button), "size-allocate", + G_CALLBACK(folderview_col_resized), folderview); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "xalign", 1.0, "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes + (_("Total"), renderer, "text", COL_TOTAL, + "foreground-gdk", COL_FOREGROUND, + "weight", COL_BOLD, NULL); + gtk_tree_view_column_set_alignment(column, 1.0); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width + (column, prefs_common.folder_col_total); + gtk_tree_view_column_set_min_width(column, 8); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + g_signal_connect(G_OBJECT(column->button), "size-allocate", + G_CALLBACK(folderview_col_resized), folderview); + + /* add rightmost empty column */ + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_min_width(column, 0); + gtk_tree_view_column_set_max_width(column, 0); + gtk_tree_view_column_set_clickable(column, FALSE); + gtk_tree_view_column_set_reorderable(column, FALSE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), + COL_FOLDER_NAME, + GTK_SORT_ASCENDING); + + /* popup menu */ + n_entries = sizeof(folderview_mail_popup_entries) / + sizeof(folderview_mail_popup_entries[0]); + mail_popup = menu_create_items(folderview_mail_popup_entries, + n_entries, + "", &mail_factory, + folderview); + n_entries = sizeof(folderview_imap_popup_entries) / + sizeof(folderview_imap_popup_entries[0]); + imap_popup = menu_create_items(folderview_imap_popup_entries, + n_entries, + "", &imap_factory, + folderview); + n_entries = sizeof(folderview_news_popup_entries) / + sizeof(folderview_news_popup_entries[0]); + news_popup = menu_create_items(folderview_news_popup_entries, + n_entries, + "", &news_factory, + folderview); + + g_signal_connect(G_OBJECT(treeview), "button_press_event", + G_CALLBACK(folderview_button_pressed), folderview); + g_signal_connect(G_OBJECT(treeview), "button_release_event", + G_CALLBACK(folderview_button_released), folderview); + g_signal_connect(G_OBJECT(treeview), "key_press_event", + G_CALLBACK(folderview_key_pressed), folderview); + + g_signal_connect(G_OBJECT(selection), "changed", + G_CALLBACK(folderview_selection_changed), folderview); + + g_signal_connect_after(G_OBJECT(treeview), "row-expanded", + G_CALLBACK(folderview_row_expanded), + folderview); + g_signal_connect_after(G_OBJECT(treeview), "row-collapsed", + G_CALLBACK(folderview_row_collapsed), + folderview); + + g_signal_connect(G_OBJECT(mail_popup), "selection_done", + G_CALLBACK(folderview_popup_close), folderview); + g_signal_connect(G_OBJECT(imap_popup), "selection_done", + G_CALLBACK(folderview_popup_close), folderview); + g_signal_connect(G_OBJECT(news_popup), "selection_done", + G_CALLBACK(folderview_popup_close), folderview); + + /* drop callback */ + gtk_drag_dest_set(treeview, GTK_DEST_DEFAULT_ALL, + folderview_drag_types, 1, + GDK_ACTION_MOVE | GDK_ACTION_COPY); + g_signal_connect(G_OBJECT(treeview), "drag-motion", + G_CALLBACK(folderview_drag_motion_cb), + folderview); + g_signal_connect(G_OBJECT(treeview), "drag-leave", + G_CALLBACK(folderview_drag_leave_cb), + folderview); + g_signal_connect(G_OBJECT(treeview), "drag-data-received", + G_CALLBACK(folderview_drag_received_cb), + folderview); + + folderview->vbox = vbox; + folderview->scrolledwin = scrolledwin; + folderview->treeview = treeview; + folderview->store = store; + folderview->selection = selection; + folderview->mail_popup = mail_popup; + folderview->mail_factory = mail_factory; + folderview->imap_popup = imap_popup; + folderview->imap_factory = imap_factory; + folderview->news_popup = news_popup; + folderview->news_factory = news_factory; + + folderview->display_folder_unread = prefs_common.display_folder_unread; + + folderview_set_columns(folderview); + + gtk_widget_show_all(vbox); + + folderview_list = g_list_append(folderview_list, folderview); + + return folderview; +} + +void folderview_init(FolderView *folderview) +{ + GtkWidget *treeview = folderview->treeview; + + stock_pixbuf_gdk(treeview, STOCK_PIXMAP_INBOX, &inbox_pixbuf); + stock_pixbuf_gdk(treeview, STOCK_PIXMAP_OUTBOX, &outbox_pixbuf); + stock_pixbuf_gdk(treeview, STOCK_PIXMAP_FOLDER_CLOSE, &folder_pixbuf); + stock_pixbuf_gdk(treeview, STOCK_PIXMAP_FOLDER_OPEN, &folderopen_pixbuf); + stock_pixbuf_gdk(treeview, STOCK_PIXMAP_FOLDER_NOSELECT, + &foldernoselect_pixbuf); + stock_pixbuf_gdk(treeview, STOCK_PIXMAP_DRAFT, &draft_pixbuf); + stock_pixbuf_gdk(treeview, STOCK_PIXMAP_TRASH, &trash_pixbuf); + stock_pixbuf_gdk(treeview, STOCK_PIXMAP_SPAM_SMALL, &junk_pixbuf); + stock_pixbuf_gdk(treeview, STOCK_PIXMAP_FOLDER_SEARCH, &virtual_pixbuf); +} + +void folderview_reflect_prefs(FolderView *folderview) +{ + folderview_set_columns(folderview); + if (folderview->display_folder_unread != + prefs_common.display_folder_unread) { + folderview->display_folder_unread = + prefs_common.display_folder_unread; + folderview_update_row_all(folderview); + } +} + +void folderview_add_sub_widget(FolderView *folderview, GtkWidget *widget) +{ + g_return_if_fail(folderview != NULL); + g_return_if_fail(widget != NULL); + + debug_print("folderview_add_sub_widget: adding sub widget\n"); + + gtk_box_pack_start(GTK_BOX(folderview->vbox), widget, FALSE, FALSE, 0); +} + +FolderView *folderview_get(void) +{ + return (FolderView *)folderview_list->data; +} + +void folderview_set(FolderView *folderview) +{ + MainWindow *mainwin = folderview->mainwin; + GtkTreeIter iter; + + debug_print(_("Setting folder info...\n")); + STATUSBAR_PUSH(mainwin, _("Setting folder info...")); + + main_window_cursor_wait(mainwin); + + folderview_unselect(folderview); + + gtk_tree_store_clear(folderview->store); + + folderview_set_folders(folderview); + + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(folderview->store), + &iter)) + folderview_select_row(folderview, &iter); + + main_window_cursor_normal(mainwin); + STATUSBAR_POP(mainwin); +} + +void folderview_set_all(void) +{ + GList *list; + + for (list = folderview_list; list != NULL; list = list->next) + folderview_set((FolderView *)list->data); +} + +static void folderview_set_columns(FolderView *folderview) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(folderview->treeview); + GtkTreeViewColumn *column; + + column = gtk_tree_view_get_column(treeview, COL_NEW); + gtk_tree_view_column_set_visible + (column, prefs_common.folder_col_visible[COL_NEW]); + column = gtk_tree_view_get_column(treeview, COL_UNREAD); + gtk_tree_view_column_set_visible + (column, prefs_common.folder_col_visible[COL_UNREAD]); + column = gtk_tree_view_get_column(treeview, COL_TOTAL); + gtk_tree_view_column_set_visible + (column, prefs_common.folder_col_visible[COL_TOTAL]); + column = gtk_tree_view_get_column(treeview, COL_TOTAL + 1); + gtk_tree_view_column_set_visible + (column, prefs_common.folder_col_visible[COL_NEW] || + prefs_common.folder_col_visible[COL_UNREAD] || + prefs_common.folder_col_visible[COL_TOTAL]); +} + +void folderview_select(FolderView *folderview, FolderItem *item) +{ + GtkTreeIter iter; + + if (!item) return; + + if (gtkut_tree_model_find_by_column_data + (GTK_TREE_MODEL(folderview->store), &iter, NULL, + COL_FOLDER_ITEM, item)) + folderview_select_row(folderview, &iter); +} + +static void folderview_select_row(FolderView *folderview, GtkTreeIter *iter) +{ + GtkTreeModel *model = GTK_TREE_MODEL(folderview->store); + GtkTreePath *path; + + g_return_if_fail(iter != NULL); + + path = gtk_tree_model_get_path(model, iter); + + gtkut_tree_view_expand_parent_all(GTK_TREE_VIEW(folderview->treeview), + iter); + + folderview->open_folder = TRUE; + gtk_tree_view_set_cursor(GTK_TREE_VIEW(folderview->treeview), path, + NULL, FALSE); + if (folderview->summaryview->folder_item && + folderview->summaryview->folder_item->total > 0) + gtk_widget_grab_focus(folderview->summaryview->treeview); + else + gtk_widget_grab_focus(folderview->treeview); + + gtk_tree_path_free(path); +} + +static void folderview_select_row_ref(FolderView *folderview, + GtkTreeRowReference *row) +{ + GtkTreePath *path; + GtkTreeIter iter; + + if (!row) return; + + path = gtk_tree_row_reference_get_path(row); + if (!path) + return; + gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), &iter, path); + gtk_tree_path_free(path); + + folderview_select_row(folderview, &iter); +} + +void folderview_unselect(FolderView *folderview) +{ + if (folderview->selected) { + gtk_tree_row_reference_free(folderview->selected); + folderview->selected = NULL; + } + if (folderview->opened) { + gtk_tree_row_reference_free(folderview->opened); + folderview->opened = NULL; + } +} + +static gboolean folderview_find_next_unread(GtkTreeModel *model, + GtkTreeIter *next, + GtkTreeIter *iter) +{ + FolderItem *item; + GtkTreeIter iter_; + gboolean valid; + + if (iter) { + iter_ = *iter; + valid = gtkut_tree_model_next(model, &iter_); + } else + valid = gtk_tree_model_get_iter_first(model, &iter_); + + while (valid) { + item = NULL; + gtk_tree_model_get(model, &iter_, COL_FOLDER_ITEM, &item, -1); + if (item && item->unread > 0 && item->stype != F_TRASH) { + if (next) + *next = iter_; + return TRUE; + } + + valid = gtkut_tree_model_next(model, &iter_); + } + + return FALSE; +} + +void folderview_select_next_unread(FolderView *folderview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(folderview->store); + GtkTreeIter iter, next; + gboolean remember_last; + + if (folderview->opened) { + GtkTreePath *path; + + path = gtk_tree_row_reference_get_path(folderview->opened); + if (!path) + return; + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_path_free(path); + } else { + if (!gtk_tree_model_get_iter_first(model, &iter)) + return; + } + if (folderview_find_next_unread(model, &next, &iter)) { + remember_last = prefs_common.remember_last_selected; + prefs_common.remember_last_selected = FALSE; + folderview_select_row(folderview, &next); + prefs_common.remember_last_selected = remember_last; + return; + } + + if (!folderview->opened) + return; + + /* search again from the first row */ + if (folderview_find_next_unread(model, &next, NULL)) { + remember_last = prefs_common.remember_last_selected; + prefs_common.remember_last_selected = FALSE; + folderview_select_row(folderview, &next); + prefs_common.remember_last_selected = remember_last; + } +} + +FolderItem *folderview_get_selected_item(FolderView *folderview) +{ + GtkTreePath *path; + GtkTreeIter iter; + FolderItem *item = NULL; + + if (!folderview->selected) + return NULL; + + path = gtk_tree_row_reference_get_path(folderview->selected); + if (!path) + return NULL; + gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), &iter, path); + gtk_tree_path_free(path); + gtk_tree_model_get(GTK_TREE_MODEL(folderview->store), &iter, + COL_FOLDER_ITEM, &item, -1); + + return item; +} + +void folderview_set_opened_item(FolderView *folderview, FolderItem *item) +{ + GtkTreeModel *model = GTK_TREE_MODEL(folderview->store); + GtkTreeIter iter; + GtkTreePath *path; + + gtk_tree_row_reference_free(folderview->opened); + folderview->opened = NULL; + + if (!item) + return; + + if (gtkut_tree_model_find_by_column_data + (model, &iter, NULL, COL_FOLDER_ITEM, item)) { + path = gtk_tree_model_get_path(model, &iter); + folderview->opened = gtk_tree_row_reference_new(model, path); + gtk_tree_path_free(path); + } +} + +void folderview_update_opened_msg_num(FolderView *folderview) +{ + GtkTreePath *path; + GtkTreeIter iter; + + if (!folderview->opened) + return; + + path = gtk_tree_row_reference_get_path(folderview->opened); + if (!path) + return; + gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), &iter, path); + gtk_tree_path_free(path); + + folderview_update_row(folderview, &iter); +} + +gboolean folderview_append_item(FolderView *folderview, GtkTreeIter *iter, + FolderItem *item, gboolean expand_parent) +{ + FolderItem *parent_item; + GtkTreeModel *model = GTK_TREE_MODEL(folderview->store); + GtkTreeIter iter_, child; + GtkTreeIter *iter_p = &iter_; + + g_return_val_if_fail(item != NULL, FALSE); + g_return_val_if_fail(item->folder != NULL, FALSE); + + parent_item = item->parent; + + if (!parent_item) + iter_p = NULL; + else if (!gtkut_tree_model_find_by_column_data + (model, iter_p, NULL, COL_FOLDER_ITEM, parent_item)) + return FALSE; + + if (!gtkut_tree_model_find_by_column_data + (model, &child, iter_p, COL_FOLDER_ITEM, item)) { + gtk_tree_store_append(folderview->store, &child, iter_p); + gtk_tree_store_set(folderview->store, &child, + COL_FOLDER_NAME, item->name, + COL_FOLDER_ITEM, item, + -1); + folderview_update_row(folderview, &child); + if (iter) + *iter = child; + if (expand_parent && iter_p) { + GtkTreePath *path; + + path = gtk_tree_model_get_path(model, iter_p); + gtk_tree_view_expand_row + (GTK_TREE_VIEW(folderview->treeview), + path, FALSE); + gtk_tree_path_free(path); + } + return TRUE; + } + + return FALSE; +} + +static void folderview_set_folders(FolderView *folderview) +{ + GList *list; + + list = folder_get_list(); + + for (; list != NULL; list = list->next) + folderview_append_folder(folderview, FOLDER(list->data)); +} + +static void folderview_scan_tree_func(Folder *folder, FolderItem *item, + gpointer data) +{ + GList *list; + gchar *rootpath; + + if (FOLDER_IS_LOCAL(folder)) + rootpath = LOCAL_FOLDER(folder)->rootpath; + else if (FOLDER_TYPE(folder) == F_IMAP && folder->account && + folder->account->recv_server) + rootpath = folder->account->recv_server; + else if (FOLDER_TYPE(folder) == F_NEWS && folder->account && + folder->account->nntp_server) + rootpath = folder->account->nntp_server; + else + return; + + for (list = folderview_list; list != NULL; list = list->next) { + FolderView *folderview = (FolderView *)list->data; + MainWindow *mainwin = folderview->mainwin; + gchar *str; + + if (item->path) + str = g_strdup_printf(_("Scanning folder %s%c%s ..."), + rootpath, G_DIR_SEPARATOR, + item->path); + else + str = g_strdup_printf(_("Scanning folder %s ..."), + rootpath); + + STATUSBAR_PUSH(mainwin, str); + STATUSBAR_POP(mainwin); + g_free(str); + } +} + +static GtkWidget *label_window_create(const gchar *str) +{ + GtkWidget *window; + GtkWidget *label; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request(window, 380, 60); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_title(GTK_WINDOW(window), str); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE); + manage_window_set_transient(GTK_WINDOW(window)); + g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(gtk_true), + NULL); + + label = gtk_label_new(str); + gtk_container_add(GTK_CONTAINER(window), label); + gtk_widget_show(label); + + gtk_widget_show(window); + + return window; +} + +static void folderview_rescan_tree(FolderView *folderview, Folder *folder) +{ + GtkWidget *window; + AlertValue avalue; + + g_return_if_fail(folder != NULL); + + if (!folder->klass->scan_tree) return; + + avalue = alertpanel + (_("Rebuild folder tree"), + _("The folder tree will be rebuilt. Continue?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if (avalue != G_ALERTDEFAULT) return; + + if (!FOLDER_IS_LOCAL(folder) && + !main_window_toggle_online_if_offline(folderview->mainwin)) + return; + + inc_lock(); + window = label_window_create(_("Rebuilding folder tree...")); + + summary_show(folderview->summaryview, NULL, FALSE); + GTK_EVENTS_FLUSH(); + + folder_set_ui_func(folder, folderview_scan_tree_func, NULL); + if (folder->klass->scan_tree(folder) < 0) + alertpanel_error(_("Rebuilding of the folder tree failed.")); + folder_set_ui_func(folder, NULL, NULL); + + folder_write_list(); + folderview_set_all(); + statusbar_pop_all(); + + gtk_widget_destroy(window); + inc_unlock(); +} + +gint folderview_check_new(Folder *folder) +{ + FolderItem *item; + FolderView *folderview; + GtkTreeModel *model; + GtkTreeIter iter; + gboolean valid; + gint prev_new, prev_unread, n_updated = 0; + + folderview = (FolderView *)folderview_list->data; + model = GTK_TREE_MODEL(folderview->store); + + if (folder && FOLDER_IS_REMOTE(folder)) { + if (!main_window_toggle_online_if_offline(folderview->mainwin)) + return 0; + } + + inc_lock(); + main_window_lock(folderview->mainwin); + gtk_widget_set_sensitive(folderview->treeview, FALSE); + GTK_EVENTS_FLUSH(); + + for (valid = gtk_tree_model_get_iter_first(model, &iter); + valid; valid = gtkut_tree_model_next(model, &iter)) { + item = NULL; + gtk_tree_model_get(model, &iter, + COL_FOLDER_ITEM, &item, -1); + if (!item || !item->path || !item->folder) continue; + if (item->stype == F_VIRTUAL) continue; + if (item->no_select) continue; + if (folder && folder != item->folder) continue; + if (!folder && FOLDER_IS_REMOTE(item->folder)) continue; + + prev_new = item->new; + prev_unread = item->unread; + folderview_scan_tree_func(item->folder, item, NULL); + if (folder_item_scan(item) < 0) { + if (folder && FOLDER_IS_REMOTE(folder) && + REMOTE_FOLDER(folder)->session == NULL) + break; + } + folderview_update_row(folderview, &iter); + if (item->stype != F_TRASH && item->stype != F_JUNK) { + if (prev_unread < item->unread) + n_updated += item->unread - prev_unread; + else if (prev_new < item->new) + n_updated += item->new - prev_new; + } + } + + gtk_widget_set_sensitive(folderview->treeview, TRUE); + main_window_unlock(folderview->mainwin); + inc_unlock(); + statusbar_pop_all(); + + folder_write_list(); + + return n_updated; +} + +gint folderview_check_new_item(FolderItem *item) +{ + Folder *folder; + FolderView *folderview; + GtkTreeModel *model; + GtkTreeIter iter; + gint prev_new, prev_unread, n_updated = 0; + + g_return_val_if_fail(item != NULL, 0); + g_return_val_if_fail(item->folder != NULL, 0); + + if (!item->path || item->no_select) + return 0; + + folderview = (FolderView *)folderview_list->data; + model = GTK_TREE_MODEL(folderview->store); + + folder = item->folder; + + if (!FOLDER_IS_LOCAL(folder)) { + if (!main_window_toggle_online_if_offline(folderview->mainwin)) + return 0; + } + + if (!gtkut_tree_model_find_by_column_data + (model, &iter, NULL, COL_FOLDER_ITEM, item)) + return 0; + + inc_lock(); + main_window_lock(folderview->mainwin); + gtk_widget_set_sensitive(folderview->treeview, FALSE); + GTK_EVENTS_FLUSH(); + + prev_new = item->new; + prev_unread = item->unread; + folderview_scan_tree_func(folder, item, NULL); + folder_item_scan(item); + folderview_update_row(folderview, &iter); + if (item->stype != F_TRASH && item->stype != F_JUNK) { + if (prev_unread < item->unread) + n_updated = item->unread - prev_unread; + else if (prev_new < item->new) + n_updated = item->new - prev_new; + } + + gtk_widget_set_sensitive(folderview->treeview, TRUE); + main_window_unlock(folderview->mainwin); + inc_unlock(); + statusbar_pop_all(); + + folder_write_list(); + + return n_updated; +} + +gint folderview_check_new_all(void) +{ + GList *list; + GtkWidget *window; + FolderView *folderview; + gint n_updated = 0; + + folderview = (FolderView *)folderview_list->data; + + inc_lock(); + main_window_lock(folderview->mainwin); + window = label_window_create + (_("Checking for new messages in all folders...")); + + list = folder_get_list(); + for (; list != NULL; list = list->next) { + Folder *folder = list->data; + + n_updated += folderview_check_new(folder); + } + + gtk_widget_destroy(window); + main_window_unlock(folderview->mainwin); + inc_unlock(); + + return n_updated; +} + +static gboolean folderview_search_new_recursive(GtkTreeModel *model, + GtkTreeIter *iter) +{ + FolderItem *item = NULL; + GtkTreeIter iter_; + gboolean valid; + + if (iter) { + gtk_tree_model_get(model, iter, COL_FOLDER_ITEM, &item, -1); + if (item) { + if (item->new > 0 || + (item->stype == F_QUEUE && item->total > 0)) + return TRUE; + } + valid = gtk_tree_model_iter_children(model, &iter_, iter); + } else + valid = gtk_tree_model_get_iter_first(model, &iter_); + + while (valid) { + if (folderview_search_new_recursive(model, &iter_) == TRUE) + return TRUE; + valid = gtk_tree_model_iter_next(model, &iter_); + } + + return FALSE; +} + +static gboolean folderview_have_new_children(FolderView *folderview, + GtkTreeIter *iter) +{ + GtkTreeModel *model = GTK_TREE_MODEL(folderview->store); + GtkTreeIter iter_; + gboolean valid; + + if (iter) + valid = gtk_tree_model_iter_children(model, &iter_, iter); + else + valid = gtk_tree_model_get_iter_first(model, &iter_); + + while (valid) { + if (folderview_search_new_recursive(model, &iter_) == TRUE) + return TRUE; + valid = gtk_tree_model_iter_next(model, &iter_); + } + + return FALSE; +} + +static gboolean folderview_search_unread_recursive(GtkTreeModel *model, + GtkTreeIter *iter) +{ + FolderItem *item = NULL; + GtkTreeIter iter_; + gboolean valid; + + if (iter) { + gtk_tree_model_get(model, iter, COL_FOLDER_ITEM, &item, -1); + if (item) { + if (item->stype == F_TRASH) + return FALSE; + if (item->unread > 0 || + (item->stype == F_QUEUE && item->total > 0)) + return TRUE; + } + valid = gtk_tree_model_iter_children(model, &iter_, iter); + } else + valid = gtk_tree_model_get_iter_first(model, &iter_); + + while (valid) { + if (folderview_search_unread_recursive(model, &iter_) == TRUE) + return TRUE; + valid = gtk_tree_model_iter_next(model, &iter_); + } + + return FALSE; +} + +static gboolean folderview_have_unread_children(FolderView *folderview, + GtkTreeIter *iter) +{ + GtkTreeModel *model = GTK_TREE_MODEL(folderview->store); + GtkTreeIter iter_; + gboolean valid; + + if (iter) + valid = gtk_tree_model_iter_children(model, &iter_, iter); + else + valid = gtk_tree_model_get_iter_first(model, &iter_); + + while (valid) { + if (folderview_search_unread_recursive(model, &iter_) == TRUE) + return TRUE; + valid = gtk_tree_model_iter_next(model, &iter_); + } + + return FALSE; +} + +static void folderview_update_row(FolderView *folderview, GtkTreeIter *iter) +{ + GtkTreeStore *store = folderview->store; + GtkTreeModel *model = GTK_TREE_MODEL(store); + GtkTreePath *path; + GtkTreeIter parent; + FolderItem *item = NULL; + GdkPixbuf *pixbuf, *open_pixbuf; + gchar *name, *str; + gchar new_s[11], unread_s[11], total_s[11]; + gboolean add_unread_mark; + gboolean use_color; + PangoWeight weight = PANGO_WEIGHT_NORMAL; + GdkColor *foreground = NULL; + + gtk_tree_model_get(model, iter, COL_FOLDER_ITEM, &item, -1); + g_return_if_fail(item != NULL); + + switch (item->stype) { + case F_INBOX: + pixbuf = open_pixbuf = inbox_pixbuf; + name = g_strdup(FOLDER_IS_LOCAL(item->folder) && + !strcmp2(item->name, INBOX_DIR) ? _("Inbox") : + item->name); + break; + case F_OUTBOX: + pixbuf = open_pixbuf = outbox_pixbuf; + name = g_strdup(FOLDER_IS_LOCAL(item->folder) && + !strcmp2(item->name, OUTBOX_DIR) ? _("Sent") : + item->name); + break; + case F_QUEUE: + pixbuf = open_pixbuf = outbox_pixbuf; + name = g_strdup(FOLDER_IS_LOCAL(item->folder) && + !strcmp2(item->name, QUEUE_DIR) ? _("Queue") : + item->name); + break; + case F_TRASH: + pixbuf = open_pixbuf = trash_pixbuf; + name = g_strdup(FOLDER_IS_LOCAL(item->folder) && + !strcmp2(item->name, TRASH_DIR) ? _("Trash") : + item->name); + break; + case F_DRAFT: + pixbuf = open_pixbuf = draft_pixbuf; + name = g_strdup(FOLDER_IS_LOCAL(item->folder) && + !strcmp2(item->name, DRAFT_DIR) ? _("Drafts") : + item->name); + break; + case F_JUNK: + pixbuf = open_pixbuf = junk_pixbuf; + name = g_strdup(FOLDER_IS_LOCAL(item->folder) && + !strcmp2(item->name, JUNK_DIR) ? _("Junk") : + item->name); + break; + case F_VIRTUAL: + pixbuf = open_pixbuf = virtual_pixbuf; + name = g_strdup(item->name); + break; + default: + if (item->no_select) { + pixbuf = open_pixbuf = foldernoselect_pixbuf; + } else { + pixbuf = folder_pixbuf; + open_pixbuf = folderopen_pixbuf; + } + + if (!item->parent) { + switch (FOLDER_TYPE(item->folder)) { + case F_MH: + name = " (MH)"; break; + case F_IMAP: + name = " (IMAP4)"; break; + case F_NEWS: + name = " (News)"; break; + default: + name = ""; + } + name = g_strconcat(item->name, name, NULL); + } else { + if (FOLDER_TYPE(item->folder) == F_NEWS && + item->path && + !strcmp2(item->name, item->path)) + name = get_abbrev_newsgroup_name + (item->path, + prefs_common.ng_abbrev_len); + else + name = g_strdup(item->name); + } + } + + path = gtk_tree_model_get_path(model, iter); + if (!gtk_tree_view_row_expanded + (GTK_TREE_VIEW(folderview->treeview), path) && + folderview_have_unread_children(folderview, iter)) + add_unread_mark = TRUE; + else + add_unread_mark = FALSE; + gtk_tree_path_free(path); + + if (item->stype == F_QUEUE && item->total > 0 && + folderview->display_folder_unread) { + str = g_strdup_printf("%s (%d%s)", name, item->total, + add_unread_mark ? "+" : ""); + g_free(name); + name = str; + } else if ((item->unread > 0 || add_unread_mark) && + folderview->display_folder_unread) { + if (item->unread > 0) + str = g_strdup_printf("%s (%d%s)", name, item->unread, + add_unread_mark ? "+" : ""); + else + str = g_strdup_printf("%s (+)", name); + g_free(name); + name = str; + } + + if (!item->parent) { + strcpy(new_s, "-"); + strcpy(unread_s, "-"); + strcpy(total_s, "-"); + } else { + itos_buf(new_s, item->new); + itos_buf(unread_s, item->unread); + itos_buf(total_s, item->total); + } + + if (item->stype == F_OUTBOX || item->stype == F_DRAFT || + item->stype == F_TRASH || item->stype == F_JUNK) { + use_color = FALSE; + if (item->stype == F_JUNK) { + if ((item->unread > 0) || add_unread_mark) + weight = PANGO_WEIGHT_BOLD; + } + } else if (item->stype == F_QUEUE) { + /* highlight queue folder if there are any messages */ + use_color = (item->total > 0); + if (item->total > 0) + weight = PANGO_WEIGHT_BOLD; + } else { + /* if unread messages exist, print with bold font */ + if ((item->unread > 0) || add_unread_mark) + weight = PANGO_WEIGHT_BOLD; + /* if new messages exist, print with colored letter */ + use_color = + (item->new > 0) || + (add_unread_mark && + folderview_have_new_children(folderview, iter)); + } + + if (item->no_select) + foreground = &folderview->color_noselect; + else if (use_color) + foreground = &folderview->color_new; + + gtk_tree_store_set(store, iter, + COL_FOLDER_NAME, name, + COL_NEW, new_s, + COL_UNREAD, unread_s, + COL_TOTAL, total_s, + COL_FOLDER_ITEM, item, + COL_PIXBUF, pixbuf, + COL_PIXBUF_OPEN, open_pixbuf, + COL_FOREGROUND, foreground, + COL_BOLD, weight, + -1); + /* g_print("folderview_update_row: %s: %s\n", item->path, name); */ + g_free(name); + + item->updated = FALSE; + + if (gtkut_tree_view_find_collapsed_parent + (GTK_TREE_VIEW(folderview->treeview), &parent, iter)) + folderview_update_row(folderview, &parent); +} + +static void folderview_update_row_all(FolderView *folderview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(folderview->store); + GtkTreeIter iter; + gboolean valid; + + valid = gtk_tree_model_get_iter_first(model, &iter); + + while (valid) { + folderview_update_row(folderview, &iter); + valid = gtkut_tree_model_next(model, &iter); + } +} + +void folderview_update_item(FolderItem *item, gboolean update_summary) +{ + FolderView *folderview; + GtkTreeIter iter; + + g_return_if_fail(item != NULL); + + folderview = folderview_get(); + + if (gtkut_tree_model_find_by_column_data + (GTK_TREE_MODEL(folderview->store), &iter, NULL, + COL_FOLDER_ITEM, item)) { + folderview_update_row(folderview, &iter); + if (update_summary && + folderview->summaryview->folder_item == item) + summary_show(folderview->summaryview, item, FALSE); + } +} + +static void folderview_update_item_foreach_func(gpointer key, gpointer val, + gpointer data) +{ + folderview_update_item((FolderItem *)key, GPOINTER_TO_INT(data)); +} + +void folderview_update_item_foreach(GHashTable *table, gboolean update_summary) +{ + g_hash_table_foreach(table, folderview_update_item_foreach_func, + GINT_TO_POINTER(update_summary)); +} + +static gboolean folderview_update_all_updated_func(GNode *node, gpointer data) +{ + FolderItem *item; + + item = FOLDER_ITEM(node->data); + if (item->updated) { + debug_print("folderview_update_all_updated(): '%s' is updated\n", item->path); + folderview_update_item(item, GPOINTER_TO_INT(data)); + } + + return FALSE; +} + +void folderview_update_all_updated(gboolean update_summary) +{ + GList *list; + Folder *folder; + + for (list = folder_get_list(); list != NULL; list = list->next) { + folder = (Folder *)list->data; + g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + folderview_update_all_updated_func, + GINT_TO_POINTER(update_summary)); + } +} + +static gboolean folderview_insert_item_recursive(FolderView *folderview, + FolderItem *item, + GtkTreeIter *iter) +{ + GNode *node; + GtkTreeIter iter_; + gboolean valid; + + g_return_val_if_fail(item != NULL, FALSE); + + valid = folderview_append_item(folderview, &iter_, item, FALSE); + g_return_val_if_fail(valid == TRUE, FALSE); + + for (node = item->node->children; node != NULL; node = node->next) { + FolderItem *child_item = FOLDER_ITEM(node->data); + folderview_insert_item_recursive(folderview, child_item, NULL); + } + + if (item->node->children && !item->collapsed) { + GtkTreePath *path; + + path = gtk_tree_model_get_path + (GTK_TREE_MODEL(folderview->store), &iter_); + gtk_tree_view_expand_row(GTK_TREE_VIEW(folderview->treeview), + path, FALSE); + gtk_tree_path_free(path); + } + + if (iter) + *iter = iter_; + return TRUE; +} + +static void folderview_append_folder(FolderView *folderview, Folder *folder) +{ + g_return_if_fail(folder != NULL); + + folderview_insert_item_recursive + (folderview, FOLDER_ITEM(folder->node->data), NULL); +} + +void folderview_new_folder(FolderView *folderview) +{ + FolderItem *item; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->folder != NULL); + + if (item->folder->klass->create_folder) + folderview_new_folder_cb(folderview, 0, NULL); + else if (FOLDER_TYPE(item->folder) == F_NEWS) + folderview_new_news_group_cb(folderview, 0, NULL); +} + +void folderview_rename_folder(FolderView *folderview) +{ + FolderItem *item; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->folder != NULL); + + if (!item->path) return; + if (item->stype != F_NORMAL) return; + + if (item->folder->klass->rename_folder) + folderview_rename_folder_cb(folderview, 0, NULL); +} + +void folderview_move_folder(FolderView *folderview) +{ + FolderItem *item; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->folder != NULL); + + if (!item->path) return; + if (item->stype != F_NORMAL && item->stype != F_VIRTUAL) return; + + if (item->folder->klass->move_folder) + folderview_move_folder_cb(folderview, 0, NULL); +} + +void folderview_delete_folder(FolderView *folderview) +{ + FolderItem *item; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->folder != NULL); + + if (!item->path) return; + if (item->stype != F_NORMAL) return; + + if (item->folder->klass->remove_folder) + folderview_delete_folder_cb(folderview, 0, NULL); + else if (FOLDER_TYPE(item->folder) == F_NEWS) + folderview_rm_news_group_cb(folderview, 0, NULL); +} + +void folderview_check_new_selected(FolderView *folderview) +{ + FolderItem *item; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->folder != NULL); + if (item->parent != NULL) return; + + folderview_check_new(item->folder); +} + +void folderview_remove_mailbox(FolderView *folderview) +{ + FolderItem *item; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->folder != NULL); + if (item->parent != NULL) return; + + switch (FOLDER_TYPE(item->folder)) { + case F_MH: + case F_MBOX: + case F_MAILDIR: + folderview_remove_mailbox_cb(folderview, 0, NULL); + break; + case F_IMAP: + folderview_rm_imap_server_cb(folderview, 0, NULL); + break; + case F_NEWS: + folderview_rm_news_server_cb(folderview, 0, NULL); + break; + default: + break; + } +} + +void folderview_rebuild_tree(FolderView *folderview) +{ + FolderItem *item; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->folder != NULL); + if (item->parent != NULL) return; + + folderview_rescan_tree(folderview, item->folder); +} + +static gboolean folderview_menu_popup(FolderView *folderview, + GdkEventButton *event) +{ + FolderItem *item = NULL; + Folder *folder; + GtkWidget *popup; + GtkItemFactory *ifactory; + GtkTreeModel *model = GTK_TREE_MODEL(folderview->store); + GtkTreeIter iter; + gboolean new_folder = FALSE; + gboolean rename_folder = FALSE; + gboolean move_folder = FALSE; + gboolean delete_folder = FALSE; + gboolean empty_junk = FALSE; + gboolean empty_trash = FALSE; + gboolean download_msg = FALSE; + gboolean update_tree = FALSE; + gboolean update_summary = FALSE; + gboolean mark_all_read = FALSE; + gboolean send_queue = FALSE; + gboolean rescan_tree = FALSE; + gboolean remove_tree = FALSE; + gboolean search_folder = FALSE; + gboolean folder_property = FALSE; + + if (!gtk_tree_selection_get_selected + (folderview->selection, NULL, &iter)) + return FALSE; + + gtk_tree_model_get(model, &iter, COL_FOLDER_ITEM, &item, -1); + g_return_val_if_fail(item != NULL, FALSE); + g_return_val_if_fail(item->folder != NULL, FALSE); + folder = item->folder; + + if (folderview->mainwin->lock_count == 0) { + new_folder = TRUE; + folder_property = TRUE; + search_folder = TRUE; + if (item->parent == NULL) { + update_tree = remove_tree = TRUE; + } else { + if (FOLDER_TYPE(folder) != F_IMAP) + mark_all_read = TRUE; + if (gtkut_tree_row_reference_equal + (folderview->selected, folderview->opened)) { + update_summary = TRUE; + mark_all_read = TRUE; + } + } + if (FOLDER_IS_LOCAL(folder) || FOLDER_TYPE(folder) == F_IMAP) { + if (item->parent == NULL) + update_tree = rescan_tree = TRUE; + else if (item->stype == F_NORMAL) { + rename_folder = delete_folder = TRUE; + if (folder->klass->move_folder) + move_folder = TRUE; + } else if (item->stype == F_TRASH) { + if (item->total > 0) + empty_trash = TRUE; + } else if (item->stype == F_JUNK) { + if (item->total > 0) + empty_junk = TRUE; + } else if (item->stype == F_QUEUE) { + if (item->total > 0) + send_queue = TRUE; + } + } else if (FOLDER_TYPE(folder) == F_NEWS) { + if (item->parent != NULL) + delete_folder = TRUE; + } + if (item->stype == F_VIRTUAL) { + new_folder = FALSE; + move_folder = rename_folder = delete_folder = TRUE; + } + if (FOLDER_TYPE(folder) == F_IMAP || + FOLDER_TYPE(folder) == F_NEWS) { + if (item->no_select == FALSE && + item->stype != F_VIRTUAL) + download_msg = TRUE; + } + } else { + search_folder = TRUE; + if (item->parent) { + if (FOLDER_TYPE(folder) != F_IMAP) + mark_all_read = TRUE; + if (gtkut_tree_row_reference_equal + (folderview->selected, folderview->opened)) { + update_summary = TRUE; + mark_all_read = TRUE; + } + if (item->stype == F_TRASH) { + if (item->total > 0) + empty_trash = TRUE; + } else if (item->stype == F_JUNK) { + if (item->total > 0) + empty_junk = TRUE; + } + } + } + +#define SET_SENS(factory, name, sens) \ +{ \ + GtkWidget *widget; \ + widget = gtk_item_factory_get_item(factory, name); \ + if (widget) \ + gtk_widget_set_sensitive(widget, sens); \ +} + +#define SET_VISIBILITY(factory, name, visible) \ +{ \ + GtkWidget *widget; \ + widget = gtk_item_factory_get_item(factory, name); \ + if (widget) { \ + if (visible) \ + gtk_widget_show(widget); \ + else \ + gtk_widget_hide(widget); \ + } \ +} + +#define SET_VISIBILITY2(factory, name, visible) \ +{ \ + GtkWidget *widget; \ + widget = gtk_item_factory_get_item(factory, name); \ + if (widget) { \ + GList *child; \ + GtkWidget *sep = NULL; \ + \ + child = g_list_find \ + (GTK_MENU_SHELL(popup)->children, widget); \ + if (child && child->next) \ + sep = GTK_WIDGET(child->next->data); \ + if (visible) { \ + gtk_widget_show(widget); \ + gtk_widget_show(sep); \ + } else { \ + gtk_widget_hide(widget); \ + gtk_widget_hide(sep); \ + } \ + } \ +} + + if (FOLDER_IS_LOCAL(folder)) { + popup = folderview->mail_popup; + ifactory = folderview->mail_factory; + } else if (FOLDER_TYPE(folder) == F_IMAP) { + popup = folderview->imap_popup; + ifactory = folderview->imap_factory; + } else if (FOLDER_TYPE(folder) == F_NEWS) { + popup = folderview->news_popup; + ifactory = folderview->news_factory; + } else + return FALSE; + + menu_set_insensitive_all(GTK_MENU_SHELL(popup)); + + SET_SENS(ifactory, "/Create new folder...", new_folder); + SET_SENS(ifactory, "/Rename folder...", rename_folder); + SET_SENS(ifactory, "/Move folder...", move_folder); + SET_SENS(ifactory, "/Delete folder", delete_folder); + SET_SENS(ifactory, "/Empty junk", empty_junk); + SET_SENS(ifactory, "/Empty trash", empty_trash); + SET_SENS(ifactory, "/Download", download_msg); + SET_SENS(ifactory, "/Check for new messages", update_tree); + SET_SENS(ifactory, "/Rebuild folder tree", rescan_tree); + SET_SENS(ifactory, "/Update summary", update_summary); + SET_SENS(ifactory, "/Mark all read", mark_all_read); + SET_SENS(ifactory, "/Send queued messages", send_queue); + SET_SENS(ifactory, "/Search messages...", search_folder); + SET_SENS(ifactory, "/Edit search condition...", search_folder); + SET_SENS(ifactory, "/Properties...", folder_property); + + if (FOLDER_TYPE(folder) == F_NEWS) { + SET_SENS(ifactory, "/Subscribe to newsgroup...", new_folder); + SET_SENS(ifactory, "/Remove newsgroup", delete_folder); + SET_VISIBILITY(ifactory, "/Remove newsgroup", + item->stype != F_VIRTUAL); + SET_VISIBILITY(ifactory, "/Rename folder...", + item->stype == F_VIRTUAL); + SET_VISIBILITY(ifactory, "/Delete folder", + item->stype == F_VIRTUAL); + } + + if (item->stype == F_JUNK) { + SET_VISIBILITY(ifactory, "/Empty junk", TRUE); + SET_VISIBILITY2(ifactory, "/Empty trash", TRUE); + SET_VISIBILITY(ifactory, "/Empty trash", FALSE); + } else { + SET_VISIBILITY(ifactory, "/Empty junk", FALSE); + SET_VISIBILITY2(ifactory, "/Empty trash", + item->stype == F_TRASH); + } + + SET_VISIBILITY(ifactory, "/Check for new messages", + item->parent == NULL); + SET_VISIBILITY(ifactory, "/Rebuild folder tree", item->parent == NULL); + SET_VISIBILITY(ifactory, "/Update summary", item->parent != NULL); + + if (FOLDER_TYPE(folder) == F_NEWS) { + SET_VISIBILITY2(ifactory, "/Mark all read", + item->parent != NULL && item->stype != F_QUEUE); + } else { + SET_VISIBILITY(ifactory, "/Mark all read", + item->parent != NULL && item->stype != F_QUEUE); + if (item->parent != NULL) { + SET_VISIBILITY2(ifactory, "/Send queued messages", TRUE); + SET_VISIBILITY(ifactory, "/Send queued messages", + item->stype == F_QUEUE); + } else { + SET_VISIBILITY2(ifactory, "/Send queued messages", + item->stype == F_QUEUE); + } + } + + SET_VISIBILITY(ifactory, "/Search messages...", + item->stype != F_VIRTUAL); + SET_VISIBILITY(ifactory, "/Edit search condition...", + item->stype == F_VIRTUAL); + +#undef SET_SENS +#undef SET_VISIBILITY +#undef SET_VISIBILITY2 + + syl_plugin_signal_emit("folderview-menu-popup", ifactory); + + if (event) + gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL, + event->button, event->time); + else + gtk_menu_popup(GTK_MENU(popup), NULL, NULL, + menu_widget_position, folderview->treeview, + 0, GDK_CURRENT_TIME); + + return FALSE; +} + + +/* callback functions */ + +static gboolean folderview_button_pressed(GtkWidget *widget, + GdkEventButton *event, + FolderView *folderview) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(widget); + GtkTreePath *path; + + if (!event) + return FALSE; + + if (!gtk_tree_view_get_path_at_pos(treeview, event->x, event->y, + &path, NULL, NULL, NULL)) + return TRUE; + + if (folderview->selection_locked || + summary_is_locked(folderview->summaryview)) + return TRUE; + + if (event->button == 1 || event->button == 2) { + if (event->type == GDK_2BUTTON_PRESS) { + if (gtk_tree_view_row_expanded(treeview, path)) + gtk_tree_view_collapse_row(treeview, path); + else + gtk_tree_view_expand_row(treeview, path, FALSE); + } + folderview->open_folder = TRUE; + } else if (event->button == 3) { + if (folderview->selected) { + folderview->prev_selected = + gtk_tree_row_reference_copy + (folderview->selected); + } + gtk_tree_selection_select_path(folderview->selection, path); + folderview_menu_popup(folderview, event); + gtk_tree_path_free(path); + return TRUE; + } + + gtk_tree_path_free(path); + return FALSE; +} + +static gboolean folderview_button_released(GtkWidget *treeview, + GdkEventButton *event, + FolderView *folderview) +{ + folderview->open_folder = FALSE; + return FALSE; +} + +static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event, + FolderView *folderview) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(widget); + GtkTreePath *opened = NULL, *selected = NULL; + GtkAdjustment *adj; + gboolean moved; + + if (!event) return FALSE; + + if (folderview->selection_locked || + summary_is_locked(folderview->summaryview)) + return TRUE; + + switch (event->keyval) { + case GDK_Return: + case GDK_KP_Enter: + if (folderview->selected) { + folderview_select_row_ref(folderview, + folderview->selected); + } + return TRUE; + case GDK_space: + case GDK_KP_Space: + if (folderview->selected) { + if (folderview->opened) + opened = gtk_tree_row_reference_get_path + (folderview->opened); + selected = gtk_tree_row_reference_get_path + (folderview->selected); + if (opened && selected && + gtk_tree_path_compare(opened, selected) == 0 && + (!folderview->summaryview->folder_item || + folderview->summaryview->folder_item->total == 0)) + folderview_select_next_unread(folderview); + else + folderview_select_row_ref(folderview, + folderview->selected); + gtk_tree_path_free(selected); + gtk_tree_path_free(opened); + return TRUE; + } + break; + case GDK_Left: + case GDK_KP_Left: + if ((event->state & + (GDK_SHIFT_MASK|GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) + return FALSE; + adj = gtk_scrolled_window_get_hadjustment + (GTK_SCROLLED_WINDOW(folderview->scrolledwin)); + if (adj->lower < adj->value) + return FALSE; + if (folderview->selected) { + selected = gtk_tree_row_reference_get_path + (folderview->selected); + if (selected) { + if (gtk_tree_view_row_expanded(treeview, selected)) { + gtk_tree_view_collapse_row(treeview, selected); + gtk_tree_path_free(selected); + return TRUE; + } + gtk_tree_path_free(selected); + } + } + g_signal_emit_by_name(G_OBJECT(treeview), + "select-cursor-parent", &moved); + return TRUE; + case GDK_Right: + case GDK_KP_Right: + if ((event->state & + (GDK_SHIFT_MASK|GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) + return FALSE; + adj = gtk_scrolled_window_get_hadjustment + (GTK_SCROLLED_WINDOW(folderview->scrolledwin)); + if (adj->upper - adj->page_size > adj->value) + return FALSE; + if (folderview->selected) { + selected = gtk_tree_row_reference_get_path + (folderview->selected); + if (selected) { + if (!gtk_tree_view_row_expanded(treeview, selected)) { + gtk_tree_view_expand_row(treeview, selected, FALSE); + gtk_tree_path_free(selected); + return TRUE; + } + gtk_tree_path_free(selected); + } + } + break; + case GDK_F10: + if ((event->state & GDK_SHIFT_MASK) != 0) { + folderview_menu_popup(folderview, NULL); + return TRUE; + } + break; + case GDK_Menu: + folderview_menu_popup(folderview, NULL); + return TRUE; + default: + break; + } + + return FALSE; +} + +static gboolean folderview_focus_idle_func(gpointer data) +{ + FolderView *folderview = (FolderView *)data; + + gdk_threads_enter(); + GTK_WIDGET_SET_FLAGS(folderview->treeview, GTK_CAN_FOCUS); + gdk_threads_leave(); + + return FALSE; +} + +static void folderview_selection_changed(GtkTreeSelection *selection, + FolderView *folderview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(folderview->store); + FolderItem *item = NULL; + GtkTreeIter iter; + GtkTreePath *path; + gboolean opened; + + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) { + if (folderview->selected) { + gtk_tree_row_reference_free(folderview->selected); + folderview->selected = NULL; + } + return; + } + + path = gtk_tree_model_get_path(model, &iter); + + gtk_tree_row_reference_free(folderview->selected); + folderview->selected = gtk_tree_row_reference_new(model, path); + + main_window_set_menu_sensitive(folderview->mainwin); + + if (!folderview->open_folder) { + gtk_tree_path_free(path); + return; + } + folderview->open_folder = FALSE; + + gtk_tree_model_get(model, &iter, COL_FOLDER_ITEM, &item, -1); + if (!item) { + gtk_tree_path_free(path); + return; + } + + if (item->path) + debug_print(_("Folder %s is selected\n"), item->path); + + if (summary_is_locked(folderview->summaryview)) { + gtk_tree_path_free(path); + return; + } + + if (folderview->opened) { + GtkTreePath *open_path = NULL; + + open_path = gtk_tree_row_reference_get_path(folderview->opened); + if (open_path && gtk_tree_path_compare(open_path, path) == 0) { + gtk_tree_path_free(open_path); + gtk_tree_path_free(path); + return; + } + gtk_tree_path_free(open_path); + } + + folderview->selection_locked = TRUE; + + GTK_EVENTS_FLUSH(); + opened = summary_show(folderview->summaryview, item, FALSE); + + if (opened) { + gtk_tree_row_reference_free(folderview->opened); + folderview->opened = gtk_tree_row_reference_new(model, path); + gtk_tree_view_scroll_to_cell + (GTK_TREE_VIEW(folderview->treeview), path, NULL, FALSE, + 0.0, 0.0); + if (item->total > 0) { + /* don't let GtkTreeView::gtk_tree_view_button_press() + * grab focus */ + GTK_WIDGET_UNSET_FLAGS(folderview->treeview, + GTK_CAN_FOCUS); + g_idle_add(folderview_focus_idle_func, folderview); + } + } else + folderview_select_row_ref(folderview, folderview->opened); + + gtk_tree_path_free(path); + + folderview->selection_locked = FALSE; + + if (prefs_common.change_account_on_folder_sel) { + PrefsAccount *account; + + account = account_find_from_item_property(item); + if (!account && item->folder) + account = item->folder->account; + if (!account) + account = account_get_default(); + if (account && account != cur_account) { + cur_account = account; + main_window_change_cur_account(); + } + } +} + +static void folderview_row_expanded(GtkTreeView *treeview, GtkTreeIter *iter, + GtkTreePath *path, FolderView *folderview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(folderview->store); + FolderItem *item = NULL; + GtkTreeIter iter_; + gboolean valid; + + folderview->open_folder = FALSE; + + gtk_tree_model_get(GTK_TREE_MODEL(folderview->store), iter, + COL_FOLDER_ITEM, &item, -1); + g_return_if_fail(item != NULL); + item->collapsed = FALSE; + folderview_update_row(folderview, iter); + + valid = gtk_tree_model_iter_children(model, &iter_, iter); + + while (valid) { + FolderItem *child_item = NULL; + + gtk_tree_model_get(model, &iter_, COL_FOLDER_ITEM, &child_item, + -1); + if (child_item && child_item->node->children && + !child_item->collapsed) { + GtkTreePath *path; + + path = gtk_tree_model_get_path(model, &iter_); + gtk_tree_view_expand_row + (GTK_TREE_VIEW(folderview->treeview), + path, FALSE); + gtk_tree_path_free(path); + } + valid = gtk_tree_model_iter_next(model, &iter_); + } +} + +static void folderview_row_collapsed(GtkTreeView *treeview, GtkTreeIter *iter, + GtkTreePath *path, FolderView *folderview) +{ + FolderItem *item = NULL; + + folderview->open_folder = FALSE; + + gtk_tree_model_get(GTK_TREE_MODEL(folderview->store), iter, + COL_FOLDER_ITEM, &item, -1); + g_return_if_fail(item != NULL); + item->collapsed = TRUE; + folderview_update_row(folderview, iter); +} + +static void folderview_popup_close(GtkMenuShell *menu_shell, + FolderView *folderview) +{ + GtkTreePath *path; + + if (!folderview->prev_selected) return; + + path = gtk_tree_row_reference_get_path(folderview->prev_selected); + gtk_tree_row_reference_free(folderview->prev_selected); + folderview->prev_selected = NULL; + if (!path) + return; + gtk_tree_selection_select_path(folderview->selection, path); + gtk_tree_path_free(path); +} + +static void folderview_col_resized(GtkWidget *widget, GtkAllocation *allocation, + FolderView *folderview) +{ + GtkTreeViewColumn *column; + gint type; + gint width = allocation->width; + + for (type = 0; type <= COL_TOTAL; type++) { + column = gtk_tree_view_get_column + (GTK_TREE_VIEW(folderview->treeview), type); + if (column && column->button == widget) { + switch (type) { + case COL_FOLDER_NAME: + prefs_common.folder_col_folder = width; + break; + case COL_NEW: + prefs_common.folder_col_new = width; + break; + case COL_UNREAD: + prefs_common.folder_col_unread = width; + break; + case COL_TOTAL: + prefs_common.folder_col_total = width; + break; + default: + break; + } + break; + } + } +} + +static void folderview_download_func(Folder *folder, FolderItem *item, + gpointer data) +{ + GList *list; + + for (list = folderview_list; list != NULL; list = list->next) { + FolderView *folderview = (FolderView *)list->data; + MainWindow *mainwin = folderview->mainwin; + gchar *str; + + str = g_strdup_printf + (_("Downloading messages in %s ..."), item->path); + main_window_progress_set(mainwin, + GPOINTER_TO_INT(data), item->total); + STATUSBAR_PUSH(mainwin, str); + STATUSBAR_POP(mainwin); + g_free(str); + } +} + +static void folderview_download_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + MainWindow *mainwin = folderview->mainwin; + FolderItem *item; + gint ret = 0; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + if (item->stype == F_VIRTUAL) + return; + + g_return_if_fail(item->folder != NULL); + + if (item->parent == NULL) { + gchar *name, *msg; + + name = trim_string(item->name, 32); + msg = g_strdup_printf(_("Download all messages under '%s' ?"), + name); + g_free(name); + if (alertpanel(_("Download all messages"), msg, + GTK_STOCK_YES, GTK_STOCK_NO, NULL) + != G_ALERTDEFAULT) { + g_free(msg); + return; + } + g_free(msg); + } + + if (!main_window_toggle_online_if_offline(folderview->mainwin)) + return; + + main_window_cursor_wait(mainwin); + inc_lock(); + main_window_lock(mainwin); + gtk_widget_set_sensitive(folderview->treeview, FALSE); + main_window_progress_on(mainwin); + GTK_EVENTS_FLUSH(); + folder_set_ui_func(item->folder, folderview_download_func, NULL); + + if (item->parent == NULL) { + GtkTreeModel *model = GTK_TREE_MODEL(folderview->store); + GtkTreeIter iter; + gboolean valid; + FolderItem *cur_item; + + valid = gtkut_tree_model_find_by_column_data + (model, &iter, NULL, COL_FOLDER_ITEM, item); + while ((valid = gtkut_tree_model_next(model, &iter)) == TRUE) { + cur_item = NULL; + gtk_tree_model_get(model, &iter, COL_FOLDER_ITEM, + &cur_item, -1); + if (!cur_item || cur_item->folder != item->folder) + break; + if (!cur_item->no_select && + cur_item->stype != F_VIRTUAL && + cur_item->stype != F_TRASH) { + ret = folder_item_fetch_all_msg(cur_item); + if (ret < 0) + break; + } + } + } else + ret = folder_item_fetch_all_msg(item); + + if (ret < 0) { + gchar *name; + + name = trim_string(item->name, 32); + alertpanel_error(_("Error occurred while downloading messages in `%s'."), name); + g_free(name); + } + + folder_set_ui_func(item->folder, NULL, NULL); + main_window_progress_off(mainwin); + gtk_widget_set_sensitive(folderview->treeview, TRUE); + main_window_unlock(mainwin); + inc_unlock(); + main_window_cursor_normal(mainwin); + statusbar_pop_all(); +} + +static void folderview_update_tree_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + FolderItem *item; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->folder != NULL); + + if (action == 0) + folderview_check_new(item->folder); + else + folderview_rescan_tree(folderview, item->folder); +} + +static void folderview_update_summary_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + if (!folderview->summaryview->folder_item) + return; + + GTK_EVENTS_FLUSH(); + summary_show(folderview->summaryview, + folderview->summaryview->folder_item, TRUE); +} + +static void folderview_mark_all_read_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + FolderItem *item; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + if (item == folderview->summaryview->folder_item) + summary_mark_all_read(folderview->summaryview); + else { + procmsg_mark_all_read(item); + folderview_update_item(item, FALSE); + trayicon_set_tooltip(NULL); + trayicon_set_notify(FALSE); + } +} + +static void folderview_send_queue_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + FolderItem *item; + gint ret; + + if (!main_window_toggle_online_if_offline(folderview->mainwin)) + return; + + item = folderview_get_selected_item(folderview); + if (!item || item->stype != F_QUEUE) + return; + + ret = send_message_queue_all(item, prefs_common.savemsg, + prefs_common.filter_sent); + statusbar_pop_all(); + if (ret > 0) + folder_item_scan(item); + + folderview_update_item(item, TRUE); + main_window_set_menu_sensitive(folderview->mainwin); + main_window_set_toolbar_sensitive(folderview->mainwin); +} + +static void folderview_new_folder_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + FolderItem *item; + FolderItem *new_item; + gchar *new_folder; + gchar *name; + gchar *p; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->folder != NULL); + + if (FOLDER_TYPE(item->folder) == F_IMAP) + g_return_if_fail(item->folder->account != NULL); + + if (FOLDER_TYPE(item->folder) == F_IMAP) { + new_folder = input_dialog + (_("New folder"), + _("Input the name of new folder:\n" + "(if you want to create a folder to store subfolders,\n" + " append `/' at the end of the name)"), + _("NewFolder")); + } else { + new_folder = input_dialog(_("New folder"), + _("Input the name of new folder:"), + _("NewFolder")); + } + if (!new_folder) return; + AUTORELEASE_STR(new_folder, {g_free(new_folder); return;}); + +#ifdef G_OS_WIN32 + p = strpbrk(new_folder, "\\/:*?\"<>|"); + if ((p && FOLDER_TYPE(item->folder) != F_IMAP) || (p && *p != '/') || + (p && *p == '/' && + FOLDER_TYPE(item->folder) == F_IMAP && *(p + 1) != '\0')) { + alertpanel_error(_("`%c' can't be included in folder name."), + *p); + return; + } +#else + p = strchr(new_folder, G_DIR_SEPARATOR); + if ((p && FOLDER_TYPE(item->folder) != F_IMAP) || + (p && FOLDER_TYPE(item->folder) == F_IMAP && *(p + 1) != '\0')) { + alertpanel_error(_("`%c' can't be included in folder name."), + G_DIR_SEPARATOR); + return; + } +#endif + + name = trim_string(new_folder, 32); + AUTORELEASE_STR(name, {g_free(name); return;}); + + /* find whether the directory already exists */ + if (folder_find_child_item_by_name(item, new_folder)) { + alertpanel_error(_("The folder `%s' already exists."), name); + return; + } + + new_item = item->folder->klass->create_folder(item->folder, item, + new_folder); + if (!new_item) { + alertpanel_error(_("Can't create the folder `%s'."), name); + return; + } + + folderview_append_item(folderview, NULL, new_item, TRUE); + folder_write_list(); +} + +static void folderview_rename_folder_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + FolderItem *item; + gchar *new_folder; + gchar *name; + gchar *message; + gchar *old_path; + gchar *old_id; + gchar *new_id; + GtkTreePath *sel_path; + GtkTreePath *open_path = NULL; + GtkTreeIter iter; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->path != NULL); + g_return_if_fail(item->folder != NULL); + + sel_path = gtk_tree_row_reference_get_path(folderview->selected); + + name = trim_string(item->name, 32); + message = g_strdup_printf(_("Input new name for `%s':"), name); + new_folder = input_dialog(_("Rename folder"), message, + g_basename(item->path)); + g_free(message); + g_free(name); + if (!new_folder) { + gtk_tree_path_free(sel_path); + return; + } + AUTORELEASE_STR(new_folder, {g_free(new_folder); gtk_tree_path_free(sel_path); return;}); + + if (strchr(new_folder, G_DIR_SEPARATOR) != NULL) { + alertpanel_error(_("`%c' can't be included in folder name."), + G_DIR_SEPARATOR); + gtk_tree_path_free(sel_path); + return; + } + + if (folder_find_child_item_by_name(item->parent, new_folder)) { + name = trim_string(new_folder, 32); + alertpanel_error(_("The folder `%s' already exists."), name); + g_free(name); + gtk_tree_path_free(sel_path); + return; + } + + old_path = g_strdup(item->path); + old_id = folder_item_get_identifier(item); + + if (item->stype == F_VIRTUAL) { + if (virtual_get_class()->rename_folder(item->folder, item, + new_folder) < 0) { + alertpanel_error(_("Can't rename the folder '%s'."), + item->name); + g_free(old_id); + g_free(old_path); + gtk_tree_path_free(sel_path); + return; + } + } else if (item->folder->klass->rename_folder(item->folder, item, + new_folder) < 0) { + alertpanel_error(_("Can't rename the folder '%s'."), + item->name); + g_free(old_id); + g_free(old_path); + gtk_tree_path_free(sel_path); + return; + } + + if (folder_get_default_folder() == item->folder) { + filter_list_rename_path(old_path, item->path); + prefs_common_junk_folder_rename_path(old_path, item->path); + } + new_id = folder_item_get_identifier(item); + filter_list_rename_path(old_id, new_id); + prefs_common_junk_folder_rename_path(old_id, new_id); + g_free(new_id); + g_free(old_id); + g_free(old_path); + + if (folderview->opened) + open_path = gtk_tree_row_reference_get_path(folderview->opened); + if (sel_path) { + gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), + &iter, sel_path); + folderview_update_row(folderview, &iter); + } + if (sel_path && open_path && + (gtk_tree_path_compare(open_path, sel_path) == 0 || + gtk_tree_path_is_ancestor(sel_path, open_path))) { + GtkTreeRowReference *row; + + row = gtk_tree_row_reference_copy(folderview->opened); + folderview_unselect(folderview); + folderview_select_row_ref(folderview, row); + gtk_tree_row_reference_free(row); + } + gtk_tree_path_free(open_path); + gtk_tree_path_free(sel_path); + + folder_write_list(); +} + +static void folderview_move_folder_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + FolderItem *item; + FolderItem *new_parent; + GtkTreePath *sel_path; + GtkTreePath *open_path = NULL; + GtkTreeIter iter; + gchar *old_path, *old_id, *new_id; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->path != NULL); + g_return_if_fail(item->folder != NULL); + + sel_path = gtk_tree_row_reference_get_path(folderview->selected); + g_return_if_fail(sel_path != NULL); + + new_parent = foldersel_folder_sel(item->folder, FOLDER_SEL_MOVE_FOLDER, + NULL); + if (!new_parent || new_parent->folder != item->folder || + new_parent == item->parent || new_parent->stype == F_VIRTUAL) { + gtk_tree_path_free(sel_path); + return; + } + + old_path = g_strdup(item->path); + old_id = folder_item_get_identifier(item); + + if (item->folder->klass->move_folder + (item->folder, item, new_parent) < 0) { + alertpanel_error(_("Can't move the folder `%s'."), item->name); + g_free(old_id); + g_free(old_path); + gtk_tree_path_free(sel_path); + return; + } + + if (folder_get_default_folder() == item->folder) { + filter_list_rename_path(old_path, item->path); + prefs_common_junk_folder_rename_path(old_path, item->path); + } + new_id = folder_item_get_identifier(item); + filter_list_rename_path(old_id, new_id); + prefs_common_junk_folder_rename_path(old_id, new_id); + g_free(new_id); + g_free(old_id); + g_free(old_path); + + if (folderview->opened) + open_path = gtk_tree_row_reference_get_path(folderview->opened); + gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), &iter, + sel_path); + if (sel_path && open_path && + (gtk_tree_path_compare(open_path, sel_path) == 0 || + gtk_tree_path_is_ancestor(sel_path, open_path))) { + summary_clear_all(folderview->summaryview); + gtk_tree_row_reference_free(folderview->opened); + folderview->opened = NULL; + } + gtk_tree_path_free(open_path); + gtk_tree_path_free(sel_path); + + gtk_tree_store_remove(folderview->store, &iter); + if (folderview_insert_item_recursive(folderview, item, &iter)) { + gtkut_tree_view_expand_parent_all + (GTK_TREE_VIEW(folderview->treeview), &iter); + } + + folder_write_list(); +} + +static void folderview_delete_folder_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + Folder *folder; + FolderItem *item; + gchar *message, *name; + AlertValue avalue; + gchar *old_path; + gchar *old_id; + GtkTreePath *sel_path, *open_path = NULL; + GtkTreeIter iter; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->path != NULL); + g_return_if_fail(item->folder != NULL); + + sel_path = gtk_tree_row_reference_get_path(folderview->selected); + g_return_if_fail(sel_path != NULL); + + folder = item->folder; + + name = trim_string(item->name, 32); + AUTORELEASE_STR(name, {g_free(name); gtk_tree_path_free(sel_path); return;}); + if (item->stype == F_VIRTUAL) { + message = g_strdup_printf + (_("Delete the search folder '%s' ?\n" + "The real messages are not deleted."), name); + avalue = alertpanel_full(_("Delete search folder"), message, + ALERT_WARNING, G_ALERTALTERNATE, FALSE, + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + } else { + message = g_strdup_printf + (_("All folders and messages under '%s' will be permanently deleted.\n" + "Recovery will not be possible.\n\n" + "Do you really want to delete?"), name); + avalue = alertpanel_full(_("Delete folder"), message, + ALERT_WARNING, G_ALERTALTERNATE, FALSE, + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + } + g_free(message); + if (avalue != G_ALERTDEFAULT) { + gtk_tree_path_free(sel_path); + return; + } + + old_path = g_strdup(item->path); + old_id = folder_item_get_identifier(item); + + if (folderview->opened) + open_path = gtk_tree_row_reference_get_path(folderview->opened); + gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), &iter, + sel_path); + if (sel_path && open_path && + (gtk_tree_path_compare(open_path, sel_path) == 0 || + gtk_tree_path_is_ancestor(sel_path, open_path))) { + summary_clear_all(folderview->summaryview); + gtk_tree_row_reference_free(folderview->opened); + folderview->opened = NULL; + } + gtk_tree_path_free(open_path); + gtk_tree_path_free(sel_path); + + if (item->stype == F_VIRTUAL) { + if (virtual_get_class()->remove_folder(folder, item) < 0) { + alertpanel_error(_("Can't remove the folder '%s'."), + name); + g_free(old_id); + g_free(old_path); + return; + } + } else if (folder->klass->remove_folder(folder, item) < 0) { + alertpanel_error(_("Can't remove the folder '%s'."), name); + g_free(old_id); + g_free(old_path); + return; + } + + if (folder_get_default_folder() == folder) + filter_list_delete_path(old_path); + filter_list_delete_path(old_id); + g_free(old_id); + g_free(old_path); + + gtk_tree_store_remove(folderview->store, &iter); + + folder_write_list(); +} + +static void folderview_empty_trash_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + FolderItem *item; + Folder *folder; + GtkTreePath *sel_path, *open_path; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->path != NULL); + g_return_if_fail(item->folder != NULL); + + folder = item->folder; + + if (item->stype != F_TRASH && item->stype != F_JUNK) return; + + if (folderview->selection_locked || + summary_is_locked(folderview->summaryview)) return; + folderview->selection_locked = TRUE; + + sel_path = gtk_tree_row_reference_get_path(folderview->selected); + + if (item->stype == F_TRASH) { + if (alertpanel(_("Empty trash"), + _("Delete all messages in the trash folder?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL) != G_ALERTDEFAULT) { + gtk_tree_path_free(sel_path); + folderview->selection_locked = FALSE; + return; + } + } else { + if (alertpanel(_("Empty junk"), + _("Delete all messages in the junk folder?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL) != G_ALERTDEFAULT) { + gtk_tree_path_free(sel_path); + folderview->selection_locked = FALSE; + return; + } + } + + summary_lock(folderview->summaryview); + procmsg_empty_trash(item); + summary_unlock(folderview->summaryview); + statusbar_pop_all(); + folderview_update_item(item, TRUE); + trayicon_set_tooltip(NULL); + trayicon_set_notify(FALSE); + + open_path = gtk_tree_row_reference_get_path(folderview->opened); + if (open_path && sel_path && + gtk_tree_path_compare(open_path, sel_path) == 0) + gtk_widget_grab_focus(folderview->treeview); + gtk_tree_path_free(open_path); + gtk_tree_path_free(sel_path); + + folderview->selection_locked = FALSE; +} + +static void folderview_remove_mailbox_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + FolderItem *item; + gchar *name; + gchar *message; + AlertValue avalue; + GtkTreePath *sel_path; + GtkTreeIter iter; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->folder != NULL); + if (item->parent) return; + + sel_path = gtk_tree_row_reference_get_path(folderview->selected); + + name = trim_string(item->folder->name, 32); + message = g_strdup_printf + (_("Really remove the mailbox `%s' ?\n" + "(The messages are NOT deleted from the disk)"), name); + avalue = alertpanel_full(_("Remove mailbox"), message, + ALERT_WARNING, G_ALERTALTERNATE, FALSE, + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + g_free(message); + g_free(name); + if (avalue != G_ALERTDEFAULT) { + gtk_tree_path_free(sel_path); + return; + } + + if (folderview->summaryview->folder_item && + folderview->summaryview->folder_item->folder == item->folder) { + summary_clear_all(folderview->summaryview); + gtk_tree_row_reference_free(folderview->opened); + folderview->opened = NULL; + } + folder_destroy(item->folder); + + if (sel_path) { + gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), + &iter, sel_path); + gtk_tree_path_free(sel_path); + gtk_tree_store_remove(folderview->store, &iter); + } + + folder_write_list(); +} + +static void folderview_rm_imap_server_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + FolderItem *item; + PrefsAccount *account; + gchar *name; + gchar *message; + AlertValue avalue; + GtkTreePath *sel_path; + GtkTreeIter iter; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->folder != NULL); + g_return_if_fail(FOLDER_TYPE(item->folder) == F_IMAP); + g_return_if_fail(item->folder->account != NULL); + + sel_path = gtk_tree_row_reference_get_path(folderview->selected); + + name = trim_string(item->folder->name, 32); + message = g_strdup_printf(_("Really delete IMAP4 account `%s'?"), name); + avalue = alertpanel_full(_("Delete IMAP4 account"), message, + ALERT_WARNING, G_ALERTALTERNATE, FALSE, + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + g_free(message); + g_free(name); + + if (avalue != G_ALERTDEFAULT) { + gtk_tree_path_free(sel_path); + return; + } + + if (folderview->summaryview->folder_item && + folderview->summaryview->folder_item->folder == item->folder) { + summary_clear_all(folderview->summaryview); + gtk_tree_row_reference_free(folderview->opened); + folderview->opened = NULL; + } + + account = item->folder->account; + folder_destroy(item->folder); + account_destroy(account); + account_write_config_all(); + + if (sel_path) { + gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), + &iter, sel_path); + gtk_tree_path_free(sel_path); + gtk_tree_store_remove(folderview->store, &iter); + } + + account_set_menu(); + main_window_reflect_prefs_all(); + folder_write_list(); +} + +static void folderview_new_news_group_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + GtkTreeModel *model = GTK_TREE_MODEL(folderview->store); + Folder *folder; + FolderItem *item; + FolderItem *rootitem = NULL; + FolderItem *newitem; + GSList *new_subscr; + GSList *cur; + GNode *gnode; + GtkTreePath *server_path; + GtkTreeIter iter, root; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + folder = item->folder; + g_return_if_fail(folder != NULL); + g_return_if_fail(FOLDER_TYPE(folder) == F_NEWS); + g_return_if_fail(folder->account != NULL); + + server_path = gtk_tree_row_reference_get_path(folderview->selected); + g_return_if_fail(server_path != NULL); + gtk_tree_model_get_iter(model, &iter, server_path); + gtk_tree_path_free(server_path); + + if (!gtk_tree_model_iter_parent(model, &root, &iter)) + root = iter; + + gtk_tree_model_get(model, &root, COL_FOLDER_ITEM, &rootitem, -1); + + new_subscr = subscribe_dialog(folder); + + /* remove unsubscribed newsgroups */ + for (gnode = folder->node->children; gnode != NULL; ) { + GNode *next = gnode->next; + GtkTreeIter found; + + item = FOLDER_ITEM(gnode->data); + if (g_slist_find_custom(new_subscr, item->path, + (GCompareFunc)g_ascii_strcasecmp) + != NULL) { + gnode = next; + continue; + } + + if (!gtkut_tree_model_find_by_column_data + (model, &found, &root, COL_FOLDER_ITEM, item)) { + gnode = next; + continue; + } + + if (folderview->summaryview->folder_item == item) { + summary_clear_all(folderview->summaryview); + gtk_tree_row_reference_free(folderview->opened); + folderview->opened = NULL; + } + + folder_item_remove(item); + gtk_tree_store_remove(folderview->store, &found); + + gnode = next; + } + + /* add subscribed newsgroups */ + for (cur = new_subscr; cur != NULL; cur = cur->next) { + gchar *name = (gchar *)cur->data; + + if (folder_find_child_item_by_name(rootitem, name) != NULL) + continue; + + newitem = folder_item_new(name, name); + folder_item_append(rootitem, newitem); + folderview_append_item(folderview, NULL, newitem, TRUE); + } + + if (new_subscr) { + server_path = gtk_tree_model_get_path(model, &root); + gtk_tree_view_expand_row(GTK_TREE_VIEW(folderview->treeview), + server_path, FALSE); + gtk_tree_path_free(server_path); + } + + slist_free_strings(new_subscr); + g_slist_free(new_subscr); + + folder_write_list(); +} + +static void folderview_rm_news_group_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + FolderItem *item; + gchar *name; + gchar *message; + AlertValue avalue; + GtkTreePath *sel_path, *open_path = NULL; + GtkTreeIter iter; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + if (item->stype == F_VIRTUAL) { + folderview_delete_folder_cb(folderview, 0, widget); + return; + } + + g_return_if_fail(item->folder != NULL); + g_return_if_fail(FOLDER_TYPE(item->folder) == F_NEWS); + g_return_if_fail(item->folder->account != NULL); + + sel_path = gtk_tree_row_reference_get_path(folderview->selected); + g_return_if_fail(sel_path != NULL); + + name = trim_string_before(item->path, 32); + message = g_strdup_printf(_("Really delete newsgroup `%s'?"), name); + avalue = alertpanel_full(_("Delete newsgroup"), message, + ALERT_WARNING, G_ALERTALTERNATE, FALSE, + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + g_free(message); + g_free(name); + if (avalue != G_ALERTDEFAULT) { + gtk_tree_path_free(sel_path); + return; + } + + if (folderview->opened) + open_path = gtk_tree_row_reference_get_path(folderview->opened); + gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), &iter, + sel_path); + if (open_path && sel_path && + gtk_tree_path_compare(open_path, sel_path) == 0) { + summary_clear_all(folderview->summaryview); + gtk_tree_row_reference_free(folderview->opened); + folderview->opened = NULL; + } + gtk_tree_path_free(open_path); + gtk_tree_path_free(sel_path); + + folder_item_remove(item); + gtk_tree_store_remove(folderview->store, &iter); + folder_write_list(); +} + +static void folderview_rm_news_server_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + FolderItem *item; + PrefsAccount *account; + gchar *name; + gchar *message; + AlertValue avalue; + GtkTreePath *sel_path; + GtkTreeIter iter; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->folder != NULL); + g_return_if_fail(FOLDER_TYPE(item->folder) == F_NEWS); + g_return_if_fail(item->folder->account != NULL); + + sel_path = gtk_tree_row_reference_get_path(folderview->selected); + + name = trim_string(item->folder->name, 32); + message = g_strdup_printf(_("Really delete news account `%s'?"), name); + avalue = alertpanel_full(_("Delete news account"), message, + ALERT_WARNING, G_ALERTALTERNATE, FALSE, + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + g_free(message); + g_free(name); + + if (avalue != G_ALERTDEFAULT) { + gtk_tree_path_free(sel_path); + return; + } + + if (folderview->summaryview->folder_item && + folderview->summaryview->folder_item->folder == item->folder) { + summary_clear_all(folderview->summaryview); + gtk_tree_row_reference_free(folderview->opened); + folderview->opened = NULL; + } + + account = item->folder->account; + folder_destroy(item->folder); + account_destroy(account); + account_write_config_all(); + + if (sel_path) { + gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), + &iter, sel_path); + gtk_tree_path_free(sel_path); + gtk_tree_store_remove(folderview->store, &iter); + } + + account_set_menu(); + main_window_reflect_prefs_all(); + folder_write_list(); +} + +static void folderview_search_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + FolderItem *item; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + if (item->stype == F_VIRTUAL) { + GtkTreePath *sel_path, *open_path; + + sel_path = gtk_tree_row_reference_get_path + (folderview->selected); + open_path = gtk_tree_row_reference_get_path(folderview->opened); + + if (prefs_search_folder_open(item)) { + if (sel_path && open_path && + gtk_tree_path_compare(open_path, sel_path) == 0) { + GtkTreeRowReference *row; + row = gtk_tree_row_reference_copy(folderview->opened); + folderview_unselect(folderview); + summary_clear_all(folderview->summaryview); + folderview_select_row_ref(folderview, row); + gtk_tree_row_reference_free(row); + } + } + + gtk_tree_path_free(open_path); + gtk_tree_path_free(sel_path); + } else + query_search(item); +} + +static void folderview_property_cb(FolderView *folderview, guint action, + GtkWidget *widget) +{ + FolderItem *item; + + item = folderview_get_selected_item(folderview); + if (!item) + return; + + g_return_if_fail(item->folder != NULL); + + if (item->parent == NULL && item->folder->account) + account_open(item->folder->account); + else + prefs_folder_item_open(item); +} + +static gint auto_expand_timeout(gpointer data) +{ + FolderView *folderview = data; + GtkTreeView *treeview = GTK_TREE_VIEW(folderview->treeview); + GtkTreePath *path = NULL; + gint ret; + + gdk_threads_enter(); + + gtk_tree_view_get_drag_dest_row(treeview, &path, NULL); + + if (path) { + gtk_tree_view_expand_row(treeview, path, FALSE); + gtk_tree_path_free(path); + folderview->expand_timeout = 0; + + ret = FALSE; + } else + ret = TRUE; + + gdk_threads_leave(); + + return ret; +} + +static void remove_auto_expand_timeout(FolderView *folderview) +{ + if (folderview->expand_timeout != 0) { + g_source_remove(folderview->expand_timeout); + folderview->expand_timeout = 0; + } +} + +static gint auto_scroll_timeout(gpointer data) +{ + FolderView *folderview = data; + + gdk_threads_enter(); + + gtkut_tree_view_vertical_autoscroll + (GTK_TREE_VIEW(folderview->treeview)); + + gdk_threads_leave(); + + return TRUE; +} + +static void remove_auto_scroll_timeout(FolderView *folderview) +{ + if (folderview->scroll_timeout != 0) { + g_source_remove(folderview->scroll_timeout); + folderview->scroll_timeout = 0; + } +} + +static gboolean folderview_drag_motion_cb(GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time, + FolderView *folderview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(folderview->store); + GtkTreePath *path = NULL, *prev_path = NULL; + GtkTreeIter iter; + FolderItem *item = NULL, *src_item = NULL; + gboolean acceptable = FALSE; + + if (gtk_tree_view_get_dest_row_at_pos + (GTK_TREE_VIEW(widget), x, y, &path, NULL)) { + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, COL_FOLDER_ITEM, &item, -1); + src_item = folderview->summaryview->folder_item; + if (src_item && src_item != item && + src_item->stype != F_QUEUE && item->stype != F_QUEUE && + item->stype != F_VIRTUAL) + acceptable = FOLDER_ITEM_CAN_ADD(item); + } else + remove_auto_expand_timeout(folderview); + + if (summary_is_locked(folderview->summaryview)) + acceptable = FALSE; + + gtk_tree_view_get_drag_dest_row(GTK_TREE_VIEW(widget), + &prev_path, NULL); + if (!path || (prev_path && gtk_tree_path_compare(path, prev_path) != 0)) + remove_auto_expand_timeout(folderview); + if (prev_path) + gtk_tree_path_free(prev_path); + + gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(widget), path, + GTK_TREE_VIEW_DROP_INTO_OR_AFTER); + + if (path) { + if (folderview->expand_timeout == 0) { + folderview->expand_timeout = + g_timeout_add(1000, auto_expand_timeout, + folderview); + } else if (folderview->scroll_timeout == 0) { + folderview->scroll_timeout = + g_timeout_add(150, auto_scroll_timeout, + folderview); + } + } + +#ifdef G_OS_WIN32 + /* Win32 hack: somehow context->actions is not properly set on Win32 */ + { + GdkWindow *rootwin; + GdkModifierType state; + + rootwin = gtk_widget_get_root_window(widget); + gdk_window_get_pointer(rootwin, NULL, NULL, &state); + if ((state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) == 0) + context->actions = GDK_ACTION_MOVE | GDK_ACTION_COPY; + } +#endif + + if (acceptable) { + if ((context->actions & GDK_ACTION_MOVE) != 0 && + FOLDER_ITEM_CAN_ADD(src_item)) + gdk_drag_status(context, GDK_ACTION_MOVE, time); + else if ((context->actions & GDK_ACTION_COPY) != 0) + gdk_drag_status(context, GDK_ACTION_COPY, time); + else if ((context->actions & GDK_ACTION_LINK) != 0) + gdk_drag_status(context, GDK_ACTION_LINK, time); + else + gdk_drag_status(context, 0, time); + } else + gdk_drag_status(context, 0, time); + + if (path) + gtk_tree_path_free(path); + + return TRUE; +} + +static void folderview_drag_leave_cb(GtkWidget *widget, + GdkDragContext *context, + guint time, + FolderView *folderview) +{ + remove_auto_expand_timeout(folderview); + remove_auto_scroll_timeout(folderview); + + gtk_tree_view_set_drag_dest_row + (GTK_TREE_VIEW(widget), NULL, GTK_TREE_VIEW_DROP_INTO_OR_AFTER); +} + +static void folderview_drag_received_cb(GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + FolderView *folderview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(folderview->store); + GtkTreePath *path = NULL; + GtkTreeIter iter; + FolderItem *item = NULL, *src_item; + + remove_auto_expand_timeout(folderview); + remove_auto_scroll_timeout(folderview); + + if (!gtk_tree_view_get_dest_row_at_pos + (GTK_TREE_VIEW(widget), x, y, &path, NULL)) + return; + + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, COL_FOLDER_ITEM, &item, -1); + src_item = folderview->summaryview->folder_item; + + if (FOLDER_ITEM_CAN_ADD(item) && src_item && src_item != item && + src_item->stype != F_QUEUE && item->stype != F_QUEUE && + item->stype != F_VIRTUAL) { + if ((context->actions & GDK_ACTION_MOVE) != 0 && + FOLDER_ITEM_CAN_ADD(src_item)) { + summary_move_selected_to(folderview->summaryview, item); + context->action = 0; + gtk_drag_finish(context, TRUE, FALSE, time); + } else if ((context->actions & GDK_ACTION_COPY) != 0) { + summary_copy_selected_to(folderview->summaryview, item); + gtk_drag_finish(context, TRUE, FALSE, time); + } else + gtk_drag_finish(context, FALSE, FALSE, time); + } else + gtk_drag_finish(context, FALSE, FALSE, time); + + gtk_tree_path_free(path); +} + +static gint folderview_folder_name_compare(GtkTreeModel *model, + GtkTreeIter *a, GtkTreeIter *b, + gpointer data) +{ + FolderItem *item_a = NULL, *item_b = NULL; + + gtk_tree_model_get(model, a, COL_FOLDER_ITEM, &item_a, -1); + gtk_tree_model_get(model, b, COL_FOLDER_ITEM, &item_b, -1); + + return folder_item_compare(item_a, item_b); +} diff --git a/src/folderview.h b/src/folderview.h new file mode 100644 index 0000000..3d9aaa9 --- /dev/null +++ b/src/folderview.h @@ -0,0 +1,121 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2010 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __FOLDERVIEW_H__ +#define __FOLDERVIEW_H__ + +#include +#include +#include +#include + +#include "folder.h" + +typedef struct _FolderView FolderView; + +#include "mainwindow.h" +#include "summaryview.h" + +struct _FolderView +{ + GtkWidget *vbox; + + GtkWidget *scrolledwin; + + GtkWidget *treeview; + + GtkTreeStore *store; + GtkTreeSelection *selection; + + GtkWidget *mail_popup; + GtkWidget *imap_popup; + GtkWidget *news_popup; + + GtkItemFactory *mail_factory; + GtkItemFactory *imap_factory; + GtkItemFactory *news_factory; + + GtkTreeRowReference *selected; + GtkTreeRowReference *opened; + GtkTreeRowReference *prev_selected; + + gboolean display_folder_unread; + + gboolean open_folder; + + guint expand_timeout; + guint scroll_timeout; + + gboolean selection_locked; + + GdkColor color_new; + GdkColor color_noselect; + + MainWindow *mainwin; + SummaryView *summaryview; +}; + +FolderView *folderview_create (void); +void folderview_init (FolderView *folderview); +void folderview_reflect_prefs (FolderView *folderview); + +void folderview_add_sub_widget (FolderView *folderview, + GtkWidget *widget); + +FolderView *folderview_get (void); + +void folderview_set (FolderView *folderview); +void folderview_set_all (void); + +void folderview_select (FolderView *folderview, + FolderItem *item); +void folderview_unselect (FolderView *folderview); +void folderview_select_next_unread (FolderView *folderview); + +FolderItem *folderview_get_selected_item(FolderView *folderview); + +void folderview_set_opened_item (FolderView *folderview, + FolderItem *item); +void folderview_update_opened_msg_num (FolderView *folderview); + +gboolean folderview_append_item (FolderView *folderview, + GtkTreeIter *iter, + FolderItem *item, + gboolean expand_parent); + +gint folderview_check_new (Folder *folder); +gint folderview_check_new_item (FolderItem *item); +gint folderview_check_new_all (void); + +void folderview_update_item (FolderItem *item, + gboolean update_summary); +void folderview_update_item_foreach (GHashTable *table, + gboolean update_summary); +void folderview_update_all_updated (gboolean update_summary); + +void folderview_new_folder (FolderView *folderview); +void folderview_rename_folder (FolderView *folderview); +void folderview_move_folder (FolderView *folderview); +void folderview_delete_folder (FolderView *folderview); + +void folderview_check_new_selected (FolderView *folderview); +void folderview_remove_mailbox (FolderView *folderview); +void folderview_rebuild_tree (FolderView *folderview); + +#endif /* __FOLDERVIEW_H__ */ diff --git a/src/gtkshruler.c b/src/gtkshruler.c new file mode 100644 index 0000000..546affd --- /dev/null +++ b/src/gtkshruler.c @@ -0,0 +1,228 @@ +/* GtkSHRuler + * + * Copyright (C) 2000-2005 Alfons Hoogervorst & The Sylpheed Claws Team + * Copyright (C) 2007 Hiroyuki Yamamoto + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* I derived this class from hruler. S in HRuler could be read as + * Sylpheed (sylpheed.sraoss.jp), but also [S]ettable HRuler. + * I basically ripped apart the draw_ticks member of HRuler; it + * now draws the ticks at ruler->max_size. so gtk_ruler_set_range's + * last parameter has the distance between two ticks (which is + * the width of the fixed font character! + * + * -- Alfons + */ + +#include +#include +#include +#include +#include "gtkshruler.h" + +#define RULER_HEIGHT 14 +#define MINIMUM_INCR 5 +#define MAXIMUM_SUBDIVIDE 5 +#define MAXIMUM_SCALES 10 + +#define ROUND(x) ((int) ((x) + 0.5)) + +static void gtk_shruler_class_init (GtkSHRulerClass *klass); +static void gtk_shruler_init (GtkSHRuler *hruler); +static void gtk_shruler_draw_ticks (GtkRuler *ruler); +#if 0 +static void gtk_shruler_draw_pos (GtkRuler *ruler); +#endif + +GType +gtk_shruler_get_type(void) +{ + static GType shruler_type = 0; + + if ( !shruler_type ) { + static const GTypeInfo shruler_info = { + sizeof (GtkSHRulerClass), + + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + + (GClassInitFunc) gtk_shruler_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + + sizeof (GtkSHRuler), + 0, /* n_preallocs */ + (GInstanceInitFunc) gtk_shruler_init, + }; + /* inherit from GtkHRuler */ + shruler_type = g_type_register_static (GTK_TYPE_HRULER, "GtkSHRuler", &shruler_info, (GTypeFlags)0); + } + return shruler_type; +} + +static void +gtk_shruler_class_init(GtkSHRulerClass * klass) +{ + GtkWidgetClass * widget_class; + GtkRulerClass * hruler_class; + + widget_class = (GtkWidgetClass*) klass; + hruler_class = (GtkRulerClass*) klass; + + /* just neglect motion notify events */ + widget_class->motion_notify_event = NULL /* gtk_shruler_motion_notify */; + + /* we want the old ruler draw ticks... */ + /* ruler_class->draw_ticks = gtk_hruler_draw_ticks; */ + hruler_class->draw_ticks = gtk_shruler_draw_ticks; + + /* unimplemented draw pos */ + hruler_class->draw_pos = NULL; +/* + hruler_class->draw_pos = gtk_shruler_draw_pos; +*/ +} + +static void +gtk_shruler_init (GtkSHRuler * shruler) +{ + GtkWidget * widget; + + widget = GTK_WIDGET (shruler); + widget->requisition.width = widget->style->xthickness * 2 + 1; + widget->requisition.height = widget->style->ythickness * 2 + RULER_HEIGHT; + shruler->start_pos = 0; +} + + +GtkWidget* +gtk_shruler_new(void) +{ + return GTK_WIDGET( g_object_new( gtk_shruler_get_type(), NULL ) ); +} + +void +gtk_shruler_set_start_pos(GtkSHRuler *ruler, gint pos) +{ + g_return_if_fail (GTK_IS_SHRULER (ruler)); + + ruler->start_pos = pos; +} + +gint +gtk_shruler_get_start_pos(GtkSHRuler *ruler) +{ + g_return_val_if_fail (GTK_IS_SHRULER (ruler), 0); + + return ruler->start_pos; +} + +static void +gtk_shruler_draw_ticks(GtkRuler *ruler) +{ + GtkWidget *widget; + GdkGC *gc, *bg_gc; + gint i; + gint width, height; + gint xthickness; + gint ythickness; + gint pos; + + g_return_if_fail (ruler != NULL); + g_return_if_fail (GTK_IS_HRULER (ruler)); + + if (!GTK_WIDGET_DRAWABLE (ruler)) + return; + + widget = GTK_WIDGET (ruler); + + gc = widget->style->fg_gc[GTK_STATE_NORMAL]; + bg_gc = widget->style->bg_gc[GTK_STATE_NORMAL]; + + xthickness = widget->style->xthickness; + ythickness = widget->style->ythickness; + + width = widget->allocation.width; + height = widget->allocation.height - ythickness * 2; + + gtk_paint_box (widget->style, ruler->backing_store, + GTK_STATE_NORMAL, GTK_SHADOW_OUT, + NULL, widget, "hruler", + 0, 0, + widget->allocation.width, widget->allocation.height); + +#if 0 + gdk_draw_line (ruler->backing_store, gc, + xthickness, + height + ythickness, + widget->allocation.width - xthickness, + height + ythickness); +#endif + + /* assume ruler->max_size has the char width */ + /* i is increment of char_width, pos is label number + * y position is based on height of widget itself */ + for ( pos = GTK_SHRULER(ruler)->start_pos, i = 0; pos < widget->allocation.width - xthickness; pos += ruler->max_size, i++ ) { + gint length = height / 8; + + if ( i % 10 == 0 ) length = ( 2 * height / 3 ); + else if ( i % 5 == 0 ) length = ( height / 3 ); + + gdk_draw_line(ruler->backing_store, gc, + pos, height + ythickness, + pos, height - length); + + if ( i % 10 == 0 ) { + gchar buf[8]; + PangoLayout *layout; + + /* draw label */ + g_snprintf(buf, sizeof buf, "%d", i); + + layout = gtk_widget_create_pango_layout + (GTK_WIDGET(ruler), buf); + + gdk_draw_layout(ruler->backing_store, gc, pos + 2, + 0, layout); + + g_object_unref(layout); + } + } +} + +/* gtk_ruler_set_pos() - does not work yet, need to reimplement + * gtk_ruler_draw_pos(). */ +void +gtk_shruler_set_pos(GtkSHRuler * ruler, gfloat pos) +{ + GtkRuler * ruler_; + g_return_if_fail( ruler != NULL ); + + ruler_ = GTK_RULER(ruler); + + if ( pos < ruler_->lower ) + pos = ruler_->lower; + if ( pos > ruler_->upper ) + pos = ruler_->upper; + + ruler_->position = pos; + + /* Make sure the ruler has been allocated already */ + if ( ruler_->backing_store != NULL ) + gtk_ruler_draw_pos(ruler_); +} diff --git a/src/gtkshruler.h b/src/gtkshruler.h new file mode 100644 index 0000000..abe56bf --- /dev/null +++ b/src/gtkshruler.h @@ -0,0 +1,71 @@ +/* GTKSHRuler + * Copyright (C) 2000-2004 Alfons Hoogervorst & The Sylpheed Claws Team + * Copyright (C) 2007 Hiroyuki Yamamoto + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_SHRULER_H__ +#define __GTK_SHRULER_H__ + + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GTK_SHRULER(obj) (GTK_CHECK_CAST ((obj), gtk_shruler_get_type (), GtkSHRuler)) +#define GTK_SHRULER_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), gtk_shruler_get_type (), GtkSHRulerClass)) +#define GTK_IS_SHRULER(obj) (GTK_CHECK_TYPE ((obj), gtk_shruler_get_type ())) + + +typedef struct _GtkSHRuler GtkSHRuler; +typedef struct _GtkSHRulerClass GtkSHRulerClass; + +struct _GtkSHRuler +{ + GtkHRuler ruler; + + gint start_pos; +}; + +struct _GtkSHRulerClass +{ + GtkHRulerClass parent_class; +}; + + +GType gtk_shruler_get_type (void); +GtkWidget* gtk_shruler_new (void); + +void gtk_shruler_set_start_pos (GtkSHRuler *ruler, + gint pos); +gint gtk_shruler_get_start_pos (GtkSHRuler *ruler); + +void gtk_shruler_set_pos (GtkSHRuler *ruler, + gfloat pos); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GTK_SHRULER_H__ */ diff --git a/src/gtkutils.c b/src/gtkutils.c new file mode 100644 index 0000000..b1a488a --- /dev/null +++ b/src/gtkutils.c @@ -0,0 +1,1119 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef G_OS_WIN32 +# include +#endif + +#include "gtkutils.h" +#include "utils.h" +#include "codeconv.h" +#include "menu.h" + +gboolean gtkut_get_str_size(GtkWidget *widget, const gchar *str, + gint *width, gint *height) +{ + PangoLayout *layout; + + g_return_val_if_fail(GTK_IS_WIDGET(widget), FALSE); + + layout = gtk_widget_create_pango_layout(widget, str); + g_return_val_if_fail(layout, FALSE); + pango_layout_get_pixel_size(layout, width, height); + g_object_unref(layout); + + return TRUE; +} + +gboolean gtkut_get_font_size(GtkWidget *widget, gint *width, gint *height) +{ + const gchar *str = "Abcdef"; + gboolean ret; + + ret = gtkut_get_str_size(widget, str, width, height); + if (ret && width) + *width = *width / g_utf8_strlen(str, -1); + + return ret; +} + +PangoFontDescription *gtkut_get_default_font_desc(void) +{ + static PangoFontDescription *font_desc = NULL; + + if (!font_desc) { + GtkWidget *window; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_ensure_style(window); + font_desc = pango_font_description_copy + (window->style->font_desc); + gtk_object_sink(GTK_OBJECT(window)); + } + + return pango_font_description_copy(font_desc); +} + +void gtkut_widget_set_small_font_size(GtkWidget *widget) +{ + PangoFontDescription *font_desc; + gint size; + + g_return_if_fail(widget != NULL); + g_return_if_fail(widget->style != NULL); + + font_desc = gtkut_get_default_font_desc(); + size = pango_font_description_get_size(font_desc); + pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL); + gtk_widget_modify_font(widget, font_desc); + pango_font_description_free(font_desc); +} + +gboolean gtkut_font_can_load(const gchar *str) +{ +#ifdef G_OS_WIN32 + PangoFontDescription *desc; + PangoContext *context; + PangoFont *font; + gboolean can_load = FALSE; + + desc = pango_font_description_from_string(str); + if (desc) { + context = pango_win32_get_context(); + font = pango_context_load_font(context, desc); + if (font) { + can_load = TRUE; + g_object_unref(font); + } + g_object_unref(context); + pango_font_description_free(desc); + } + + return can_load; +#elif defined(__APPLE__) + PangoFontDescription *desc; + PangoContext *context; + PangoFont *font; + gboolean can_load = FALSE; + + desc = pango_font_description_from_string(str); + if (desc) { + context = gdk_pango_context_get_for_screen + (gdk_screen_get_default()); + font = pango_context_load_font(context, desc); + if (font) { + can_load = TRUE; + g_object_unref(font); + } + g_object_unref(context); + pango_font_description_free(desc); + } + + return can_load; +#else + return FALSE; +#endif +} + +static gdouble system_dpi = 0.0; + +gdouble gtkut_get_dpi(void) +{ + gdouble dp, di; + + if (system_dpi > 0.0) + return system_dpi; + + //dpi = gdk_screen_get_resolution(gdk_screen_get_default()); + + dp = gdk_screen_get_height(gdk_screen_get_default()); + di = gdk_screen_get_height_mm(gdk_screen_get_default()) / 25.4; + system_dpi = dp / di; + + debug_print("gtkut_get_dpi: dpi: %f\n", system_dpi); + return system_dpi; +} + +gdouble gtkut_get_dpi_multiplier(void) +{ + gdouble dpi; + gdouble mul; + + dpi = gtkut_get_dpi(); + + /* 96 / 120 / 144 dpi */ + if (dpi > 142) + mul = 1.5; + else if (dpi > 118) + mul = 1.25; + else + mul = 1.0; + + return mul; +} + +void gtkut_set_dpi(gdouble dpi) +{ + system_dpi = dpi; +} + +void gtkut_convert_int_to_gdk_color(gint rgbvalue, GdkColor *color) +{ + g_return_if_fail(color != NULL); + + color->pixel = 0L; + color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0) * 65535.0); + color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0) * 65535.0); + color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0) * 65535.0); +} + +static gboolean reverse_order = FALSE; + +void gtkut_stock_button_set_set_reverse(gboolean reverse) +{ + reverse_order = reverse; +} + +void gtkut_stock_button_set_create(GtkWidget **bbox, + GtkWidget **button1, const gchar *label1, + GtkWidget **button2, const gchar *label2, + GtkWidget **button3, const gchar *label3) +{ + g_return_if_fail(bbox != NULL); + g_return_if_fail(button1 != NULL); + + *bbox = gtk_hbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(*bbox), 6 * gtkut_get_dpi_multiplier()); + + if (button3) { + *button3 = gtk_button_new_from_stock(label3); + GTK_WIDGET_SET_FLAGS(*button3, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(*bbox), *button3, FALSE, FALSE, 0); + gtk_widget_show(*button3); + } + + if (button2) { + *button2 = gtk_button_new_from_stock(label2); + GTK_WIDGET_SET_FLAGS(*button2, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(*bbox), *button2, FALSE, FALSE, 0); + gtk_widget_show(*button2); + } + + *button1 = gtk_button_new_from_stock(label1); + GTK_WIDGET_SET_FLAGS(*button1, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(*bbox), *button1, FALSE, FALSE, 0); + gtk_widget_show(*button1); + + if (reverse_order) + gtkut_box_set_reverse_order(GTK_BOX(*bbox), TRUE); +} + +void gtkut_box_set_reverse_order(GtkBox *box, gboolean reverse) +{ + GList *cur; + GList *new_order = NULL; + gint pos = 0; + gboolean is_reversed; + + g_return_if_fail(box != NULL); + + is_reversed = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(box), "reverse-order")); + if (is_reversed == reverse) + return; + g_object_set_data(G_OBJECT(box), "reverse-order", + GINT_TO_POINTER(reverse)); + + for (cur = box->children; cur != NULL; cur = cur->next) { + GtkBoxChild *cinfo = cur->data; + new_order = g_list_prepend(new_order, cinfo->widget); + } + + for (cur = new_order; cur != NULL; cur = cur->next) { + GtkWidget *child = cur->data; + gtk_box_reorder_child(box, child, pos++); + } + + g_list_free(new_order); +} + +static void combo_button_size_request(GtkWidget *widget, + GtkRequisition *requisition, + gpointer data) +{ + ComboButton *combo = (ComboButton *)data; + + if (combo->arrow->allocation.height != requisition->height) + gtk_widget_set_size_request(combo->arrow, + -1, requisition->height); +} + +static void combo_button_enter(GtkWidget *widget, gpointer data) +{ + ComboButton *combo = (ComboButton *)data; + + if (GTK_WIDGET_STATE(combo->arrow) != GTK_STATE_PRELIGHT) { + gtk_widget_set_state(combo->arrow, GTK_STATE_PRELIGHT); + gtk_widget_queue_draw(combo->arrow); + } + if (GTK_WIDGET_STATE(combo->button) != GTK_STATE_PRELIGHT) { + gtk_widget_set_state(combo->button, GTK_STATE_PRELIGHT); + gtk_widget_queue_draw(combo->button); + } +} + +static void combo_button_leave(GtkWidget *widget, gpointer data) +{ + ComboButton *combo = (ComboButton *)data; + + if (GTK_WIDGET_STATE(combo->arrow) != GTK_STATE_NORMAL) { + gtk_widget_set_state(combo->arrow, GTK_STATE_NORMAL); + gtk_widget_queue_draw(combo->arrow); + } + if (GTK_WIDGET_STATE(combo->button) != GTK_STATE_NORMAL) { + gtk_widget_set_state(combo->button, GTK_STATE_NORMAL); + gtk_widget_queue_draw(combo->button); + } +} + +static gint combo_button_arrow_pressed(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + ComboButton *combo = (ComboButton *)data; + + if (!event) return FALSE; + + gtk_menu_popup(GTK_MENU(combo->menu), NULL, NULL, + menu_button_position, combo->button, + event->button, event->time); + + return TRUE; +} + +static void combo_button_destroy(GtkWidget *widget, gpointer data) +{ + ComboButton *combo = (ComboButton *)data; + + gtk_object_destroy(GTK_OBJECT(combo->factory)); + g_free(combo); +} + +ComboButton *gtkut_combo_button_create(GtkWidget *button, + GtkItemFactoryEntry *entries, + gint n_entries, const gchar *path, + gpointer data) +{ + ComboButton *combo; + GtkWidget *arrow; + + combo = g_new0(ComboButton, 1); + + combo->arrow = gtk_button_new(); + arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT); + gtk_widget_set_size_request(arrow, 7, -1); + gtk_container_add(GTK_CONTAINER(combo->arrow), arrow); + GTK_WIDGET_UNSET_FLAGS(combo->arrow, GTK_CAN_FOCUS); + gtk_widget_show_all(combo->arrow); + + combo->button = button; + combo->menu = menu_create_items(entries, n_entries, path, + &combo->factory, data); + combo->data = data; + + g_signal_connect(G_OBJECT(combo->button), "size_request", + G_CALLBACK(combo_button_size_request), combo); +#if 0 + g_signal_connect(G_OBJECT(combo->button), "enter", + G_CALLBACK(combo_button_enter), combo); + g_signal_connect(G_OBJECT(combo->button), "leave", + G_CALLBACK(combo_button_leave), combo); +#endif + g_signal_connect(G_OBJECT(combo->arrow), "enter", + G_CALLBACK(combo_button_enter), combo); + g_signal_connect(G_OBJECT(combo->arrow), "leave", + G_CALLBACK(combo_button_leave), combo); + g_signal_connect(G_OBJECT(combo->arrow), "button_press_event", + G_CALLBACK(combo_button_arrow_pressed), combo); + g_signal_connect(G_OBJECT(combo->arrow), "destroy", + G_CALLBACK(combo_button_destroy), combo); + + return combo; +} + +gint gtkut_ctree_get_nth_from_node(GtkCTree *ctree, GtkCTreeNode *node) +{ + g_return_val_if_fail(ctree != NULL, -1); + g_return_val_if_fail(node != NULL, -1); + + return g_list_position(GTK_CLIST(ctree)->row_list, (GList *)node); +} + +void gtkut_ctree_set_focus_row(GtkCTree *ctree, GtkCTreeNode *node) +{ + gtkut_clist_set_focus_row(GTK_CLIST(ctree), + gtkut_ctree_get_nth_from_node(ctree, node)); +} + +void gtkut_clist_set_focus_row(GtkCList *clist, gint row) +{ + clist->focus_row = row; + GTKUT_CTREE_REFRESH(clist); +} + +#ifdef G_OS_WIN32 +static void vadjustment_changed(GtkAdjustment *adj, gpointer data) +{ + GtkWidget *widget = GTK_WIDGET(data); + + gtk_widget_queue_draw(widget); +} +#elif defined(__APPLE__) +static void clist_select_row(GtkCList *clist, gint row, gint column, GdkEventButton *event, gpointer data) +{ + gtk_widget_queue_draw(GTK_WIDGET(clist)); +} +#endif + +void gtkut_clist_set_redraw(GtkCList *clist) +{ +#ifdef G_OS_WIN32 + if (clist->vadjustment) { + g_signal_connect(G_OBJECT(clist->vadjustment), "changed", + G_CALLBACK(vadjustment_changed), clist); + } +#elif defined(__APPLE__) + g_signal_connect(G_OBJECT(clist), "select-row", + G_CALLBACK(clist_select_row), NULL); +#endif +} + +gboolean gtkut_tree_model_next(GtkTreeModel *model, GtkTreeIter *iter) +{ + GtkTreeIter iter_, parent; + gboolean valid; + + if (gtk_tree_model_iter_children(model, &iter_, iter)) { + *iter = iter_; + return TRUE; + } + + iter_ = *iter; + if (gtk_tree_model_iter_next(model, &iter_)) { + *iter = iter_; + return TRUE; + } + + iter_ = *iter; + valid = gtk_tree_model_iter_parent(model, &parent, &iter_); + while (valid) { + iter_ = parent; + if (gtk_tree_model_iter_next(model, &iter_)) { + *iter = iter_; + return TRUE; + } + + iter_ = parent; + valid = gtk_tree_model_iter_parent(model, &parent, &iter_); + } + + return FALSE; +} + +gboolean gtkut_tree_model_prev(GtkTreeModel *model, GtkTreeIter *iter) +{ + GtkTreeIter iter_, child, next, parent; + GtkTreePath *path; + gboolean found = FALSE; + + iter_ = *iter; + + path = gtk_tree_model_get_path(model, &iter_); + + if (gtk_tree_path_prev(path)) { + gtk_tree_model_get_iter(model, &child, path); + + while (gtk_tree_model_iter_has_child(model, &child)) { + iter_ = child; + gtk_tree_model_iter_children(model, &child, &iter_); + next = child; + while (gtk_tree_model_iter_next(model, &next)) + child = next; + } + + *iter = child; + found = TRUE; + } else if (gtk_tree_model_iter_parent(model, &parent, &iter_)) { + *iter = parent; + found = TRUE; + } + + gtk_tree_path_free(path); + + return found; +} + +gboolean gtkut_tree_model_get_iter_last(GtkTreeModel *model, GtkTreeIter *iter) +{ + GtkTreeIter iter_, child, next; + + if (!gtk_tree_model_get_iter_first(model, &iter_)) + return FALSE; + + for (;;) { + next = iter_; + while (gtk_tree_model_iter_next(model, &next)) + iter_ = next; + if (gtk_tree_model_iter_children(model, &child, &iter_)) + iter_ = child; + else + break; + } + + *iter = iter_; + return TRUE; +} + +gboolean gtkut_tree_model_find_by_column_data(GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *start, + gint col, gpointer data) +{ + gboolean valid; + GtkTreeIter iter_; + gpointer store_data; + + if (start) { + gtk_tree_model_get(model, start, col, &store_data, -1); + if (store_data == data) { + *iter = *start; + return TRUE; + } + valid = gtk_tree_model_iter_children(model, &iter_, start); + } else + valid = gtk_tree_model_get_iter_first(model, &iter_); + + while (valid) { + if (gtkut_tree_model_find_by_column_data + (model, iter, &iter_, col, data)) { + return TRUE; + } + + valid = gtk_tree_model_iter_next(model, &iter_); + } + + return FALSE; +} + +void gtkut_tree_model_foreach(GtkTreeModel *model, GtkTreeIter *start, + GtkTreeModelForeachFunc func, gpointer user_data) +{ + gboolean valid = TRUE; + GtkTreeIter iter; + GtkTreePath *path; + + g_return_if_fail(func != NULL); + + if (!start) { + gtk_tree_model_foreach(model, func, user_data); + return; + } + + path = gtk_tree_model_get_path(model, start); + func(model, path, start, user_data); + gtk_tree_path_free(path); + + valid = gtk_tree_model_iter_children(model, &iter, start); + while (valid) { + gtkut_tree_model_foreach(model, &iter, func, user_data); + valid = gtk_tree_model_iter_next(model, &iter); + } +} + +gboolean gtkut_tree_row_reference_get_iter(GtkTreeModel *model, + GtkTreeRowReference *ref, + GtkTreeIter *iter) +{ + GtkTreePath *path; + gboolean valid = FALSE; + + if (ref) { + path = gtk_tree_row_reference_get_path(ref); + if (path) { + valid = gtk_tree_model_get_iter(model, iter, path); + gtk_tree_path_free(path); + } + } + + return valid; +} + +gboolean gtkut_tree_row_reference_equal(GtkTreeRowReference *ref1, + GtkTreeRowReference *ref2) +{ + GtkTreePath *path1, *path2; + gint result; + + if (ref1 == NULL || ref2 == NULL) + return FALSE; + + path1 = gtk_tree_row_reference_get_path(ref1); + if (!path1) + return FALSE; + path2 = gtk_tree_row_reference_get_path(ref2); + if (!path2) { + gtk_tree_path_free(path1); + return FALSE; + } + + result = gtk_tree_path_compare(path1, path2); + + gtk_tree_path_free(path2); + gtk_tree_path_free(path1); + + return (result == 0); +} + +void gtkut_tree_sortable_unset_sort_column_id(GtkTreeSortable *sortable) +{ +#if GTK_CHECK_VERSION(2, 6, 0) + gtk_tree_sortable_set_sort_column_id + (sortable, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); +#else + GtkTreeStore *store = GTK_TREE_STORE(sortable); + + g_return_if_fail(GTK_IS_TREE_STORE(sortable)); + + if (store->sort_column_id == -2 && store->order == GTK_SORT_ASCENDING) + return; + + store->sort_column_id = -2; + store->order = GTK_SORT_ASCENDING; + + gtk_tree_sortable_sort_column_changed(sortable); +#endif +} + +gboolean gtkut_tree_view_find_collapsed_parent(GtkTreeView *treeview, + GtkTreeIter *parent, + GtkTreeIter *iter) +{ + GtkTreeModel *model; + GtkTreeIter iter_, parent_; + GtkTreePath *path; + gboolean valid; + + if (!iter) return FALSE; + + model = gtk_tree_view_get_model(treeview); + valid = gtk_tree_model_iter_parent(model, &parent_, iter); + + while (valid) { + path = gtk_tree_model_get_path(model, &parent_); + if (!gtk_tree_view_row_expanded(treeview, path)) { + *parent = parent_; + gtk_tree_path_free(path); + return TRUE; + } + gtk_tree_path_free(path); + iter_ = parent_; + valid = gtk_tree_model_iter_parent(model, &parent_, &iter_); + } + + return FALSE; +} + +void gtkut_tree_view_expand_parent_all(GtkTreeView *treeview, GtkTreeIter *iter) +{ + GtkTreeModel *model; + GtkTreeIter parent; + GtkTreePath *path; + + model = gtk_tree_view_get_model(treeview); + + if (gtk_tree_model_iter_parent(model, &parent, iter)) { + path = gtk_tree_model_get_path(model, &parent); + gtk_tree_view_expand_to_path(treeview, path); + gtk_tree_path_free(path); + } +} + +#define SCROLL_EDGE_SIZE 15 + +/* borrowed from gtktreeview.c */ +void gtkut_tree_view_vertical_autoscroll(GtkTreeView *treeview) +{ + GdkRectangle visible_rect; + gint y, wy; + gint offset; + GtkAdjustment *vadj; + gfloat value; + + gdk_window_get_pointer(gtk_tree_view_get_bin_window(treeview), + NULL, &wy, NULL); + gtk_tree_view_widget_to_tree_coords(treeview, 0, wy, NULL, &y); + + gtk_tree_view_get_visible_rect(treeview, &visible_rect); + + /* see if we are near the edge. */ + offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE); + if (offset > 0) { + offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE); + if (offset < 0) + return; + } + + vadj = gtk_tree_view_get_vadjustment(treeview); + value = CLAMP(vadj->value + offset, 0.0, vadj->upper - vadj->page_size); + gtk_adjustment_set_value(vadj, value); +} + +/* modified version of gtk_tree_view_scroll_to_cell */ +void gtkut_tree_view_scroll_to_cell(GtkTreeView *treeview, GtkTreePath *path, + gboolean align_center) +{ + GdkRectangle cell_rect; + GdkRectangle vis_rect; + gint dest_x, dest_y; + gint margin = 0; + + if (!path) + return; + + gtk_tree_view_get_cell_area(treeview, path, NULL, &cell_rect); + gtk_tree_view_widget_to_tree_coords(treeview, cell_rect.x, cell_rect.y, + NULL, &(cell_rect.y)); + gtk_tree_view_get_visible_rect(treeview, &vis_rect); + + dest_x = vis_rect.x; + dest_y = vis_rect.y; + + /* add margin */ + if (cell_rect.height * 2 < vis_rect.height) + margin = cell_rect.height + (align_center ? 0 : 2); + + if (cell_rect.y < vis_rect.y + margin) { + if (align_center) + dest_y = cell_rect.y - + (vis_rect.height - cell_rect.height) / 2; + else + dest_y = cell_rect.y - margin; + } + if (cell_rect.y + cell_rect.height > + vis_rect.y + vis_rect.height - margin) { + if (align_center) + dest_y = cell_rect.y - + (vis_rect.height - cell_rect.height) / 2; + else + dest_y = cell_rect.y + cell_rect.height - + vis_rect.height + margin; + } + + gtk_tree_view_scroll_to_point(treeview, dest_x, dest_y); +} + +void gtkut_tree_view_fast_clear(GtkTreeView *treeview, GtkTreeStore *store) +{ +#if GTK_CHECK_VERSION(2, 8, 0) && !GTK_CHECK_VERSION(2, 10, 0) + gtk_tree_store_clear(store); +#else + /* this is faster than above, but it seems to trigger crashes in + GTK+ 2.8.x */ + gtk_tree_view_set_model(treeview, NULL); + gtk_tree_store_clear(store); + gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(store)); +#endif +} + +void gtkut_combo_set_items(GtkCombo *combo, const gchar *str1, ...) +{ + va_list args; + gchar *s; + GList *combo_items = NULL; + + g_return_if_fail(str1 != NULL); + + combo_items = g_list_append(combo_items, (gpointer)str1); + va_start(args, str1); + s = va_arg(args, gchar*); + while (s) { + combo_items = g_list_append(combo_items, (gpointer)s); + s = va_arg(args, gchar*); + } + va_end(args); + + gtk_combo_set_popdown_strings(combo, combo_items); + + g_list_free(combo_items); +} + +gchar *gtkut_editable_get_selection(GtkEditable *editable) +{ + gint start_pos, end_pos; + gboolean found; + + g_return_val_if_fail(GTK_IS_EDITABLE(editable), NULL); + + found = gtk_editable_get_selection_bounds(editable, + &start_pos, &end_pos); + if (found) + return gtk_editable_get_chars(editable, start_pos, end_pos); + else + return NULL; +} + +void gtkut_editable_disable_im(GtkEditable *editable) +{ + g_return_if_fail(editable != NULL); + +#if USE_XIM + if (editable->ic) { + gdk_ic_destroy(editable->ic); + editable->ic = NULL; + } + if (editable->ic_attr) { + gdk_ic_attr_destroy(editable->ic_attr); + editable->ic_attr = NULL; + } +#endif +} + +void gtkut_entry_strip_text(GtkEntry *entry) +{ + gchar *text; + gint len; + + text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1); + len = strlen(text); + g_strstrip(text); + if (len > strlen(text)) + gtk_entry_set_text(entry, text); + g_free(text); +} + +void gtkut_container_remove(GtkContainer *container, GtkWidget *widget) +{ + gtk_container_remove(container, widget); +} + +void gtkut_scrolled_window_reset_position(GtkScrolledWindow *window) +{ + GtkAdjustment *adj; + + adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(window)); + gtk_adjustment_set_value(adj, adj->lower); + adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(window)); + gtk_adjustment_set_value(adj, adj->lower); +} + +gboolean gtkut_text_buffer_match_string(GtkTextBuffer *textbuf, + const GtkTextIter *iter, + gunichar *wcs, gint len, + gboolean case_sens) +{ + GtkTextIter start_iter, end_iter; + gchar *utf8str, *p; + gint match_count; + + start_iter = end_iter = *iter; + gtk_text_iter_forward_chars(&end_iter, len); + + utf8str = gtk_text_buffer_get_text(textbuf, &start_iter, &end_iter, + FALSE); + if (!utf8str) return FALSE; + + if ((gint)g_utf8_strlen(utf8str, -1) != len) { + g_free(utf8str); + return FALSE; + } + + for (p = utf8str, match_count = 0; + *p != '\0' && match_count < len; + p = g_utf8_next_char(p), match_count++) { + gunichar wc; + + wc = g_utf8_get_char(p); + + if (case_sens) { + if (wc != wcs[match_count]) + break; + } else { + if (g_unichar_tolower(wc) != + g_unichar_tolower(wcs[match_count])) + break; + } + } + + g_free(utf8str); + + if (match_count == len) + return TRUE; + else + return FALSE; +} + +gboolean gtkut_text_buffer_find(GtkTextBuffer *buffer, const GtkTextIter *iter, + const gchar *str, gboolean case_sens, + GtkTextIter *match_pos) +{ + gunichar *wcs; + gint len; + glong items_read = 0, items_written = 0; + GError *error = NULL; + GtkTextIter iter_; + gboolean found = FALSE; + + wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error); + if (error != NULL) { + g_warning("An error occurred while converting a string from UTF-8 to UCS-4: %s\n", error->message); + g_error_free(error); + } + if (!wcs || items_written <= 0) return FALSE; + len = (gint)items_written; + + iter_ = *iter; + do { + found = gtkut_text_buffer_match_string + (buffer, &iter_, wcs, len, case_sens); + if (found) { + *match_pos = iter_; + break; + } + } while (gtk_text_iter_forward_char(&iter_)); + + g_free(wcs); + + return found; +} + +gboolean gtkut_text_buffer_find_backward(GtkTextBuffer *buffer, + const GtkTextIter *iter, + const gchar *str, gboolean case_sens, + GtkTextIter *match_pos) +{ + gunichar *wcs; + gint len; + glong items_read = 0, items_written = 0; + GError *error = NULL; + GtkTextIter iter_; + gboolean found = FALSE; + + wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error); + if (error != NULL) { + g_warning("An error occurred while converting a string from UTF-8 to UCS-4: %s\n", error->message); + g_error_free(error); + } + if (!wcs || items_written <= 0) return FALSE; + len = (gint)items_written; + + iter_ = *iter; + while (gtk_text_iter_backward_char(&iter_)) { + found = gtkut_text_buffer_match_string + (buffer, &iter_, wcs, len, case_sens); + if (found) { + *match_pos = iter_; + break; + } + } + + g_free(wcs); + + return found; +} + +#define MAX_TEXT_LINE_LEN 8190 + +void gtkut_text_buffer_insert_with_tag_by_name(GtkTextBuffer *buffer, + GtkTextIter *iter, + const gchar *text, + gint len, + const gchar *tag) +{ + if (len < 0) + len = strlen(text); + + gtk_text_buffer_insert_with_tags_by_name + (buffer, iter, text, len, tag, NULL); + + if (len > 0 && text[len - 1] != '\n') { + /* somehow returns invalid value first (bug?), + so call it twice */ + gtk_text_iter_get_chars_in_line(iter); + if (gtk_text_iter_get_chars_in_line(iter) > MAX_TEXT_LINE_LEN) { + gtk_text_buffer_insert_with_tags_by_name + (buffer, iter, "\n", 1, tag, NULL); + } + } +} + +gchar *gtkut_text_view_get_selection(GtkTextView *textview) +{ + GtkTextBuffer *buffer; + GtkTextIter start_iter, end_iter; + gboolean found; + + g_return_val_if_fail(GTK_IS_TEXT_VIEW(textview), NULL); + + buffer = gtk_text_view_get_buffer(textview); + found = gtk_text_buffer_get_selection_bounds(buffer, + &start_iter, &end_iter); + if (found) + return gtk_text_buffer_get_text(buffer, &start_iter, &end_iter, + FALSE); + else + return NULL; +} + +void gtkut_window_popup(GtkWidget *window) +{ + gint x, y, sx, sy, new_x, new_y; + + g_return_if_fail(window != NULL); + g_return_if_fail(window->window != NULL); + + sx = gdk_screen_width(); + sy = gdk_screen_height(); + + gdk_window_get_origin(window->window, &x, &y); + new_x = x % sx; if (new_x < 0) new_x = 0; + new_y = y % sy; if (new_y < 0) new_y = 0; + if (new_x != x || new_y != y) + gdk_window_move(window->window, new_x, new_y); + + gtk_window_set_skip_taskbar_hint(GTK_WINDOW(window), FALSE); + gtk_widget_show(window); + gtk_window_present(GTK_WINDOW(window)); +#ifdef G_OS_WIN32 + /* ensure that the window is displayed at the top */ + gdk_window_show(window->window); +#endif +} + +gboolean gtkut_window_modal_exist(void) +{ + GList *window_list, *cur; + gboolean exist = FALSE; + + window_list = gtk_window_list_toplevels(); + for (cur = window_list; cur != NULL; cur = cur->next) { + GtkWidget *window = GTK_WIDGET(cur->data); + + if (GTK_WIDGET_VISIBLE(window) && + gtk_window_get_modal(GTK_WINDOW(window))) { + exist = TRUE; + break; + } + } + g_list_free(window_list); + + return exist; +} + +/* ensure that the window is displayed on screen */ +void gtkut_window_move(GtkWindow *window, gint x, gint y) +{ + g_return_if_fail(window != NULL); + + if (x < 0) + x = 0; + if (y < 0) + y = 0; + x %= gdk_screen_width(); + y %= gdk_screen_height(); + + gtk_window_move(window, x, y); +} + +void gtkut_widget_get_uposition(GtkWidget *widget, gint *px, gint *py) +{ + gint x, y; + gint sx, sy; + + g_return_if_fail(widget != NULL); + g_return_if_fail(widget->window != NULL); + + sx = gdk_screen_width(); + sy = gdk_screen_height(); + + /* gdk_window_get_root_origin ever return *rootwindow*'s position */ + gdk_window_get_root_origin(widget->window, &x, &y); + + x %= sx; if (x < 0) x = 0; + y %= sy; if (y < 0) y = 0; + *px = x; + *py = y; +} + +void gtkut_widget_draw_now(GtkWidget *widget) +{ + if (GTK_WIDGET_VISIBLE(widget) && GTK_WIDGET_DRAWABLE(widget)) + gdk_window_process_updates(widget->window, FALSE); +} + +static void gtkut_clist_bindings_add(GtkWidget *clist) +{ + GtkBindingSet *binding_set; + + binding_set = gtk_binding_set_by_class(GTK_CLIST_GET_CLASS(clist)); + + gtk_binding_entry_add_signal(binding_set, GDK_n, GDK_CONTROL_MASK, + "scroll_vertical", 2, + G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD, + G_TYPE_FLOAT, 0.0); + gtk_binding_entry_add_signal(binding_set, GDK_p, GDK_CONTROL_MASK, + "scroll_vertical", 2, + G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD, + G_TYPE_FLOAT, 0.0); +} + +void gtkut_widget_init(void) +{ + GtkWidget *clist; + + clist = gtk_clist_new(1); + g_object_ref(G_OBJECT(clist)); + gtk_object_sink(GTK_OBJECT(clist)); + gtkut_clist_bindings_add(clist); + g_object_unref(G_OBJECT(clist)); + + clist = gtk_ctree_new(1, 0); + g_object_ref(G_OBJECT(clist)); + gtk_object_sink(GTK_OBJECT(clist)); + gtkut_clist_bindings_add(clist); + g_object_unref(G_OBJECT(clist)); +} + +void gtkut_events_flush(void) +{ + GTK_EVENTS_FLUSH(); +} diff --git a/src/gtkutils.h b/src/gtkutils.h new file mode 100644 index 0000000..1872e67 --- /dev/null +++ b/src/gtkutils.h @@ -0,0 +1,238 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __GTKUTILS_H__ +#define __GTKUTILS_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +typedef struct _ComboButton ComboButton; + +struct _ComboButton +{ + GtkWidget *arrow; + GtkWidget *button; + GtkWidget *menu; + GtkItemFactory *factory; + gpointer data; +}; + +#define GTK_EVENTS_FLUSH() \ +{ \ + while (gtk_events_pending()) \ + gtk_main_iteration(); \ +} + +#define PIXMAP_CREATE(widget, pixmap, mask, xpm_d) \ +{ \ + if (!pixmap) { \ + GtkStyle *style = gtk_widget_get_style(widget); \ + pixmap = gdk_pixmap_create_from_xpm_d \ + (widget->window, &mask, \ + &style->bg[GTK_STATE_NORMAL], xpm_d); \ + } \ +} + +#define GTK_WIDGET_PTR(wid) (*(GtkWidget **)wid) + +#define GTKUT_CTREE_NODE_SET_ROW_DATA(node, d) \ +{ \ + GTK_CTREE_ROW(node)->row.data = d; \ +} + +#define GTKUT_CTREE_NODE_GET_ROW_DATA(node) \ + (GTK_CTREE_ROW(node)->row.data) + +#define GTKUT_CTREE_REFRESH(clist) \ + GTK_CLIST_GET_CLASS(clist)->refresh(clist) + +gboolean gtkut_get_str_size (GtkWidget *widget, + const gchar *str, + gint *width, + gint *height); +gboolean gtkut_get_font_size (GtkWidget *widget, + gint *width, + gint *height); +PangoFontDescription *gtkut_get_default_font_desc + (void); +void gtkut_widget_set_small_font_size (GtkWidget *widget); + +gboolean gtkut_font_can_load (const gchar *str); + +gdouble gtkut_get_dpi (void); +gdouble gtkut_get_dpi_multiplier (void); +void gtkut_set_dpi (gdouble dpi); + +void gtkut_convert_int_to_gdk_color (gint rgbvalue, + GdkColor *color); + +void gtkut_stock_button_set_set_reverse (gboolean reverse); +void gtkut_stock_button_set_create (GtkWidget **bbox, + GtkWidget **button1, + const gchar *label1, + GtkWidget **button2, + const gchar *label2, + GtkWidget **button3, + const gchar *label3); + +void gtkut_box_set_reverse_order (GtkBox *box, + gboolean reverse); + +ComboButton *gtkut_combo_button_create (GtkWidget *button, + GtkItemFactoryEntry *entries, + gint n_entries, + const gchar *path, + gpointer data); + +/* CTree functions */ + +gint gtkut_ctree_get_nth_from_node (GtkCTree *ctree, + GtkCTreeNode *node); +void gtkut_ctree_set_focus_row (GtkCTree *ctree, + GtkCTreeNode *node); +void gtkut_clist_set_focus_row (GtkCList *clist, + gint row); +void gtkut_clist_set_redraw (GtkCList *clist); + +/* TreeView functions */ + +gboolean gtkut_tree_model_next (GtkTreeModel *model, + GtkTreeIter *iter); +gboolean gtkut_tree_model_prev (GtkTreeModel *model, + GtkTreeIter *iter); + +gboolean gtkut_tree_model_get_iter_last (GtkTreeModel *model, + GtkTreeIter *iter); + +gboolean gtkut_tree_model_find_by_column_data + (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *start, + gint col, + gpointer data); + +void gtkut_tree_model_foreach (GtkTreeModel *model, + GtkTreeIter *start, + GtkTreeModelForeachFunc func, + gpointer user_data); + +gboolean gtkut_tree_row_reference_get_iter + (GtkTreeModel *model, + GtkTreeRowReference *ref, + GtkTreeIter *iter); +gboolean gtkut_tree_row_reference_equal (GtkTreeRowReference *ref1, + GtkTreeRowReference *ref2); + +void gtkut_tree_sortable_unset_sort_column_id + (GtkTreeSortable *sortable); + +gboolean gtkut_tree_view_find_collapsed_parent + (GtkTreeView *treeview, + GtkTreeIter *parent, + GtkTreeIter *iter); +void gtkut_tree_view_expand_parent_all (GtkTreeView *treeview, + GtkTreeIter *iter); + +void gtkut_tree_view_vertical_autoscroll(GtkTreeView *treeview); + +void gtkut_tree_view_scroll_to_cell (GtkTreeView *treeview, + GtkTreePath *path, + gboolean align_center); + +void gtkut_tree_view_fast_clear (GtkTreeView *treeview, + GtkTreeStore *store); + +void gtkut_combo_set_items (GtkCombo *combo, + const gchar *str1, ...); + +gchar *gtkut_editable_get_selection (GtkEditable *editable); +void gtkut_editable_disable_im (GtkEditable *editable); + +void gtkut_entry_strip_text (GtkEntry *entry); + +void gtkut_container_remove (GtkContainer *container, + GtkWidget *widget); + +void gtkut_scrolled_window_reset_position + (GtkScrolledWindow *window); + +/* TextView functions */ + +gboolean gtkut_text_buffer_match_string (GtkTextBuffer *buffer, + const GtkTextIter *iter, + gunichar *wcs, + gint len, + gboolean case_sens); +gboolean gtkut_text_buffer_find (GtkTextBuffer *buffer, + const GtkTextIter *iter, + const gchar *str, + gboolean case_sens, + GtkTextIter *match_pos); +gboolean gtkut_text_buffer_find_backward(GtkTextBuffer *buffer, + const GtkTextIter *iter, + const gchar *str, + gboolean case_sens, + GtkTextIter *match_pos); + +void gtkut_text_buffer_insert_with_tag_by_name + (GtkTextBuffer *buffer, + GtkTextIter *iter, + const gchar *text, + gint len, + const gchar *tag); + +gchar *gtkut_text_view_get_selection (GtkTextView *textview); + +void gtkut_window_popup (GtkWidget *window); +gboolean gtkut_window_modal_exist (void); + +void gtkut_window_move (GtkWindow *window, + gint x, + gint y); + +void gtkut_widget_get_uposition (GtkWidget *widget, + gint *px, + gint *py); +void gtkut_widget_draw_now (GtkWidget *widget); +void gtkut_widget_init (void); + +void gtkut_events_flush (void); + +#endif /* __GTKUTILS_H__ */ diff --git a/src/headerview.c b/src/headerview.c new file mode 100644 index 0000000..984e8bc --- /dev/null +++ b/src/headerview.c @@ -0,0 +1,428 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2012 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_LIBCOMPFACE +# include +#endif + +#include "main.h" +#include "headerview.h" +#include "prefs_common.h" +#include "codeconv.h" +#include "gtkutils.h" +#include "utils.h" + +#define TR(str) (prefs_common.trans_hdr ? gettext(str) : str) + +#if 0 + _("From:"); + _("To:"); + _("Cc:"); + _("Newsgroups:"); + _("Subject:"); +#endif + +#if HAVE_LIBCOMPFACE +#define XPM_XFACE_HEIGHT (HEIGHT + 3) /* 3 = 1 header + 2 colors */ + +static gchar *xpm_xface[XPM_XFACE_HEIGHT]; + +static void headerview_show_xface (HeaderView *headerview, + MsgInfo *msginfo); +static gint create_xpm_from_xface (gchar *xpm[], + const gchar *xface); +#endif + +HeaderView *headerview_create(void) +{ + HeaderView *headerview; + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *hbox1; + GtkWidget *hbox2; + GtkWidget *from_header_label; + GtkWidget *from_body_label; + GtkWidget *to_header_label; + GtkWidget *to_body_label; + GtkWidget *cc_header_label; + GtkWidget *cc_body_label; + GtkWidget *ng_header_label; + GtkWidget *ng_body_label; + GtkWidget *subject_header_label; + GtkWidget *subject_body_label; + GtkTooltips *tip; + + debug_print(_("Creating header view...\n")); + headerview = g_new0(HeaderView, 1); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 2); + vbox = gtk_vbox_new(FALSE, 2); + gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0); + + hbox1 = gtk_hbox_new(FALSE, 4); + gtk_box_pack_start(GTK_BOX(vbox), hbox1, FALSE, FALSE, 0); + hbox2 = gtk_hbox_new(FALSE, 4); + gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, FALSE, 0); + + from_header_label = gtk_label_new(TR("From:")); + from_body_label = gtk_label_new(""); + to_header_label = gtk_label_new(TR("To:")); + to_body_label = gtk_label_new(""); + cc_header_label = gtk_label_new(TR("Cc:")); + cc_body_label = gtk_label_new(""); + ng_header_label = gtk_label_new(TR("Newsgroups:")); + ng_body_label = gtk_label_new(""); + subject_header_label = gtk_label_new(TR("Subject:")); + subject_body_label = gtk_label_new(""); + + gtk_label_set_selectable(GTK_LABEL(from_body_label), TRUE); + gtk_label_set_selectable(GTK_LABEL(to_body_label), TRUE); + gtk_label_set_selectable(GTK_LABEL(cc_body_label), TRUE); + gtk_label_set_selectable(GTK_LABEL(ng_body_label), TRUE); + gtk_label_set_selectable(GTK_LABEL(subject_body_label), TRUE); + + GTK_WIDGET_UNSET_FLAGS(from_body_label, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS(to_body_label, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS(cc_body_label, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS(ng_body_label, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS(subject_body_label, GTK_CAN_FOCUS); + + gtk_widget_add_events(from_body_label, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); + gtk_widget_add_events(to_body_label, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); + gtk_widget_add_events(cc_body_label, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); + gtk_widget_add_events(ng_body_label, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); + gtk_widget_add_events(subject_body_label, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); + + tip = gtk_tooltips_new(); + g_object_ref_sink(tip); + + gtk_box_pack_start(GTK_BOX(hbox1), from_header_label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox1), from_body_label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox1), to_header_label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox1), to_body_label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox1), cc_header_label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox1), cc_body_label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox1), ng_header_label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox1), ng_body_label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox2), subject_header_label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox2), subject_body_label, FALSE, FALSE, 0); + + headerview->hbox = hbox; + headerview->from_header_label = from_header_label; + headerview->from_body_label = from_body_label; + headerview->to_header_label = to_header_label; + headerview->to_body_label = to_body_label; + headerview->cc_header_label = cc_header_label; + headerview->cc_body_label = cc_body_label; + headerview->ng_header_label = ng_header_label; + headerview->ng_body_label = ng_body_label; + headerview->subject_header_label = subject_header_label; + headerview->subject_body_label = subject_body_label; + + headerview->image = NULL; + + headerview->tip = tip; + + gtk_widget_show_all(hbox); + + return headerview; +} + +void headerview_init(HeaderView *headerview) +{ + static PangoFontDescription *boldfont = NULL; +#ifdef G_OS_WIN32 + GtkStyle *style; +#endif + + if (!boldfont) { + boldfont = pango_font_description_new(); + pango_font_description_set_weight(boldfont, PANGO_WEIGHT_BOLD); + } + + if (boldfont) { + gtk_widget_modify_font(headerview->from_header_label, boldfont); + gtk_widget_modify_font(headerview->to_header_label, boldfont); + gtk_widget_modify_font(headerview->cc_header_label, boldfont); + gtk_widget_modify_font(headerview->ng_header_label, boldfont); + gtk_widget_modify_font(headerview->subject_header_label, boldfont); + } + +#ifdef G_OS_WIN32 +#define SET_LABEL_STYLE(label) \ + style = gtk_widget_get_style(label); \ + gtk_widget_modify_base(label, GTK_STATE_ACTIVE, \ + &style->base[GTK_STATE_SELECTED]); \ + gtk_widget_modify_text(label, GTK_STATE_ACTIVE, \ + &style->text[GTK_STATE_SELECTED]); + + SET_LABEL_STYLE(headerview->from_body_label); + SET_LABEL_STYLE(headerview->to_body_label); + SET_LABEL_STYLE(headerview->cc_body_label); + SET_LABEL_STYLE(headerview->ng_body_label); + SET_LABEL_STYLE(headerview->subject_body_label); + +#undef SET_LABEL_STYLE +#endif + + headerview_clear(headerview); + headerview_set_visibility(headerview, prefs_common.display_header_pane); + +#if HAVE_LIBCOMPFACE + { + gint i; + + for (i = 0; i < XPM_XFACE_HEIGHT; i++) { + xpm_xface[i] = g_malloc(WIDTH + 1); + *xpm_xface[i] = '\0'; + } + } +#endif +} + +void headerview_show(HeaderView *headerview, MsgInfo *msginfo) +{ + headerview_clear(headerview); + gtk_tooltips_enable(headerview->tip); + + gtk_label_set_text(GTK_LABEL(headerview->from_body_label), + msginfo->from ? msginfo->from : _("(No From)")); + if (msginfo->from) { + gtk_tooltips_set_tip(headerview->tip, headerview->from_body_label, msginfo->from, NULL); + } + + if (msginfo->to) { + gtk_label_set_text(GTK_LABEL(headerview->to_body_label), + msginfo->to); + gtk_widget_show(headerview->to_header_label); + gtk_widget_show(headerview->to_body_label); + gtk_tooltips_set_tip(headerview->tip, headerview->to_body_label, msginfo->to, NULL); + } + + if (msginfo->cc) { + gtk_label_set_text(GTK_LABEL(headerview->cc_body_label), + msginfo->cc); + gtk_widget_show(headerview->cc_header_label); + gtk_widget_show(headerview->cc_body_label); + gtk_tooltips_set_tip(headerview->tip, headerview->cc_body_label, msginfo->cc, NULL); + } + + if (msginfo->newsgroups) { + gtk_label_set_text(GTK_LABEL(headerview->ng_body_label), + msginfo->newsgroups); + gtk_widget_show(headerview->ng_header_label); + gtk_widget_show(headerview->ng_body_label); + gtk_tooltips_set_tip(headerview->tip, headerview->ng_body_label, msginfo->newsgroups, NULL); + } + + gtk_label_set_text(GTK_LABEL(headerview->subject_body_label), + msginfo->subject ? msginfo->subject + : _("(No Subject)")); + if (msginfo->subject) { + gtk_tooltips_set_tip(headerview->tip, headerview->subject_body_label, msginfo->subject, NULL); + } + +#if HAVE_LIBCOMPFACE + headerview_show_xface(headerview, msginfo); +#endif +} + +#if HAVE_LIBCOMPFACE +static void headerview_show_xface(HeaderView *headerview, MsgInfo *msginfo) +{ + gchar xface[2048]; + GdkPixmap *pixmap; + GdkBitmap *mask; + GtkWidget *hbox = headerview->hbox; + + if (!msginfo->xface || strlen(msginfo->xface) < 5) { + if (headerview->image && + GTK_WIDGET_VISIBLE(headerview->image)) { + gtk_widget_hide(headerview->image); + gtk_widget_queue_resize(hbox); + } + return; + } + if (!GTK_WIDGET_VISIBLE(headerview->hbox)) return; + + strncpy2(xface, msginfo->xface, sizeof(xface)); + + if (uncompface(xface) < 0) { + g_warning("uncompface failed\n"); + if (headerview->image) + gtk_widget_hide(headerview->image); + return; + } + + create_xpm_from_xface(xpm_xface, xface); + + pixmap = gdk_pixmap_create_from_xpm_d + (hbox->window, &mask, &hbox->style->white, xpm_xface); + + if (!headerview->image) { + GtkWidget *image; + + image = gtk_image_new_from_pixmap(pixmap, mask); + gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); + gtk_widget_show(image); + headerview->image = image; + } else { + gtk_image_set_from_pixmap(GTK_IMAGE(headerview->image), + pixmap, mask); + gtk_widget_show(headerview->image); + } + + gdk_pixmap_unref(pixmap); +} +#endif + +void headerview_clear(HeaderView *headerview) +{ + gtk_label_set_text(GTK_LABEL(headerview->from_body_label), ""); + gtk_label_set_text(GTK_LABEL(headerview->to_body_label), ""); + gtk_label_set_text(GTK_LABEL(headerview->cc_body_label), ""); + gtk_label_set_text(GTK_LABEL(headerview->ng_body_label), ""); + gtk_label_set_text(GTK_LABEL(headerview->subject_body_label), ""); + gtk_widget_hide(headerview->to_header_label); + gtk_widget_hide(headerview->to_body_label); + gtk_widget_hide(headerview->cc_header_label); + gtk_widget_hide(headerview->cc_body_label); + gtk_widget_hide(headerview->ng_header_label); + gtk_widget_hide(headerview->ng_body_label); + +#if GTK_CHECK_VERSION(2, 12, 0) + gtk_widget_set_tooltip_text(headerview->from_body_label, NULL); + gtk_widget_set_tooltip_text(headerview->subject_body_label, NULL); +#endif + gtk_tooltips_disable(headerview->tip); + + if (headerview->image && GTK_WIDGET_VISIBLE(headerview->image)) { + gtk_widget_hide(headerview->image); + gtk_widget_queue_resize(headerview->hbox); + } +} + +void headerview_set_visibility(HeaderView *headerview, gboolean visibility) +{ + if (visibility) + gtk_widget_show(headerview->hbox); + else + gtk_widget_hide(headerview->hbox); +} + +void headerview_destroy(HeaderView *headerview) +{ + g_object_unref(headerview->tip); + g_free(headerview); +} + +#if HAVE_LIBCOMPFACE +static gint create_xpm_from_xface(gchar *xpm[], const gchar *xface) +{ + static gchar *bit_pattern[] = { + "....", + "...#", + "..#.", + "..##", + ".#..", + ".#.#", + ".##.", + ".###", + "#...", + "#..#", + "#.#.", + "#.##", + "##..", + "##.#", + "###.", + "####" + }; + + static gchar *xface_header = "48 48 2 1"; + static gchar *xface_black = "# c #000000"; + static gchar *xface_white = ". c #ffffff"; + + gint i, line = 0; + const guchar *p; + gchar buf[WIDTH * 4 + 1]; /* 4 = strlen("0x0000") */ + + p = (const guchar *)xface; + + strcpy(xpm[line++], xface_header); + strcpy(xpm[line++], xface_black); + strcpy(xpm[line++], xface_white); + + for (i = 0; i < HEIGHT; i++) { + gint col; + + buf[0] = '\0'; + + for (col = 0; col < 3; col++) { + gint figure; + + p += 2; /* skip '0x' */ + + for (figure = 0; figure < 4; figure++) { + gint n = 0; + + if ('0' <= *p && *p <= '9') { + n = *p - '0'; + } else if ('a' <= *p && *p <= 'f') { + n = *p - 'a' + 10; + } else if ('A' <= *p && *p <= 'F') { + n = *p - 'A' + 10; + } + + strcat(buf, bit_pattern[n]); + p++; /* skip ',' */ + } + + p++; /* skip '\n' */ + } + + strcpy(xpm[line++], buf); + p++; + } + + return 0; +} +#endif diff --git a/src/headerview.h b/src/headerview.h new file mode 100644 index 0000000..dea52b2 --- /dev/null +++ b/src/headerview.h @@ -0,0 +1,60 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2012 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __HEADERVIEW_H__ +#define __HEADERVIEW_H__ + +#include +#include +#include + +#include "procmsg.h" + +typedef struct _HeaderView HeaderView; + +struct _HeaderView +{ + GtkWidget *hbox; + + GtkWidget *from_header_label; + GtkWidget *from_body_label; + GtkWidget *to_header_label; + GtkWidget *to_body_label; + GtkWidget *cc_header_label; + GtkWidget *cc_body_label; + GtkWidget *ng_header_label; + GtkWidget *ng_body_label; + GtkWidget *subject_header_label; + GtkWidget *subject_body_label; + + GtkWidget *image; + + GtkTooltips *tip; +}; + +HeaderView *headerview_create (void); +void headerview_init (HeaderView *headerview); +void headerview_show (HeaderView *headerview, + MsgInfo *msginfo); +void headerview_clear (HeaderView *headerview); +void headerview_set_visibility (HeaderView *headerview, + gboolean visibility); +void headerview_destroy (HeaderView *headerview); + +#endif /* __HEADERVIEW_H__ */ diff --git a/src/icons/Makefile.am b/src/icons/Makefile.am new file mode 100644 index 0000000..2c7cd17 --- /dev/null +++ b/src/icons/Makefile.am @@ -0,0 +1,99 @@ +BUILT_SOURCES = \ + stock_addressbook.h \ + stock_attach.h \ + stock_delete.h \ + stock_delete_16.h \ + stock_hand-signed.h \ + stock_inbox.h \ + stock_insert-file.h \ + stock_mail-compose.h \ + stock_mail-compose_16.h \ + stock_mail-forward.h \ + stock_mail-receive.h \ + stock_mail-reply-to-all.h \ + stock_mail-reply.h \ + stock_mail-send.h \ + stock_mail.h \ + stock_mail_receive_all.h \ + stock_mail_send_queue.h \ + stock_outbox.h \ + stock_person.h \ + stock_book.h \ + stock_spam.h \ + stock_spam_16.h \ + stock_notspam.h \ + stock_sylpheed.h \ + stock_sylpheed_16.h \ + stock_sylpheed_32.h \ + stock_sylpheed_newmail.h \ + stock_sylpheed_newmail_16.h \ + sylpheed-logo.h \ + folder-close.h \ + folder-open.h \ + folder-noselect.h \ + folder-search.h \ + group.h \ + html.h + +EXTRA_DIST = \ + stock_addressbook.png \ + stock_attach.png \ + stock_delete.png \ + stock_delete_16.png \ + stock_hand-signed.png \ + stock_inbox.png \ + stock_insert-file.png \ + stock_mail-compose.png \ + stock_mail-compose_16.png \ + stock_mail-forward.png \ + stock_mail-receive.png \ + stock_mail-reply-to-all.png \ + stock_mail-reply.png \ + stock_mail-send.png \ + stock_mail.png \ + stock_mail_receive_all.png \ + stock_mail_send_queue.png \ + stock_outbox.png \ + stock_person.png \ + stock_book.png \ + stock_spam.png \ + stock_spam_16.png \ + stock_notspam.png \ + stock_sylpheed.png \ + stock_sylpheed_16.png \ + stock_sylpheed_32.png \ + stock_sylpheed_newmail.png \ + stock_sylpheed_newmail_16.png \ + sylpheed-logo.png \ + folder-close.png \ + folder-open.png \ + folder-noselect.png \ + folder-search.png \ + group.png \ + html.png \ + clip.xpm \ + deleted.xpm \ + forwarded.xpm \ + mark.xpm \ + new.xpm \ + offline.xpm \ + online.xpm \ + replied.xpm \ + unread.xpm \ + linewrap.xpm \ + continue.xpm \ + complete.xpm \ + error.xpm \ + address.xpm \ + book.xpm \ + category.xpm \ + interface.xpm \ + jpilot.xpm \ + ldap.xpm \ + vcard.xpm \ + mail.xpm \ + sylpheed.ico \ + $(BUILT_SOURCES) + +%.h: %.png + gdk-pixbuf-csource --name=`echo $(*F)|sed s/-/_/g` $< > $@ diff --git a/src/icons/Makefile.in b/src/icons/Makefile.in new file mode 100644 index 0000000..86f0a01 --- /dev/null +++ b/src/icons/Makefile.in @@ -0,0 +1,591 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = src/icons +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/mkinstalldirs +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/ac/aclocal-include.m4 \ + $(top_srcdir)/ac/gnupg-check-typedef.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_REVISION = @BUILD_REVISION@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEVEL_VERSION = @DEVEL_VERSION@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_VERSION = @EXTRA_VERSION@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GOBJECT_QUERY = @GOBJECT_QUERY@ +GPGME_CFLAGS = @GPGME_CFLAGS@ +GPGME_CONFIG = @GPGME_CONFIG@ +GPGME_LIBS = @GPGME_LIBS@ +GREP = @GREP@ +GTK_CFLAGS = @GTK_CFLAGS@ +GTK_LIBS = @GTK_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +LD = @LD@ +LDAP_LIBS = @LDAP_LIBS@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBICONV = @LIBICONV@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSYLPH_CFLAGS = @LIBSYLPH_CFLAGS@ +LIBSYLPH_LIBS = @LIBSYLPH_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_RELEASE = @LT_RELEASE@ +LT_REVISION = @LT_REVISION@ +MAINT = @MAINT@ +MAJOR_VERSION = @MAJOR_VERSION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MICRO_VERSION = @MICRO_VERSION@ +MINOR_VERSION = @MINOR_VERSION@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POFILES = @POFILES@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYLPHEED_LIBS = @SYLPHEED_LIBS@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +XGETTEXT = @XGETTEXT@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +faqdir = @faqdir@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +manualdir = @manualdir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +plugindir = @plugindir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +themedir = @themedir@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +BUILT_SOURCES = \ + stock_addressbook.h \ + stock_attach.h \ + stock_delete.h \ + stock_delete_16.h \ + stock_hand-signed.h \ + stock_inbox.h \ + stock_insert-file.h \ + stock_mail-compose.h \ + stock_mail-compose_16.h \ + stock_mail-forward.h \ + stock_mail-receive.h \ + stock_mail-reply-to-all.h \ + stock_mail-reply.h \ + stock_mail-send.h \ + stock_mail.h \ + stock_mail_receive_all.h \ + stock_mail_send_queue.h \ + stock_outbox.h \ + stock_person.h \ + stock_book.h \ + stock_spam.h \ + stock_spam_16.h \ + stock_notspam.h \ + stock_sylpheed.h \ + stock_sylpheed_16.h \ + stock_sylpheed_32.h \ + stock_sylpheed_newmail.h \ + stock_sylpheed_newmail_16.h \ + sylpheed-logo.h \ + folder-close.h \ + folder-open.h \ + folder-noselect.h \ + folder-search.h \ + group.h \ + html.h + +EXTRA_DIST = \ + stock_addressbook.png \ + stock_attach.png \ + stock_delete.png \ + stock_delete_16.png \ + stock_hand-signed.png \ + stock_inbox.png \ + stock_insert-file.png \ + stock_mail-compose.png \ + stock_mail-compose_16.png \ + stock_mail-forward.png \ + stock_mail-receive.png \ + stock_mail-reply-to-all.png \ + stock_mail-reply.png \ + stock_mail-send.png \ + stock_mail.png \ + stock_mail_receive_all.png \ + stock_mail_send_queue.png \ + stock_outbox.png \ + stock_person.png \ + stock_book.png \ + stock_spam.png \ + stock_spam_16.png \ + stock_notspam.png \ + stock_sylpheed.png \ + stock_sylpheed_16.png \ + stock_sylpheed_32.png \ + stock_sylpheed_newmail.png \ + stock_sylpheed_newmail_16.png \ + sylpheed-logo.png \ + folder-close.png \ + folder-open.png \ + folder-noselect.png \ + folder-search.png \ + group.png \ + html.png \ + clip.xpm \ + deleted.xpm \ + forwarded.xpm \ + mark.xpm \ + new.xpm \ + offline.xpm \ + online.xpm \ + replied.xpm \ + unread.xpm \ + linewrap.xpm \ + continue.xpm \ + complete.xpm \ + error.xpm \ + address.xpm \ + book.xpm \ + category.xpm \ + interface.xpm \ + jpilot.xpm \ + ldap.xpm \ + vcard.xpm \ + mail.xpm \ + sylpheed.ico \ + $(BUILT_SOURCES) + +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/icons/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/icons/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile +installdirs: +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: all check install install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am + + +%.h: %.png + gdk-pixbuf-csource --name=`echo $(*F)|sed s/-/_/g` $< > $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/icons/address.xpm b/src/icons/address.xpm new file mode 100644 index 0000000..f651d14 --- /dev/null +++ b/src/icons/address.xpm @@ -0,0 +1,87 @@ +/* XPM */ +static char * address_xpm[] = { +"16 14 70 1", +" c None", +". c #2D549D", +"+ c #2D539D", +"@ c #2B529D", +"# c #2A519D", +"$ c #294F9D", +"% c #284E9D", +"& c #FFFFFF", +"* c #FEFEFF", +"= c #2C529D", +"- c #F1F5FF", +"; c #EAF1FF", +"> c #E4EDFF", +", c #274D9D", +"' c #2C539D", +") c #2B519D", +"! c #2A509D", +"~ c #E5EFFF", +"{ c #DFEAFF", +"] c #264B9D", +"^ c #FBFDFF", +"/ c #F4F8FF", +"( c #EEF4FF", +"_ c #E7F0FF", +": c #E0EBFF", +"< c #DAE6FF", +"[ c #254B9D", +"} c #FDFEFF", +"| c #F6F9FF", +"1 c #F0F5FF", +"2 c #E9F1FF", +"3 c #E2ECFF", +"4 c #DBE8FF", +"5 c #D4E4FF", +"6 c #244A9D", +"7 c #000000", +"8 c #E4EEFF", +"9 c #D0E0FF", +"0 c #23489D", +"a c #FAFBFF", +"b c #F4F7FF", +"c c #EDF3FF", +"d c #DFEBFF", +"e c #D8E6FF", +"f c #D2E1FF", +"g c #CBDDFF", +"h c #23479D", +"i c #E8F0FF", +"j c #C6DAFF", +"k c #22469D", +"l c #F7FAFF", +"m c #F1F6FF", +"n c #E3EDFF", +"o c #DCE9FF", +"p c #D5E4FF", +"q c #CFDFFF", +"r c #C8DCFF", +"s c #C1D7FF", +"t c #ECF3FF", +"u c #D1E1FF", +"v c #BFD6FF", +"w c #EDF4FF", +"x c #E6EFFF", +"y c #D9E7FF", +"z c #D3E3FF", +"A c #CCDDFF", +"B c #C5DAFF", +"C c #BFD5FF", +"D c #274C9D", +"E c #24499D", +" ", +" ..........+@#$%", +" .&&&.&&&&*=-;>,", +" .&&...&&&')!~{]", +" .&&&&&&&^/(_:<[", +" .&&&&&&}|123456", +" .&&&7&777787790", +" .&&&&&abc~defgh", +" .&&&77|7i7777jk", +" .&&&*lm;nopqrsk", +" .&&&77t777u77vk", +" .&&^/wx:yzABCvk", +" ...@!$%D]E0kkkk", +" "}; diff --git a/src/icons/book.xpm b/src/icons/book.xpm new file mode 100644 index 0000000..5bd9651 --- /dev/null +++ b/src/icons/book.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * book_xpm[] = { +"16 16 4 1", +" c None", +". c #000000", +"+ c #FFFFFF", +"@ c #BE22A6", +" ", +" ... ... ", +" .+++. .+++. ", +" .+++++. .+++++.", +" .++++++.++++++.", +" .++.+++.+++.++.", +" .++++.+.+.++++.", +" .++++++.+++.++.", +" .++.+++.+.++++.", +" .++++.+.+++.++.", +" .++++++.+.++++.", +" .++++++.++++++.", +" .+...++.++...+.", +" ..@@@.+.+.@@@..", +" .@ @...@ @.", +" @ @.@ @"}; diff --git a/src/icons/category.xpm b/src/icons/category.xpm new file mode 100644 index 0000000..fd16880 --- /dev/null +++ b/src/icons/category.xpm @@ -0,0 +1,35 @@ +/* XPM */ +static char * category_xpm[] = { +"16 16 16 1", +" c None", +". c #000000", +"+ c #2C7AD4", +"@ c #3B98C5", +"# c #2D5AD3", +"$ c #01DFFF", +"% c #BFEDE2", +"& c #12BDEE", +"* c #B8EADF", +"= c #2ABAD6", +"- c #039FFD", +"; c #155DEB", +"> c #271BD9", +", c #3F18C1", +"' c #1F1CE1", +") c #3719C9", +" ", +" ", +" .... ", +" .+@+@. ", +" .+#+#@+...... ", +" .$$%$%&*%$==$.", +" .=@&@&@=+=+-;.", +" .$&@&@=+=+-+#.", +" .=@&@=+=+-+->.", +" .$&@=+=+-+-;#.", +" .=@=+=+-+-;-,.", +" .$=+=+-+-;-;>.", +" .=';';';';)>,.", +" ............ ", +" ", +" "}; diff --git a/src/icons/clip.xpm b/src/icons/clip.xpm new file mode 100644 index 0000000..e695c99 --- /dev/null +++ b/src/icons/clip.xpm @@ -0,0 +1,19 @@ +/* XPM */ +static char * clip_xpm[] = { +"8 12 4 1", +" c None", +". c #000000", +"+ c #F4F4F4", +"@ c #4A4A4A", +" ... ", +" .+++. ", +" .+@@@+.", +" .+@@@+.", +".++@@++.", +".++@@++.", +".++@@++.", +".+@++@+.", +".+@@@@+.", +".+@@@@+.", +" .++++. ", +" .... "}; diff --git a/src/icons/complete.xpm b/src/icons/complete.xpm new file mode 100644 index 0000000..69a9ad4 --- /dev/null +++ b/src/icons/complete.xpm @@ -0,0 +1,17 @@ +/* XPM */ +static char * complete_xpm[] = { +"10 10 4 1", +" c None", +". c #000000", +"+ c #708C58", +"@ c #88AC84", +" ..", +" .+.", +" .++.", +".. .+++.", +".@. .+++. ", +".@@.+++. ", +".@@+++. ", +" .@++. ", +" .@. ", +" . "}; diff --git a/src/icons/continue.xpm b/src/icons/continue.xpm new file mode 100644 index 0000000..99b3c7f --- /dev/null +++ b/src/icons/continue.xpm @@ -0,0 +1,42 @@ +/* XPM */ +static char * continue_xpm[] = { +"10 10 29 1", +" c None", +". c #000000", +"+ c #B1C7AC", +"@ c #F6F9F6", +"# c #E1EADF", +"$ c #F7F9F6", +"% c #F0F4EF", +"& c #ECF2EB", +"* c #E5EDE4", +"= c #F4F7F4", +"- c #EFF3EE", +"; c #E4ECE3", +"> c #E0E9DE", +", c #8EB184", +"' c #85AB7D", +") c #84AA7C", +"! c #82A678", +"~ c #769C6C", +"{ c #577550", +"] c #2E4429", +"^ c #8CB182", +"/ c #83A879", +"( c #7DA171", +"_ c #608756", +": c #30462B", +"< c #7CA273", +"[ c #52744A", +"} c #2D4526", +"| c #31472C", +".. ", +".+.. ", +".@#+.. ", +".$%&*+.. ", +".=-&;>#+..", +".,')!~{]..", +".^/(_:.. ", +".<[}.. ", +".|.. ", +".. "}; diff --git a/src/icons/deleted.xpm b/src/icons/deleted.xpm new file mode 100644 index 0000000..988364b --- /dev/null +++ b/src/icons/deleted.xpm @@ -0,0 +1,15 @@ +/* XPM */ +static char * deleted_xpm[] = { +"10 10 2 1", +" c None", +". c #999999", +" ", +" . .. ", +" .. ... ", +" .... ", +" .. ", +" .... ", +" ..... ", +" .. ... ", +" . . ", +" "}; diff --git a/src/icons/error.xpm b/src/icons/error.xpm new file mode 100644 index 0000000..e70ac53 --- /dev/null +++ b/src/icons/error.xpm @@ -0,0 +1,45 @@ +/* XPM */ +static char * error_xpm[] = { +"10 10 32 1", +" c None", +". c #0B0402", +"+ c #331516", +"@ c #261B1C", +"# c #272120", +"$ c #3C1D1D", +"% c #4B2727", +"& c #462F2F", +"* c #443A3B", +"= c #573435", +"- c #573E3E", +"; c #524647", +"> c #734647", +", c #805353", +"' c #786D6D", +") c #826F72", +"! c #A16A6D", +"~ c #947273", +"{ c #9B7376", +"] c #877D7D", +"^ c #B07E7E", +"/ c #A48887", +"( c #AF9291", +"_ c #BB8D8F", +": c #AF9FA1", +"< c #C4A0A1", +"[ c #C2A5A4", +"} c #C3B4B5", +"| c #D0B4B6", +"1 c #D6B2B3", +"2 c #E1D7D6", +"3 c #EFE2E2", +" #*;;&+ ", +" ;:222|~% ", +"@:3}23:1,+", +"*2}#]]@/_%", +";23]..'|<=", +"-22]..)1_=", +"&[:@')+{!%", +"+~1(|1{^>+", +" ={__^!,% ", +" $===$+ "}; diff --git a/src/icons/folder-close.h b/src/icons/folder-close.h new file mode 100644 index 0000000..4b62618 --- /dev/null +++ b/src/icons/folder-close.h @@ -0,0 +1,59 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (folder_close) +#endif +#ifdef __GNUC__ +static const guint8 folder_close[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 folder_close[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (663) */ + "\0\0\2\257" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (64) */ + "\0\0\0@" + /* width (16) */ + "\0\0\0\20" + /* height (12) */ + "\0\0\0\14" + /* pixel_data: */ + "\1\0\0\0\0\205\201\244\324\377\212\0\0\0\0\1\201\244\324\377\203\344" + "\355\371\377\3\344\355\370\377\340\352\367\377}\240\322\377\211\0\0\0" + "\0\16Px\271\377\214\254\331\377\212\252\330\377\210\250\327\377\206\247" + "\327\377\205\245\325\377\202\243\324\377Ip\263\377Hn\262\377Gm\261\377" + "Fl\260\377Ek\260\377Ci\257\377Ch\256\377\202\0\0\0\0\16Mt\266\377\300" + "\322\355\377\276\320\355\377\272\315\353\377\270\313\353\377\266\311" + "\351\377\263\306\350\377\260\305\350\377\256\301\346\377\252\300\345" + "\377\247\275\344\377\245\273\343\377\242\271\341\377>c\252\377\202\0" + "\0\0\0\16Io\263\377\266\312\351\377\264\307\351\377\262\305\347\377\256" + "\302\347\377\254\300\346\377\251\277\344\377\246\274\343\377\244\271" + "\341\377\241\267\340\377\236\265\337\377\233\263\336\377\231\260\335" + "\377;_\250\377\202\0\0\0\0\16Ek\260\377\254\301\346\377\252\300\345\377" + "\247\275\343\377\245\272\342\377\242\270\341\377\237\265\340\377\235" + "\263\336\377\232\261\335\377\227\257\334\377\224\255\333\377\222\252" + "\332\377\216\247\331\3777Z\244\377\202\0\0\0\0\16Af\255\377\243\271\341" + "\377\240\266\341\377\235\264\337\377\233\262\336\377\230\257\335\377" + "\225\255\334\377\222\253\332\377\220\251\331\377\215\247\330\377\212" + "\244\326\377\207\241\325\377\205\237\325\3773V\241\377\202\0\0\0\0\16" + "=b\251\377\231\261\335\377\226\256\334\377\224\255\333\377\221\251\331" + "\377\216\247\330\377\213\246\327\377\211\242\326\377\206\240\325\377" + "\203\236\324\377\201\234\323\377~\232\322\377{\227\320\377/Q\235\377" + "\202\0\0\0\0\16""9]\246\377\217\250\331\377\215\246\327\377\212\244\326" + "\377\207\240\325\377\205\237\325\377\202\235\323\377~\232\322\377|\230" + "\321\377z\225\320\377v\224\317\377s\221\315\377q\216\314\377+M\233\377" + "\202\0\0\0\0\16""5X\243\377\206\240\325\377\203\235\324\377\200\233\323" + "\377}\231\321\377z\227\321\377x\224\317\377u\222\316\377r\217\315\377" + "o\215\313\377m\213\312\377j\210\311\377g\206\307\377(H\230\377\202\0" + "\0\0\0\16""2T\240\377{\230\321\377x\225\320\377v\223\317\377s\221\316" + "\377q\215\314\377n\214\313\377k\211\311\377h\207\311\377f\205\307\377" + "b\202\305\377`\201\304\377^~\303\377#C\224\377\202\0\0\0\0\16-O\234\377" + "-N\234\377+M\233\377*K\232\377)J\231\377(I\230\377'H\227\377&G\226\377" + "%E\225\377$D\224\377#C\223\377\"A\223\377!@\222\377\40\77\220\377\202" + "\0\0\0\0"}; + + diff --git a/src/icons/folder-close.png b/src/icons/folder-close.png new file mode 100644 index 0000000..f764bdb Binary files /dev/null and b/src/icons/folder-close.png differ diff --git a/src/icons/folder-noselect.h b/src/icons/folder-noselect.h new file mode 100644 index 0000000..24b36bd --- /dev/null +++ b/src/icons/folder-noselect.h @@ -0,0 +1,62 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (folder_noselect) +#endif +#ifdef __GNUC__ +static const guint8 folder_noselect[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 folder_noselect[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (649) */ + "\0\0\2\241" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (64) */ + "\0\0\0@" + /* width (16) */ + "\0\0\0\20" + /* height (12) */ + "\0\0\0\14" + /* pixel_data: */ + "\1\0\0\0\0\205\277\321\351\377\212\0\0\0\0\1\277\321\351\377\204\361" + "\365\373\377\2\357\364\372\377\275\317\350\377\211\0\0\0\0\11\247\273" + "\333\377\305\325\353\377\304\324\353\377\303\323\352\377\302\322\352" + "\377\301\321\351\377\300\320\351\377\243\267\330\377\243\266\330\377" + "\202\242\265\327\377\1\241\264\327\377\202\240\263\326\377\202\0\0\0" + "\0\16\245\271\332\377\337\350\365\377\336\347\365\377\334\345\364\377" + "\333\344\364\377\332\343\363\377\330\342\363\377\327\341\363\377\326" + "\337\362\377\324\337\361\377\322\335\361\377\321\334\360\377\320\333" + "\357\377\236\260\324\377\202\0\0\0\0\16\243\266\330\377\332\344\363\377" + "\331\342\363\377\330\341\362\377\326\340\362\377\325\337\362\377\323" + "\336\361\377\322\335\360\377\321\333\357\377\317\332\357\377\316\331" + "\356\377\314\330\356\377\313\327\355\377\234\256\323\377\202\0\0\0\0" + "\16\241\264\327\377\325\337\362\377\324\337\361\377\322\335\360\377\321" + "\334\360\377\320\333\357\377\316\331\357\377\315\330\356\377\314\327" + "\355\377\312\326\355\377\311\325\354\377\310\324\354\377\306\322\353" + "\377\232\254\321\377\202\0\0\0\0\16\237\262\325\377\320\333\357\377\317" + "\332\357\377\315\331\356\377\314\330\356\377\313\326\355\377\311\325" + "\355\377\310\324\354\377\307\323\353\377\305\322\353\377\304\321\352" + "\377\302\317\351\377\301\316\351\377\230\252\317\377\202\0\0\0\0\16\235" + "\260\323\377\313\327\355\377\312\326\355\377\311\325\354\377\307\323" + "\353\377\306\322\353\377\304\322\352\377\303\320\352\377\302\317\351" + "\377\300\316\351\377\277\315\350\377\276\314\350\377\274\312\347\377" + "\226\247\315\377\202\0\0\0\0\16\233\255\322\377\306\323\353\377\305\322" + "\352\377\304\321\352\377\302\317\351\377\301\316\351\377\300\315\350" + "\377\276\314\350\377\275\313\347\377\274\311\347\377\272\311\346\377" + "\270\307\345\377\267\306\345\377\224\245\314\377\202\0\0\0\0\16\231\253" + "\320\377\302\317\351\377\300\315\351\377\277\314\350\377\275\313\347" + "\377\274\312\347\377\273\311\346\377\271\310\346\377\270\306\345\377" + "\266\305\344\377\265\304\344\377\264\303\343\377\262\302\342\377\223" + "\243\313\377\202\0\0\0\0\16\230\251\317\377\274\313\347\377\273\311\347" + "\377\272\310\346\377\270\307\346\377\267\305\345\377\266\305\344\377" + "\264\303\343\377\263\302\343\377\262\301\342\377\260\300\341\377\257" + "\277\341\377\256\276\340\377\220\240\311\377\202\0\0\0\0\202\225\246" + "\315\377\6\224\245\314\377\224\244\314\377\223\244\313\377\223\243\313" + "\377\222\243\312\377\222\242\312\377\202\221\241\311\377\4\220\240\310" + "\377\220\237\310\377\217\237\310\377\217\236\307\377\202\0\0\0\0"}; + + diff --git a/src/icons/folder-noselect.png b/src/icons/folder-noselect.png new file mode 100644 index 0000000..f5f3175 Binary files /dev/null and b/src/icons/folder-noselect.png differ diff --git a/src/icons/folder-open.h b/src/icons/folder-open.h new file mode 100644 index 0000000..906f880 --- /dev/null +++ b/src/icons/folder-open.h @@ -0,0 +1,59 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (folder_open) +#endif +#ifdef __GNUC__ +static const guint8 folder_open[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 folder_open[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (675) */ + "\0\0\2\273" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (64) */ + "\0\0\0@" + /* width (16) */ + "\0\0\0\20" + /* height (12) */ + "\0\0\0\14" + /* pixel_data: */ + "\1\0\0\0\0\205\201\244\324\377\212\0\0\0\0\1\201\244\324\377\203\344" + "\355\371\377\3\344\355\370\377\340\352\367\377}\240\322\377\211\0\0\0" + "\0\16\201\244\324\377\342\354\370\377\340\351\367\377\334\346\366\377" + "\330\345\365\377\327\342\363\377\323\337\362\377v\230\315\377t\226\314" + "\377r\225\313\377p\223\312\377o\222\311\377m\220\310\377k\216\310\377" + "\202\0\0\0\0\16{\236\321\377\327\342\363\377\324\340\363\377\320\335" + "\361\377\316\332\361\377\313\331\357\377\310\326\355\377\305\324\355" + "\377\302\320\353\377\276\316\352\377\273\314\351\377\270\312\350\377" + "\266\307\346\377e\210\303\377\202\0\0\0\0nu\230\315\377\314\331\357\377" + "|\235\321\377{\233\317\377y\231\317\377w\227\316\377u\226\314\377s\224" + "\313\377q\222\312\377o\220\311\377m\216\310\377k\214\307\377j\212\306" + "\377;_\250\377r\220\311\377p\217\307\377o\222\311\377\300\320\353\377" + "u\227\315\377\247\275\343\377\245\272\342\377\242\270\341\377\237\265" + "\340\377\235\263\336\377\232\261\335\377\227\257\334\377\224\255\333" + "\377\222\252\332\377\216\247\331\377Or\267\377\231\260\334\377h\210\304" + "\377h\214\306\377\266\307\346\377o\217\312\377\235\264\337\377\233\262" + "\336\377\230\257\335\377\225\255\334\377\222\253\332\377\220\251\331" + "\377\215\247\330\377\212\244\326\377\207\241\325\377\205\237\325\377" + "Il\264\377\216\246\327\377a\201\300\377c\206\302\377j\213\306\377\226" + "\256\334\377\224\255\333\377\221\251\331\377\216\247\330\377\213\246" + "\327\377\211\242\326\377\206\240\325\377\203\236\324\377\201\234\323" + "\377~\232\322\377{\227\320\377Dg\260\377[|\275\377\0\0\0\0]\177\276\377" + "c\204\303\377\215\246\327\377\212\244\326\377\207\240\325\377\205\237" + "\325\377\202\235\323\377~\232\322\377|\230\321\377z\225\320\377v\224" + "\317\377s\221\315\377q\216\314\377>a\255\377Tu\271\377\0\0\0\0Vy\273" + "\377\\~\277\377\203\235\324\377\200\233\323\377}\231\321\377z\227\321" + "\377x\224\317\377u\222\316\377r\217\315\377o\215\313\377m\213\312\377" + "j\210\311\377g\206\307\3779[\252\377Lm\265\377\0\0\0\0""2T\240\377{\230" + "\321\377x\225\320\377v\223\317\377s\221\316\377q\215\314\377n\214\313" + "\377k\211\311\377h\207\311\377f\205\307\377b\202\305\377`\201\304\377" + "^~\303\377#C\224\377\202\0\0\0\0\16-O\234\377-N\234\377+M\233\377*K\232" + "\377)J\231\377(I\230\377'H\227\377&G\226\377%E\225\377$D\224\377#C\223" + "\377\"A\223\377!@\222\377\40\77\220\377\202\0\0\0\0"}; + + diff --git a/src/icons/folder-open.png b/src/icons/folder-open.png new file mode 100644 index 0000000..809ecac Binary files /dev/null and b/src/icons/folder-open.png differ diff --git a/src/icons/folder-search.h b/src/icons/folder-search.h new file mode 100644 index 0000000..03778ec --- /dev/null +++ b/src/icons/folder-search.h @@ -0,0 +1,57 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (folder_search) +#endif +#ifdef __GNUC__ +static const guint8 folder_search[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 folder_search[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (654) */ + "\0\0\2\246" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (64) */ + "\0\0\0@" + /* width (16) */ + "\0\0\0\20" + /* height (12) */ + "\0\0\0\14" + /* pixel_data: */ + "\1\0\0\0\0\205\216\324\235\377\212\0\0\0\0\1\216\324\235\377\203\346" + "\372\353\377\3\345\371\346\377\342\370\343\377\213\321\232\377\211\0" + "\0\0\0\12j\262\200\377\227\331\242\377\226\330\242\377\226\326\242\377" + "\224\326\242\377\223\323\235\377\220\322\234\377e\253z\377e\251z\377" + "e\247x\377\202c\247x\377\202b\244v\377\202\0\0\0\0\16h\256|\377\304\360" + "\311\377\303\357\312\377\277\355\305\377z\274\202\377X\242f\377V\240" + "d\377u\271}\377\266\346\272\377\261\347\271\377\260\344\267\377\255\343" + "\265\377\253\341\261\377]\237q\377\202\0\0\0\0\16f\252{\377\272\354\277" + "\377\271\353\300\377t\270}\377N\226b\377{\301\210\377y\277\206\377L\226" + "]\377o\263z\377\252\340\257\377\251\337\257\377\247\335\255\377\244\334" + "\253\377\\\236r\377\202\0\0\0\0\16c\247x\377\264\346\273\377\261\347" + "\271\377P\234^\377u\275\210\377\253\341\261\377\251\337\261\377q\271" + "\204\377O\231^\377\243\333\252\377\241\331\251\377\236\330\246\377\234" + "\326\245\377V\234m\377\202\0\0\0\0\16`\242v\377\253\341\261\377\252\340" + "\263\377L\234]\377p\266\205\377\244\334\253\377\242\332\254\377j\264" + "\201\377L\230Z\377\232\326\242\377\230\324\240\377\227\321\237\377\226" + "\320\241\377R\230l\377\202\0\0\0\0\16[\237p\377\244\334\251\377\241\333" + "\252\377a\255x\377I\223Y\377f\256~\377d\256|\377H\222Y\377P\240d\377" + "\225\317\240\377\222\316\235\377\217\315\234\377\216\312\233\377P\224" + "i\377\202\0\0\0\0\16X\236o\377\234\326\245\377\233\325\242\377\230\324" + "\240\377V\250m\377C\215U\377C\213V\377L\236c\377@\214T\377M\235e\377" + "\213\311\232\377\210\306\227\377\210\304\226\377K\223i\377\202\0\0\0" + "\0\16U\231m\377\226\322\240\377\225\317\241\377\222\316\237\377\217\313" + "\234\377\215\313\235\377\215\311\232\377\212\310\231\377I\231a\377=\207" + "R\377F\230_\377\202\300\224\377\200\276\222\377H\220g\377\202\0\0\0\0" + "\16R\226j\377\215\313\234\377\214\312\234\377\213\311\232\377\211\307" + "\233\377\211\303\230\377\205\303\225\377\202\300\222\377\202\300\225" + "\377B\222\\\3778\204O\377z\272\216\377z\270\216\377D\214f\377\202\0\0" + "\0\0\202N\224j\377\14K\223i\377K\221i\377J\220h\377H\220f\377F\220e\377" + "F\216e\377E\215e\377D\214d\377C\213c\377B\214g\377A\213f\377@\212e\377" + "\202\0\0\0\0"}; + + diff --git a/src/icons/folder-search.png b/src/icons/folder-search.png new file mode 100644 index 0000000..e5d5abc Binary files /dev/null and b/src/icons/folder-search.png differ diff --git a/src/icons/forwarded.xpm b/src/icons/forwarded.xpm new file mode 100644 index 0000000..a3cabad --- /dev/null +++ b/src/icons/forwarded.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * forwarded_xpm[] = { +"10 10 10 1", +" c None", +". c #000000", +"+ c #B39C82", +"@ c #BCA488", +"# c #867561", +"$ c #8F7D68", +"% c #98856E", +"& c #A18D75", +"* c #AA947B", +"= c #C5AC8F", +" ", +" .. ", +" .+. ", +"......+@. ", +".#$%&*+@=.", +".#$%&*+@=.", +"......+@. ", +" .+. ", +" .. ", +" "}; diff --git a/src/icons/group.h b/src/icons/group.h new file mode 100644 index 0000000..fea265a --- /dev/null +++ b/src/icons/group.h @@ -0,0 +1,60 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (group) +#endif +#ifdef __GNUC__ +static const guint8 group[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 group[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (645) */ + "\0\0\2\235" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (64) */ + "\0\0\0@" + /* width (16) */ + "\0\0\0\20" + /* height (12) */ + "\0\0\0\14" + /* pixel_data: */ + "\210\0\0\0\0\2wyt\35\221\222\217\244\202\231\232\227\353\2\221\222\217" + "\244wyt\35\204\0\0\0\0\2\210\212\205\35\246\247\244\244\202\257\260\255" + "\353\11\246\247\244\244\201\203~.\216\217\214\240\313\313\312\377\332" + "\332\332\377\333\333\333\377\313\313\312\377\216\217\214\240wyt\23\202" + "\0\0\0\0\16\210\212\205\23\242\243\240\240\350\350\347\377\371\371\371" + "\377\372\372\372\377\350\350\347\377\234\235\232\271\265\266\263\355" + "\324\324\323\377\327\330\327\377\331\332\331\377\325\326\325\377\271" + "\272\270\354wytB\202\0\0\0\0\16\210\212\205B\324\325\323\354\362\363" + "\361\377\366\367\366\377\370\371\370\377\364\365\364\377\321\322\320" + "\362\257\257\255\374\315\316\314\377\322\322\320\377\323\323\322\377" + "\317\320\316\377\275\275\274\373wytQ\202\0\0\0\0\16\210\212\205Q\330" + "\330\327\373\353\354\351\377\360\360\356\377\361\361\360\377\355\356" + "\354\377\327\327\326\374\245\245\242\360\317\320\316\377\312\314\311" + "\377\313\314\312\377\320\321\317\377\264\264\262\351wyt\77\202\0\0\0" + "\0\11\210\212\205\77\316\316\314\351\355\356\354\377\347\351\346\377" + "\350\351\347\377\356\357\355\377\313\313\311\361\221\222\217\361\303" + "\303\302\377\202\322\323\321\377\3\303\303\302\377\225\226\223\355|~" + "zW\202\0\0\0\0\3\216\220\213W\252\253\250\355\337\337\336\377\202\360" + "\361\357\377\11\337\337\336\377\251\252\247\373\256\257\254\377\242\243" + "\240\377\230\231\226\377\227\231\226\377\241\243\237\377\277\277\275" + "\377\232\233\230\303\202\0\0\0\0\11\260\261\256\303\332\334\331\377\271" + "\273\267\377\256\257\253\377\255\257\253\377\270\272\266\377\332\333" + "\330\377\266\267\265\377\306\306\304\377\203\305\306\303\377\2\315\315" + "\314\377\234\236\233\366\202\0\0\0\0\4\263\265\261\366\353\354\352\377" + "\342\343\340\377\341\343\337\377\202\341\342\337\377\2\352\353\351\377" + "\263\265\261\377\202\307\310\306\377\4\307\307\305\377\306\307\305\377" + "\316\317\315\377\234\235\233\373\202\0\0\0\0\2\263\264\261\373\355\355" + "\353\377\202\344\345\342\377\5\344\344\341\377\343\344\341\377\354\355" + "\353\377\263\264\261\377\314\315\313\377\202\313\314\312\377\3\313\314" + "\311\377\317\320\316\377\233\234\231\373\202\0\0\0\0\3\261\262\257\373" + "\356\356\355\377\351\352\350\377\202\350\351\347\377\3\350\351\346\377" + "\355\356\354\377\260\261\256\377\204\234\235\233\377\2\233\234\231\377" + "\210\212\206\322\202\0\0\0\0\2\233\235\231\333\261\263\257\377\204\263" + "\264\261\377\2\261\263\257\377\234\236\231\322\207\0\0\0\0"}; + + diff --git a/src/icons/group.png b/src/icons/group.png new file mode 100644 index 0000000..37a1bdd Binary files /dev/null and b/src/icons/group.png differ diff --git a/src/icons/html.h b/src/icons/html.h new file mode 100644 index 0000000..13d7e3b --- /dev/null +++ b/src/icons/html.h @@ -0,0 +1,65 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (html) +#endif +#ifdef __GNUC__ +static const guint8 html[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 html[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (724) */ + "\0\0\2\354" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (56) */ + "\0\0\0""8" + /* width (14) */ + "\0\0\0\16" + /* height (14) */ + "\0\0\0\16" + /* pixel_data: */ + "\6\251\253\245\0\260\263\255\0\252\253\246\0\252\253\246\377\251\253" + "\246\377\251\253\245\377\202\250\253\245\377\3\251\253\245\377\250\253" + "\245\266\245\250\241D\203\377\377\377\0\202\245\247\241\0\12\245\247" + "\241\377\353\354\351\377\347\351\345\377\345\350\343\377\343\346\341" + "\377\341\344\336\377\342\345\337\377\351\351\350\377\257\262\254\366" + "\245\250\242z\202\377\377\377\0\202\243\246\240\0\14\243\246\240\377" + "\347\351\345\377\344\347\342\377\343\345\340\377\341\344\336\377\337" + "\342\334\377\335\340\332\377\331\332\330\377\375\375\375\377\275\276" + "\272\366\243\246\240x\377\377\377\0\202\242\244\236\0\14\242\244\236" + "\377\344\346\341\377\341\343\336\377\337\342\335\377\336\341\333\377" + "\334\340\331\377\333\336\327\377\304\306\301\377\377\377\377\377\374" + "\375\374\377\247\251\244\367\377\377\377\0\202\240\242\234\0\14\240\242" + "\234\377\340\343\334\377\335\340\332\377\265\300\307\377`}\244\377(Q" + "\213\377^{\242\377\277\301\273\377\254\260\252\377\262\263\261\377\237" + "\242\233\375\233\236\230\4\202\236\241\233\0\14\236\241\233\377\334\340" + "\332\377\266\301\310\377Gi\204\377\221\251\230\377\252\276\231\377\237" + "\266\232\377Kn\210\377\257\272\302\377\326\332\323\377\233\235\230\374" + "\232\234\226\10\202\235\237\231\0\14\235\237\231\377\334\337\331\377" + "d\177\244\377\225\252X\377\217\255\254\377\210\253\300\377\267\315n\377" + "\245\271X\377`}\243\377\332\335\327\377\231\234\226\374\230\232\225\10" + "\202\233\235\227\0\14\233\235\227\377\335\340\332\377+S\214\377\237\264" + "\242\377i\217\251\377\215\254\216\377\274\320@\377\251\275o\377)R\213" + "\377\336\340\332\377\230\232\224\374\226\231\223\10\202\231\233\226\0" + "\14\231\233\226\377\340\343\335\377g\202\247\377\200\234\236\377\237" + "\2635\377[\204\260\377\230\261s\377\215\246\212\377d\200\246\377\341" + "\344\336\377\226\230\223\374\225\227\221\10\202\227\232\224\0\14\227" + "\232\224\377\343\346\341\377\277\310\317\3775Z\222\377\211\242x\377\231" + "\261\320\377\203\235\230\377;`\217\377\275\306\317\377\345\347\342\377" + "\224\226\221\374\223\225\220\10\202\226\230\222\0\14\226\230\222\377" + "\347\350\344\377\345\347\343\377\303\313\322\377j\205\252\377,T\215\377" + "h\203\252\377\301\312\323\377\347\351\345\377\350\352\346\377\223\225" + "\217\374\221\223\216\10\202\224\226\221\0\4\224\226\221\377\351\352\347" + "\377\350\352\346\377\351\352\347\377\202\352\353\350\377\1\352\354\351" + "\377\202\353\354\351\377\3\354\355\352\377\221\223\216\374\220\222\214" + "\10\202\222\225\217\0\1\222\225\217\377\202\354\355\352\377\4\355\356" + "\353\377\356\357\354\377\356\357\355\377\357\360\355\377\203\357\360" + "\356\377\2\217\221\214\374\216\220\213\10\202\220\222\215\0\1\220\222" + "\215I\203\216\220\213\377\202\215\217\212\377\1\215\217\211\377\202\214" + "\216\211\377\3\214\216\210\377\215\217\212K\214\216\211\0"}; + + diff --git a/src/icons/html.png b/src/icons/html.png new file mode 100644 index 0000000..c2728d6 Binary files /dev/null and b/src/icons/html.png differ diff --git a/src/icons/interface.xpm b/src/icons/interface.xpm new file mode 100644 index 0000000..2f71bd2 --- /dev/null +++ b/src/icons/interface.xpm @@ -0,0 +1,24 @@ +/* XPM */ +static char * interface_xpm[] = { +"16 16 5 1", +" c None", +". c #525252", +"+ c #FBFAEF", +"@ c #EAE5B8", +"# c #BCB156", +" ", +" . ", +" . ", +" . ", +" . ..... ", +" . .++++. ", +" . ....@@@@+. ", +" . .@@@@@....", +" . .@@@@@.###", +" . .@@@@@....", +" . ....@@@@#. ", +" . .####. ", +" . ..... ", +" . ", +" . ", +" "}; diff --git a/src/icons/jpilot.xpm b/src/icons/jpilot.xpm new file mode 100644 index 0000000..b72225c --- /dev/null +++ b/src/icons/jpilot.xpm @@ -0,0 +1,25 @@ +/* XPM */ +static char * jpilot_xpm[] = { +"16 16 6 1", +" c None", +". c #000000", +"+ c #848587", +"@ c #34991E", +"# c #F8FAF6", +"$ c #97989B", +" ", +" ........... ", +" .+++++++++. ", +" .+@@@@@@@+. ", +" .+@@@@@@@+. ", +" .+@@@@@@@+. ", +" .+@@@@@@@+. ", +" .+@@@@@@@+. ", +" .+@@@@@@@+. ", +" .+@@@@@@@+. ", +" .+++++++++. ", +" .++++#++++. ", +" .+#+#$#+#+. ", +" .++++#++++. ", +" ........... ", +" "}; diff --git a/src/icons/ldap.xpm b/src/icons/ldap.xpm new file mode 100644 index 0000000..c1e61e4 --- /dev/null +++ b/src/icons/ldap.xpm @@ -0,0 +1,25 @@ +/* XPM */ +static char * ldap_xpm[] = { +"16 16 6 1", +" c None", +". c #000000", +"+ c #E3DAB1", +"@ c #606060", +"# c #A0A0A0", +"$ c #F6210A", +" ", +" . ........ ", +" . .++++++. ", +" . .@#@#@#. ", +" . .#@#@#@. ", +" . .++++++. ", +" @.@ .++++++. ", +" @......++++++. ", +" @.@ .++++++. ", +" . .++++++. ", +" . .++++++. ", +" . .+++$$+. ", +" . .+++$$+. ", +" . .++++++. ", +" . ........ ", +" "}; diff --git a/src/icons/linewrap.xpm b/src/icons/linewrap.xpm new file mode 100644 index 0000000..bce4af2 --- /dev/null +++ b/src/icons/linewrap.xpm @@ -0,0 +1,29 @@ +/* XPM */ +static char * linewrap_xpm[] = { +"24 24 2 1", +" c None", +". c #000000", +" ", +" ", +" ", +" ", +" ", +" ", +" ............ . ", +" . . ", +" ............ .... ", +" . ", +" ............ ", +" ", +" ..... ", +" ", +" ............ . ", +" . . ", +" ............ .... ", +" . ", +" ..... ", +" ", +" ", +" ", +" ", +" "}; diff --git a/src/icons/mail.xpm b/src/icons/mail.xpm new file mode 100644 index 0000000..bf4766c --- /dev/null +++ b/src/icons/mail.xpm @@ -0,0 +1,41 @@ +/* XPM */ +static char * mail_xpm[] = { +"13 10 28 1", +" c None", +". c #000000", +"+ c #474747", +"@ c #F5F5F5", +"# c #323232", +"$ c #F4F4F4", +"% c #C1C1C1", +"& c #EAEAEA", +"* c #EBEBEB", +"= c #AAAAAA", +"- c #3D3D3D", +"; c #EFEFEF", +"> c #F3F3F3", +", c #3F3F3F", +"' c #464646", +") c #BDBDBD", +"! c #D6D6D6", +"~ c #A0A0A0", +"{ c #F2F2F2", +"] c #BABABA", +"^ c #E9E9E9", +"/ c #8C8C8C", +"( c #BFBFBF", +"_ c #DADADA", +": c #CDCDCD", +"< c #A7A7A7", +"[ c #D3D3D3", +"} c #282828", +" ........... ", +".+@@@@@@@@@#.", +".@+@@@$@$$+%.", +".@&+@@$@$+*=.", +".@@@+@@@-;**.", +".>@;+,@'+@>).", +".@$+@@+@>+!~.", +".@+$@@{$>]#^.", +".+~~/(]_:<[}.", +" ........... "}; diff --git a/src/icons/mark.xpm b/src/icons/mark.xpm new file mode 100644 index 0000000..de17d9c --- /dev/null +++ b/src/icons/mark.xpm @@ -0,0 +1,16 @@ +/* XPM */ +static char * mark_xpm[] = { +"10 10 3 1", +" c None", +". c #FFFFFF", +"+ c #000000", +" ..", +" .+.", +" .++.", +".. .+++.", +".+. .+++. ", +".++.+++. ", +".+++++. ", +" .+++. ", +" .+. ", +" . "}; diff --git a/src/icons/new.xpm b/src/icons/new.xpm new file mode 100644 index 0000000..7499261 --- /dev/null +++ b/src/icons/new.xpm @@ -0,0 +1,62 @@ +/* XPM */ +static char * new_xpm[] = { +"13 10 49 1", +" c None", +". c #000000", +"+ c #D52828", +"@ c #F5CECD", +"# c #F5CFCE", +"$ c #F4CECB", +"% c #F4CDCB", +"& c #9C1D1D", +"* c #F4CDC9", +"= c #F3C8C2", +"- c #F3C8C4", +"; c #F4CAC7", +"> c #E5837C", +", c #EFB6AE", +"' c #F3CCC6", +") c #F3CAC2", +"! c #F3CBC6", +"~ c #F3CBC4", +"{ c #EFB7B0", +"] c #E06F63", +"^ c #F5CECE", +"/ c #F0BFB7", +"( c #F3C7C2", +"_ c #F0C0B5", +": c #F3CCC4", +"< c #F2C6BF", +"[ c #E37E70", +"} c #F4CDC7", +"| c #F3CBC2", +"1 c #F2CAC1", +"2 c #F1C6BA", +"3 c #E89789", +"4 c #DE6255", +"5 c #F3CAC4", +"6 c #F0C3B7", +"7 c #F2C7BF", +"8 c #E27A69", +"9 c #A61F1F", +"0 c #EEB4AD", +"a c #DF685C", +"b c #DE6559", +"c c #DA5345", +"d c #E37F6E", +"e c #E17968", +"f c #E89C8D", +"g c #E58C7E", +"h c #DF6A5A", +"i c #E8968B", +"j c #771616", +" ........... ", +".+@#@$%@@@%&.", +".@+$*#=$-;+>.", +".%,+*')!~+{].", +".^%*+$'*+/{{.", +".(@_++:++)<[.", +".@=+}|+12+34.", +".@+5'~677890.", +".+abcdefghij.", +" ........... "}; diff --git a/src/icons/offline.xpm b/src/icons/offline.xpm new file mode 100644 index 0000000..baa36a4 --- /dev/null +++ b/src/icons/offline.xpm @@ -0,0 +1,228 @@ +/* XPM */ +static char * offline_xpm[] = { +"26 12 213 2", +" c None", +". c #FFFFFF", +"+ c #FFFFCC", +"@ c #FFFF99", +"# c #FFFF66", +"$ c #FFFF33", +"% c #FFFF00", +"& c #FFCCFF", +"* c #FFCCCC", +"= c #FFCC99", +"- c #FFCC66", +"; c #FFCC33", +"> c #FFCC00", +", c #FF99FF", +"' c #FF99CC", +") c #FF9999", +"! c #FF9966", +"~ c #FF9933", +"{ c #FF9900", +"] c #FF66FF", +"^ c #FF66CC", +"/ c #FF6699", +"( c #FF6666", +"_ c #FF6633", +": c #FF6600", +"< c #FF33FF", +"[ c #FF33CC", +"} c #FF3399", +"| c #FF3366", +"1 c #FF3333", +"2 c #FF3300", +"3 c #FF00FF", +"4 c #FF00CC", +"5 c #FF0099", +"6 c #FF0066", +"7 c #FF0033", +"8 c #FF0000", +"9 c #CCFFFF", +"0 c #CCFFCC", +"a c #CCFF99", +"b c #CCFF66", +"c c #CCFF33", +"d c #CCFF00", +"e c #CCCCFF", +"f c #CCCCCC", +"g c #CCCC99", +"h c #CCCC66", +"i c #CCCC33", +"j c #CCCC00", +"k c #CC99FF", +"l c #CC99CC", +"m c #CC9999", +"n c #CC9966", +"o c #CC9933", +"p c #CC9900", +"q c #CC66FF", +"r c #CC66CC", +"s c #CC6699", +"t c #CC6666", +"u c #CC6633", +"v c #CC6600", +"w c #CC33FF", +"x c #CC33CC", +"y c #CC3399", +"z c #CC3366", +"A c #CC3333", +"B c #CC3300", +"C c #CC00FF", +"D c #CC00CC", +"E c #CC0099", +"F c #CC0066", +"G c #CC0033", +"H c #CC0000", +"I c #99FFFF", +"J c #99FFCC", +"K c #99FF99", +"L c #99FF66", +"M c #99FF33", +"N c #99FF00", +"O c #99CCFF", +"P c #99CCCC", +"Q c #99CC99", +"R c #99CC66", +"S c #99CC33", +"T c #99CC00", +"U c #9999FF", +"V c #9999CC", +"W c #999999", +"X c #999966", +"Y c #999933", +"Z c #999900", +"` c #9966FF", +" . c #9966CC", +".. c #996699", +"+. c #996666", +"@. c #996633", +"#. c #996600", +"$. c #9933FF", +"%. c #9933CC", +"&. c #993399", +"*. c #993366", +"=. c #993333", +"-. c #993300", +";. c #9900FF", +">. c #9900CC", +",. c #990099", +"'. c #990066", +"). c #990033", +"!. c #990000", +"~. c #66FFFF", +"{. c #66FFCC", +"]. c #66FF99", +"^. c #66FF66", +"/. c #66FF33", +"(. c #66FF00", +"_. c #66CCFF", +":. c #66CCCC", +"<. c #66CC99", +"[. c #66CC66", +"}. c #66CC33", +"|. c #66CC00", +"1. c #6699FF", +"2. c #6699CC", +"3. c #669999", +"4. c #669966", +"5. c #669933", +"6. c #669900", +"7. c #6666FF", +"8. c #6666CC", +"9. c #666699", +"0. c #666666", +"a. c #666633", +"b. c #666600", +"c. c #6633FF", +"d. c #6633CC", +"e. c #663399", +"f. c #663366", +"g. c #663333", +"h. c #663300", +"i. c #6600FF", +"j. c #6600CC", +"k. c #660099", +"l. c #660066", +"m. c #660033", +"n. c #660000", +"o. c #33FFFF", +"p. c #33FFCC", +"q. c #33FF99", +"r. c #33FF66", +"s. c #33FF33", +"t. c #33FF00", +"u. c #33CCFF", +"v. c #33CCCC", +"w. c #33CC99", +"x. c #33CC66", +"y. c #33CC33", +"z. c #33CC00", +"A. c #3399FF", +"B. c #3399CC", +"C. c #339999", +"D. c #339966", +"E. c #339933", +"F. c #339900", +"G. c #3366FF", +"H. c #3366CC", +"I. c #336699", +"J. c #336666", +"K. c #336633", +"L. c #336600", +"M. c #3333FF", +"N. c #3333CC", +"O. c #333399", +"P. c #333366", +"Q. c #333333", +"R. c #333300", +"S. c #3300FF", +"T. c #3300CC", +"U. c #330099", +"V. c #330066", +"W. c #330033", +"X. c #330000", +"Y. c #00FFFF", +"Z. c #00FFCC", +"`. c #00FF99", +" + c #00FF66", +".+ c #00FF33", +"++ c #00FF00", +"@+ c #00CCFF", +"#+ c #00CCCC", +"$+ c #00CC99", +"%+ c #00CC66", +"&+ c #00CC33", +"*+ c #00CC00", +"=+ c #0099FF", +"-+ c #0099CC", +";+ c #009999", +">+ c #009966", +",+ c #009933", +"'+ c #009900", +")+ c #0066FF", +"!+ c #0066CC", +"~+ c #006699", +"{+ c #006666", +"]+ c #006633", +"^+ c #006600", +"/+ c #0033FF", +"(+ c #0033CC", +"_+ c #003399", +":+ c #003366", +"<+ c #003333", +"[+ c #003300", +"}+ c #0000FF", +"|+ c #0000CC", +" ", +" P.P.P.P.P. ", +" P.. e |+e |+ P.P.P.P. ", +" P.e U P.8.|+ P.. U P. ", +" P.P.U U P.e |+ P.|+e U P.P. ", +"P.P.P.P.P.e P.U U P.8.|+ |+U U P.e P.P.P.P.", +"U U U U P.U P.U U P.e |+ P.|+U U P.U P.8.8.8.", +"P.P.P.P.P.8.P.U U P.8.|+ |+U U P.8.P.P.P.P.", +" P.P.U U P.e |+ P.|+U U P.P. ", +" P.U U P.P.|+ P.8.8.P. ", +" P.8.8.P.W P. P.P.P.P. ", +" P.P.P.P.P. "}; diff --git a/src/icons/online.xpm b/src/icons/online.xpm new file mode 100644 index 0000000..ed50865 --- /dev/null +++ b/src/icons/online.xpm @@ -0,0 +1,232 @@ +/* XPM */ +static char * online_xpm[] = { +"26 12 217 2", +" c None", +". c #FFFFFF", +"+ c #FFFFCC", +"@ c #FFFF99", +"# c #FFFF66", +"$ c #FFFF33", +"% c #FFFF00", +"& c #FFCCFF", +"* c #FFCCCC", +"= c #FFCC99", +"- c #FFCC66", +"; c #FFCC33", +"> c #FFCC00", +", c #FF99FF", +"' c #FF99CC", +") c #FF9999", +"! c #FF9966", +"~ c #FF9933", +"{ c #FF9900", +"] c #FF66FF", +"^ c #FF66CC", +"/ c #FF6699", +"( c #FF6666", +"_ c #FF6633", +": c #FF6600", +"< c #FF33FF", +"[ c #FF33CC", +"} c #FF3399", +"| c #FF3366", +"1 c #FF3333", +"2 c #FF3300", +"3 c #FF00FF", +"4 c #FF00CC", +"5 c #FF0099", +"6 c #FF0066", +"7 c #FF0033", +"8 c #FF0000", +"9 c #CCFFFF", +"0 c #CCFFCC", +"a c #CCFF99", +"b c #CCFF66", +"c c #CCFF33", +"d c #CCFF00", +"e c #CCCCFF", +"f c #CCCCCC", +"g c #CCCC99", +"h c #CCCC66", +"i c #CCCC33", +"j c #CCCC00", +"k c #CC99FF", +"l c #CC99CC", +"m c #CC9999", +"n c #CC9966", +"o c #CC9933", +"p c #CC9900", +"q c #CC66FF", +"r c #CC66CC", +"s c #CC6699", +"t c #CC6666", +"u c #CC6633", +"v c #CC6600", +"w c #CC33FF", +"x c #CC33CC", +"y c #CC3399", +"z c #CC3366", +"A c #CC3333", +"B c #CC3300", +"C c #CC00FF", +"D c #CC00CC", +"E c #CC0099", +"F c #CC0066", +"G c #CC0033", +"H c #CC0000", +"I c #99FFFF", +"J c #99FFCC", +"K c #99FF99", +"L c #99FF66", +"M c #99FF33", +"N c #99FF00", +"O c #99CCFF", +"P c #99CCCC", +"Q c #99CC99", +"R c #99CC66", +"S c #99CC33", +"T c #99CC00", +"U c #9999FF", +"V c #9999CC", +"W c #999999", +"X c #999966", +"Y c #999933", +"Z c #999900", +"` c #9966FF", +" . c #9966CC", +".. c #996699", +"+. c #996666", +"@. c #996633", +"#. c #996600", +"$. c #9933FF", +"%. c #9933CC", +"&. c #993399", +"*. c #993366", +"=. c #993333", +"-. c #993300", +";. c #9900FF", +">. c #9900CC", +",. c #990099", +"'. c #990066", +"). c #990033", +"!. c #990000", +"~. c #66FFFF", +"{. c #66FFCC", +"]. c #66FF99", +"^. c #66FF66", +"/. c #66FF33", +"(. c #66FF00", +"_. c #66CCFF", +":. c #66CCCC", +"<. c #66CC99", +"[. c #66CC66", +"}. c #66CC33", +"|. c #66CC00", +"1. c #6699FF", +"2. c #6699CC", +"3. c #669999", +"4. c #669966", +"5. c #669933", +"6. c #669900", +"7. c #6666FF", +"8. c #6666CC", +"9. c #666699", +"0. c #666666", +"a. c #666633", +"b. c #666600", +"c. c #6633FF", +"d. c #6633CC", +"e. c #663399", +"f. c #663366", +"g. c #663333", +"h. c #663300", +"i. c #6600FF", +"j. c #6600CC", +"k. c #660099", +"l. c #660066", +"m. c #660033", +"n. c #660000", +"o. c #33FFFF", +"p. c #33FFCC", +"q. c #33FF99", +"r. c #33FF66", +"s. c #33FF33", +"t. c #33FF00", +"u. c #33CCFF", +"v. c #33CCCC", +"w. c #33CC99", +"x. c #33CC66", +"y. c #33CC33", +"z. c #33CC00", +"A. c #3399FF", +"B. c #3399CC", +"C. c #339999", +"D. c #339966", +"E. c #339933", +"F. c #339900", +"G. c #3366FF", +"H. c #3366CC", +"I. c #336699", +"J. c #336666", +"K. c #336633", +"L. c #336600", +"M. c #3333FF", +"N. c #3333CC", +"O. c #333399", +"P. c #333366", +"Q. c #333333", +"R. c #333300", +"S. c #3300FF", +"T. c #3300CC", +"U. c #330099", +"V. c #330066", +"W. c #330033", +"X. c #330000", +"Y. c #00FFFF", +"Z. c #00FFCC", +"`. c #00FF99", +" + c #00FF66", +".+ c #00FF33", +"++ c #00FF00", +"@+ c #00CCFF", +"#+ c #00CCCC", +"$+ c #00CC99", +"%+ c #00CC66", +"&+ c #00CC33", +"*+ c #00CC00", +"=+ c #0099FF", +"-+ c #0099CC", +";+ c #009999", +">+ c #009966", +",+ c #009933", +"'+ c #009900", +")+ c #0066FF", +"!+ c #0066CC", +"~+ c #006699", +"{+ c #006666", +"]+ c #006633", +"^+ c #006600", +"/+ c #0033FF", +"(+ c #0033CC", +"_+ c #003399", +":+ c #003366", +"<+ c #003333", +"[+ c #003300", +"}+ c #0000FF", +"|+ c #0000CC", +"1+ c #000099", +"2+ c #000066", +"3+ c #000033", +"4+ c #000000", +" ", +" 8.P.P. ", +" 8.. e P.8.8.P. ", +" 8.U e }+e . e P. ", +" P.P.U e }+U U U P.P. ", +"8.8.8.8.8.8.8.8.8.e P.U e }+U U U P.e 4+P.P.P.P.P.P.", +"e e U e U U U U P.U P.U e }+U U U P.U P.U U U U U U ", +"P.P.P.P.P.P.P.P.P.8.P.U e }+U U U P.8.P.P.P.P.P.P.P.", +" P.P.U e }+8.8.8.P.P. ", +" P.U 8.}+8.8.8.P. ", +" 4+8.8.P.P.P.P. ", +" P.P.P. "}; diff --git a/src/icons/replied.xpm b/src/icons/replied.xpm new file mode 100644 index 0000000..e16ab02 --- /dev/null +++ b/src/icons/replied.xpm @@ -0,0 +1,24 @@ +/* XPM */ +static char * replied_xpm[] = { +"10 10 11 1", +" c None", +". c #000000", +"+ c #E0D8B0", +"@ c #B39C82", +"# c #BCA488", +"$ c #C5AC8F", +"% c #AA947B", +"& c #A18D75", +"* c #98856E", +"= c #8F7D68", +"- c #867561", +" . ", +" .. .+. ", +" .@..+++.", +" .#@......", +".$#@%&*=-.", +".$#@%&*=. ", +" .#@.... ", +" .@. ", +" .. ", +" "}; diff --git a/src/icons/stock_addressbook.h b/src/icons/stock_addressbook.h new file mode 100644 index 0000000..15743e2 --- /dev/null +++ b/src/icons/stock_addressbook.h @@ -0,0 +1,86 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_addressbook) +#endif +#ifdef __GNUC__ +static const guint8 stock_addressbook[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_addressbook[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1560) */ + "\0\0\6""0" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\271\0\0\0\0\7\0\0\0\30\0\0\0""2\0\0\0q\0\0\0\313\0\0\0\347\0\0\0\327" + "\0\0\0\13\214\0\0\0\0\14\0\0\0\2\5\5\5#&&&K\27\27\27\240\10\10\10\350" + "\5\5\5\363YYY\372\272\272\272\375\364\364\364\375\365\365\365\372\342" + "\342\342\352\34\34\34\275\214\0\0\0\0\14\4\4\4\215\33\33\33\370___\375" + "\304\304\304\376\370\370\370\374\376\376\376\375\377\377\377\377\365" + "\365\365\377\301\301\301\377\332\332\332\377\353\353\353\363\2\2\2\366" + "\211\0\0\0\0\5\0\0\0\2\0\0\0\7\0\0\0\10\13\13\13\300\357\357\357\375" + "\202\362\362\362\377\10\354\354\354\377\357\357\357\377\361\361\361\377" + "\313\313\313\377\302\302\302\377\326\326\326\377\340\340\340\360\0\0" + "\0\364\210\0\0\0\0\3...A\33\33\33\361\31\31\31\373\205\35\35\35\377\10" + "000\377VVV\377\352\352\352\377\334\334\334\377\271\271\271\377\306\306" + "\306\377\330\330\330\357\0\0\0\347\210\0\0\0\0\6<<<\367\331\331\331\377" + "\330\330\330\377\332\332\332\377\331\331\331\377\332\332\332\377\202" + "\331\331\331\377\10\321\321\321\377xxx\377ddd\377\341\341\341\377\370" + "\370\370\377\363\363\363\377\317\317\317\327\0\0\0\347\210\0\0\0\0\3" + "\7\7\7\341\232\232\232\376\256\256\256\377\205\227\227\227\377\15\230" + "\230\230\377\224\224\224\377```\377\220\220\220\377\375\375\375\377\377" + "\377\377\377\270\270\270\312\35\35\35\302\2\2\2\372\1\1\1\372\22\22\22" + "\257\6\6\6\3NNN\3\203\0\0\0\0\4""111\10...\214ggg\357\253\253\253\377" + "\204\234\234\234\377\202\235\235\235\377\13\232\232\232\377ccc\377\270" + "\270\270\377\342\345\346\377\232\235\236\377PQQ\377NNN\377CCC\375\0\0" + "\0\367\15\15\15\2NNN\2\204\0\0\0\0\23\13\13\13\2\5\5\5]BBB\361\233\233" + "\233\377ddd\350ppp\377xxx\377\200\200\200\377\210\210\210\377\216\216" + "\216\377\207\207\207\377TWY\377\254\264\271\377\237\246\252\377EHI\376" + "111\352\1\1\1\365\0\0\0'\0\0\0\1\207\0\0\0\0\17DDD\1\10\10\10\237\14" + "\14\14\373\11\11\11\377\4\4\4\377\0\0\0\377\7\7\7\377\15\15\15\377\26" + "\26\26\377qx|\377\254\260\263\377#\40!\377\14\5\6\376\0\0\0\364\0\0\0" + "(\210\0\0\0\0\17\25\25\25\5\12\12\12\307\275\275\275\365\332\332\332" + "\375\275\275\275\377\240\240\240\377\202\202\202\377ZZZ\377555\377\20" + "\20\20\377\177\202\204\377y}\177\377#\27\32\377\16\6\10\377\7\2\3\214" + "\203\0\0\0\0\202\0\0\0\1\203\0\0\0\0\3\"\"\"\20$$$\315\340\340\340\374" + "\207\377\377\377\377\10\214\214\214\377fff\377``a\377&\30\33\377N*1\377" + "\0\0\0\377\0\0\0\20\0\0\0\2\203\0\0\0\4\7\0\0\0\3\0\0\0\1\0\0\0\10\23" + "\23\23\304\304\304\304\336\356\356\356\377\373\373\373\377\204\377\377" + "\377\377'\375\375\375\377\274\274\274\366\21\21\21\327\34\34\34\326h" + "hh\373=)-\377I(/\377\6\1\2\377\10\3\3z\0\0\0\14\0\0\0\16\0\0\0\15\0\0" + "\0\11\0\0\0\6\0\0\0\3\0\0\0\1\10\10\10-\6\6\6\317\14\14\14\336\30\30" + "\30\351\246\244\244\364\320\316\316\377\357\357\357\377\370\370\370\377" + "\310\310\310\375\24\24\24\350\0\0\0H\2\2\2<\"\"\"IT:@\272G%,\3772\23" + "\32\377\6\2\3\366\1\0\0;\0\0\0\36\0\0\0\27\0\0\0\20\0\0\0\13\0\0\0\5" + "\203\0\0\0\0\25\0\0\0\7\0\0\0\310\11\3\5\3663#'\377I=@\377{su\375J\77" + "A\364'\11\16\224)\11\16h\30\5\10b\10\2\3[cIN\3238\27\36\3774\25\34\377" + "\3\1\1\377\6\2\3\242\0\0\0>\0\0\0'\0\0\0\31\0\0\0\21\0\0\0\7\204\0\0" + "\0\0\24\1\0\0\315:\33$\364B\34%\365T\26\40\352W\24\36\374_\25\40\373" + "w\34(\370t\33'\355m\31%\336d\30#\323kIO\363>\31!\3772\24\32\377-\22\27" + "\377\0\0\0\376\2\1\1}\0\0\0K\0\0\0*\0\0\0\27\0\0\0\12\204\0\0\0\0\7\3" + "\0\1\304aBH\301\242w\200\366\250mx\375\213:F\377}\"/\377z\36+\377\202" + "y\34)\377\13v\36*\377uKS\3777\26\35\3770\23\31\377)\17\25\377!\14\20" + "\374\6\2\3\255\0\0\0o\0\0\0<\0\0\0\37\0\0\0\14\205\0\0\0\0\23\0\0\0\217" + "\1\1\1\314\14\10\11\342\207pw\332\252\213\223\370\265\201\214\377\214" + "=I\377z\35*\377w$0\377mBK\377<\30\37\377&\16\24\377\33\11\15\362\20\5" + "\7\306\0\0\0\224\0\0\0d\0\0\0:\0\0\0\35\0\0\0\11\206\0\0\0\0\22\0\0\0" + "\2\0\0\0d\0\0\0\266\1\0\0\321\11\6\6\342*\30\33\357j$.\370p\37+\377V" + "*3\3770\22\30\371\27\7\11\321\2\1\1\222\0\0\0p\0\0\0Q\0\0\0;\0\0\0&\0" + "\0\0\23\0\0\0\4\207\0\0\0\0\21\0\0\0\1\0\0\0\4\0\0\0\11\0\0\0W\0\0\0" + "\227\3\0\0\324\12\2\3\352\13\3\5\353\20\5\7\204\2\0\1Z\0\0\0>\0\0\0." + "\0\0\0&\0\0\0\40\0\0\0\25\0\0\0\10\0\0\0\1\211\0\0\0\0\16\0\0\0\1\0\0" + "\0\3\0\0\0\6\0\0\0\20\0\0\0\"\0\0\0+\0\0\0#\0\0\0\30\0\0\0\25\0\0\0\27" + "\0\0\0\25\0\0\0\17\0\0\0\10\0\0\0\2\215\0\0\0\0\1\0\0\0\1\203\0\0\0\3" + "\6\0\0\0\6\0\0\0\10\0\0\0\12\0\0\0\7\0\0\0\4\0\0\0\1\202\0\0\0\0"}; + + diff --git a/src/icons/stock_addressbook.png b/src/icons/stock_addressbook.png new file mode 100644 index 0000000..e3d953a Binary files /dev/null and b/src/icons/stock_addressbook.png differ diff --git a/src/icons/stock_attach.h b/src/icons/stock_attach.h new file mode 100644 index 0000000..8d89b2e --- /dev/null +++ b/src/icons/stock_attach.h @@ -0,0 +1,93 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_attach) +#endif +#ifdef __GNUC__ +static const guint8 stock_attach[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_attach[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1546) */ + "\0\0\6\"" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\240\0\0\0\0\1\0\0\0c\205\0\0\0\377\2\0\0\0\21\0\0\0\2\220\0\0\0\0\4" + "\0\0\0\377\0\0\0\26\0\0\0\"\0\0\0$\202\0\0\0%\2\0\0\0\377\0\0\0l\202" + "\0\0\0\377\2\0\0\0\10\0\0\0\2\214\0\0\0\0\15\0\0\0\377\0\0\0\"\0\0\0" + "\20\0\0\0$\0\0\0~\1\1\1\377QQQ\377\232\232\232\377\317\317\317\377oo" + "o\377\0\0\0\377\0\0\0\16\0\0\0\2\213\0\0\0\0\6\0\0\0\377\0\0\0x\0\0\0" + "\377FFF\377\245\245\245\377\342\342\342\377\202\377\377\377\377\5\366" + "\366\366\377\212\212\212\377999\377\0\0\0(\0\0\0\10\210\0\0\0\0\20\0" + "\0\0\34\0\0\0B\0\0\0\377\34\34\34\377\213\213\213\377\346\346\346\377" + "\356\356\356\377\341\341\341\377\377\377\377\377\370\370\370\377\367" + "\367\367\377\366\366\366\377\314\314\314\377VVV\377\0\0\0^\0\0\0\12\206" + "\0\0\0\0\23\0\0\0@\0\0\0\377>>>\377\201\201\201\377\324\324\324\377\34" + "\34\34\377\347\347\347\377\327\327\327\377\372\372\372\377\371\371\371" + "\377\370\370\370\377\302\302\302\377\313\313\313\377\365\365\365\377" + "\331\331\331\377\201\201\201\377\0\0\0\377\0\0\0\25\0\0\0\3\203\0\0\0" + "\0\4\0\0\0:\3\3\3\377TTT\377\346\346\346\377\203\377\377\377\377\16\0" + "\0\0\377\327\327\327\377\336\336\336\377\263\263\263\377yyy\377\0\0\0" + "\377\342\342\342\377\366\366\366\377\365\365\365\377\364\364\364\377" + "\235\235\235\377(((\377\0\0\0)\0\0\0\10\203\0\0\0\0\2\0\0\0\377\366\366" + "\366\377\202\377\377\377\377\202\372\372\372\377\17\373\373\373\377\0" + "\0\0\377\246\246\246\377\305\305\305\377\360\360\360\377\370\370\370" + "\377\0\0\0\377\342\342\342\377\302\302\302\377\312\312\312\377\364\364" + "\364\377\307\307\307\377VVV\377\0\0\0^\0\0\0\12\203\0\0\0\0\12\0\0\0" + "\377\362\362\362\377\370\370\370\377\371\371\371\377\316\316\316\377" + "\304\304\304\377\305\305\305\377\0\0\0\377\325\325\325\377\371\371\371" + "\377\202\370\370\370\377\4\0\0\0\377\263\263\263\377\332\332\332\377" + "\364\364\364\377\202\363\363\363\377\4yyy\377\0\0\0\377\0\0\0\25\0\0" + "\0\3\202\0\0\0\0\5\0\0\0h\205\205\205\377\377\377\377\377\370\370\370" + "\377\276\276\276\377\202\371\371\371\377\3\0\0\0\377\323\323\323\377" + "\334\334\334\377\202\302\302\302\377\12\0\0\0\377\342\342\342\377\365" + "\365\365\377\364\364\364\377\363\363\363\377\362\362\362\377\234\234" + "\234\377(((\377\0\0\0)\0\0\0\10\202\0\0\0\0\3\0\0\0\40HHH\377\377\377" + "\377\377\202\367\367\367\377\202\370\370\370\377\17\0\0\0\377\247\247" + "\247\377\326\326\326\377\367\367\367\377\366\366\366\377\0\0\0\377\342" + "\342\342\377\364\364\364\377\304\304\304\377\310\310\310\377\362\362" + "\362\377\306\306\306\377LLL\377\0\0\0^\0\0\0\12\202\0\0\0\0\27\0\0\0" + "\1\0\0\0\377\352\352\352\377\377\377\377\377\366\366\366\377\276\276" + "\276\377\302\302\302\377\0\0\0\377\345\345\345\377\330\330\330\377\366" + "\366\366\377\365\365\365\377\0\0\0\377\255\255\255\377\274\274\274\377" + "\352\352\352\377\340\340\340\377\361\361\361\377\352\352\352\377\220" + "\220\220\377\0\0\0\377\0\0\0\25\0\0\0\3\202\0\0\0\0\4\0\0\0ihhh\377\377" + "\377\377\377\365\365\365\377\202\366\366\366\377\11\242\242\242\377\0" + "\0\0\377\300\300\300\377\301\301\301\377\0\0\0\377xxx\377\326\326\326" + "\377\363\363\363\377\362\362\362\377\202\361\361\361\377\5\325\325\325" + "\377\256\256\256\377\25\25\25\377\0\0\0)\0\0\0\10\202\0\0\0\0\4\0\0\0" + "\4""999\377\377\377\377\377\364\364\364\377\202\365\365\365\377\2\332" + "\332\332\377\232\232\232\377\202\0\0\0\377\2yyy\377\326\326\326\377\202" + "\362\362\362\377\10\361\361\361\377\326\326\326\377\320\320\320\377\227" + "\227\227\377rrr\377JJJ\377\0\0\0.\0\0\0\11\203\0\0\0\0\25\0\0\0\377\354" + "\354\354\377\377\377\377\377\364\364\364\377\254\254\254\377\272\272" + "\272\377\335\335\335\377\363\363\363\377\316\316\316\377\333\333\333" + "\377\354\354\354\377\327\327\327\377\326\326\326\377\261\261\261\377" + "\201\201\201\377VVV\377###\377\0\0\0\377\0\0\0q\0\0\0)\0\0\0\10\203\0" + "\0\0\0\3\0\0\0hiii\377\377\377\377\377\204\363\363\363\377\202\362\362" + "\362\377\14\327\327\327\377\267\267\267\377\202\202\202\377@@@\377##" + "#\377\0\0\0\377\0\0\0\204\0\0\0""2\0\0\0.\0\0\0)\0\0\0\25\0\0\0\3\203" + "\0\0\0\0\4\0\0\0\4(((\377\377\377\377\377\363\363\363\377\202\362\362" + "\362\377\16\327\327\327\377\276\276\276\377\226\226\226\377222\377##" + "#\377\0\0\0\377\0\0\0u\0\0\0H\0\0\0/\0\0\0*\0\0\0\31\0\0\0\13\0\0\0\10" + "\0\0\0\3\205\0\0\0\0\20\0\0\0\377\346\346\346\377\350\350\350\377\314" + "\314\314\377\232\232\232\377@@@\377###\377\0\0\0\377\0\0\0\221\0\0\0" + "I\0\0\0/\0\0\0*\0\0\0\30\0\0\0\16\0\0\0\11\0\0\0\4\210\0\0\0\0\16\0\0" + "\0;999\377\202\202\202\377999\377\22\22\22\377\0\0\0\204\0\0\0I\0\0\0" + "/\0\0\0*\0\0\0\33\0\0\0\16\0\0\0\11\0\0\0\3\0\0\0\1\212\0\0\0\0\1\0\0" + "\0\3\202\0\0\0\377\10\0\0\0u\0\0\0""1\0\0\0*\0\0\0\31\0\0\0\16\0\0\0" + "\11\0\0\0\4\0\0\0\1\216\0\0\0\0\7\0\0\0\11\0\0\0&\0\0\0)\0\0\0\27\0\0" + "\0\12\0\0\0\4\0\0\0\1\221\0\0\0\0\4\0\0\0\1\0\0\0\7\0\0\0\10\0\0\0\3" + "\247\0\0\0\0"}; + + diff --git a/src/icons/stock_attach.png b/src/icons/stock_attach.png new file mode 100644 index 0000000..4e064b3 Binary files /dev/null and b/src/icons/stock_attach.png differ diff --git a/src/icons/stock_book.h b/src/icons/stock_book.h new file mode 100644 index 0000000..bed5938 --- /dev/null +++ b/src/icons/stock_book.h @@ -0,0 +1,60 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_book) +#endif +#ifdef __GNUC__ +static const guint8 stock_book[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_book[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (770) */ + "\0\0\3\32" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (64) */ + "\0\0\0@" + /* width (16) */ + "\0\0\0\20" + /* height (16) */ + "\0\0\0\20" + /* pixel_data: */ + "\204\0\0\0\0\3\0\0\0\1\0\0\0\2\0\0\0\1\205\0\0\0\0\3\0\0\0\1\0\0\0\2" + "\0\0\0\1\204\0\0\0\0\1\0\0\0\320\202\0\0\0\377\205\0\0\0\0\203\0\0\0" + "\377\204\0\0\0\0\2\0\0\0P\377\377\377\343\202\377\377\377\377\1\0\0\0" + "\377\203\0\0\0\0\1\0\0\0\377\202\377\377\377\377\2\357\356\356\377\0" + "\0\0\377\202\0\0\0\0\3\0\0\0\320\377\377\377\343\377\377\377\374\203" + "\377\377\377\377\3\0\0\0\377\0\0\0\0\0\0\0\377\202\377\377\377\377\6" + "\374\374\374\377\352\353\352\377\331\331\331\377\0\0\0\377\0\0\0\0\0" + "\0\0\377\206\377\377\377\377\1\0\0\0\377\203\377\377\377\377\6\371\371" + "\371\377\350\347\347\377\326\326\326\377\0\0\0\377\0\0\0\0\0\0\0\377" + "\202\377\377\377\377\1\0\0\0\377\203\377\377\377\377\1\0\0\0\377\203" + "\377\377\377\377\6\0\0\0\377\344\343\344\377\322\322\322\377\0\0\0\377" + "\0\0\0\0\0\0\0\377\204\377\377\377\377\14\0\0\0\377\377\377\377\377\0" + "\0\0\377\377\377\377\377\0\0\0\377\377\377\377\377\362\362\362\377\340" + "\340\341\377\321\322\321\377\0\0\0\377\0\0\0\0\0\0\0\377\206\377\377" + "\377\377\1\0\0\0\377\203\377\377\377\377\6\0\0\0\377\335\335\334\377" + "\322\322\321\377\0\0\0\377\0\0\0\0\0\0\0\377\202\377\377\377\377\1\0" + "\0\0\377\203\377\377\377\377\12\0\0\0\377\377\377\377\377\0\0\0\377\374" + "\375\374\377\353\353\353\377\331\331\331\377\321\321\321\377\0\0\0\377" + "\0\0\0\0\0\0\0\377\204\377\377\377\377\3\0\0\0\377\377\377\377\377\0" + "\0\0\377\202\377\377\377\377\7\371\371\371\377\0\0\0\377\325\326\326" + "\377\321\321\322\377\0\0\0\377\0\0\0\0\0\0\0\377\206\377\377\377\377" + "\12\0\0\0\377\377\377\377\377\0\0\0\377\365\365\365\377\343\343\344\377" + "\322\322\322\377\321\321\321\377\0\0\0\377\0\0\0\0\0\0\0\377\206\377" + "\377\377\377\1\0\0\0\377\202\377\377\377\377\10\362\362\361\377\340\340" + "\340\377\322\321\321\377\321\322\321\377\0\0\0\377\0\0\0\0\0\0\0\377" + "\377\377\377\377\203\0\0\0\377\202\377\377\377\377\1\0\0\0\377\202\377" + "\377\377\377\203\0\0\0\377\3\322\322\321\377\0\0\0\377\0\0\0\0\202\0" + "\0\0\377\203\276\"\246\377\10\0\0\0\377\377\377\377\377\0\0\0\377\377" + "\377\377\377\0\0\0\377\257\37\230\377\242\35\215\377\234\34\210\377\202" + "\0\0\0\377\7\0\0\0\0\0\0\0\377\276\"\246\377\0\0\0y\0\0\0-\0\0\0\34\276" + "\"\246\377\203\0\0\0\377\26\272!\242\377\0\0\0y\0\0\0-\0\0\0\34\234\34" + "\211\377\0\0\0\377\0\0\0\0\276\"\246\377\0\0\0^\0\0\0(\0\0\0\5\0\0\0" + "\1\0\0\0\5\276\"\246\377\0\0\0\377\276\"\246\377\0\0\0u\0\0\0)\0\0\0" + "\5\0\0\0\1\0\0\0\5\234\34\210\377"}; + + diff --git a/src/icons/stock_book.png b/src/icons/stock_book.png new file mode 100644 index 0000000..a30aefe Binary files /dev/null and b/src/icons/stock_book.png differ diff --git a/src/icons/stock_delete.h b/src/icons/stock_delete.h new file mode 100644 index 0000000..733a8b2 --- /dev/null +++ b/src/icons/stock_delete.h @@ -0,0 +1,105 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_delete) +#endif +#ifdef __GNUC__ +static const guint8 stock_delete[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_delete[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1970) */ + "\0\0\7\312" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\210\0\0\0\0\1\0\0\0R\206\0\0\0\377\1\0\0\0X\216\0\0\0\0\14\0\0\0\5\0" + "\0\0\220\34\34\34\377vvv\377\346\346\346\377\323\323\323\377\303\303" + "\303\377\220\220\220\377III\377\0\0\0\377\0\0\0\276\0\0\0,\211\0\0\0" + "\0\2\0\0\0E\0\0\0\240\202\0\0\0\377\12HG=\377\272\270\246\377\342\342" + "\337\377\361\361\360\377\352\351\346\377\362\362\356\377\353\352\345" + "\377\301\300\265\377WVJ\377RQF\377\202\0\0\0\377\2\0\0\0\227\0\0\0""3" + "\205\0\0\0\0\24\0\0\0T\0\0\0\377\250\246\217\377\275\273\241\377\300" + "\276\243\377\243\241\212\377ml\\\377||r\377LLI\377EE\77\377DC:\377oo" + "g\377\306\305\271\377\266\265\233\377ml]\377\263\261\233\377\247\246" + "\216\377\220\217z\377\0\0\0\377\0\0\0E\204\0\0\0\0\24\0\0\0\377\252\251" + "\223\377\314\313\265\377\320\316\272\377\325\323\301\377\256\255\243" + "\377\224\223\212\377EDB\377##!\377541\377)(#\377\36\36\32\377SRF\377" + "\255\254\223\377\222\221\211\377\302\301\257\377\261\257\226\377\254" + "\252\222\377\223\222}\377\0\0\0\377\204\0\0\0\0\24\10\10\10\377\321\320" + "\301\377\314\313\265\377\326\325\304\377\337\336\321\377\316\315\300" + "\377\254\254\246\377\220\217\212\377~}w\377\\\\U\377GF>\377edV\377wv" + "e\377\200~o\377\276\275\247\377\265\263\232\377\266\265\233\377\265\263" + "\232\377\241\237\210\377\0\0\0\377\204\0\0\0\0\24\0\0\0\377\331\330\314" + "\377\366\365\362\377\333\332\321\377\332\331\316\377\345\344\331\377" + "\320\317\303\377\323\323\311\377\312\311\274\377\267\266\246\377\270" + "\266\241\377\260\257\226\377\241\237\210\377\271\270\235\377\271\267" + "\235\377\270\266\234\377\304\302\251\377\257\255\227\377\213\211v\377" + "\0\0\0\377\204\0\0\0\0\24\0\0\0\377\252\251\230\377\266\266\262\377\371" + "\371\367\377\372\372\370\377\364\364\360\377\342\341\332\377\331\331" + "\316\377\333\332\317\377\325\323\301\377\323\321\276\377\304\302\251" + "\377\300\276\243\377\305\304\254\377\326\325\303\377\315\314\277\377" + "\273\272\255\377|zi\377qp`\377\0\0\0\336\204\0\0\0\0\24\23\23\23\316" + "\231\230\202\377\257\256\235\377\303\303\276\377\336\336\334\377\351" + "\351\350\377\373\373\372\377\375\375\374\377\375\375\375\377\374\373" + "\372\377\371\371\367\377\362\362\357\377\352\351\343\377\333\332\321" + "\377\300\277\261\377\225\224\204\377xwf\377nm]\377[[M\377\0\0\0\315\204" + "\0\0\0\0\24\0\0\0\222]\\O\377\223\222}\377\243\242\223\377\270\267\246" + "\377\330\327\320\377\333\333\324\377\323\322\312\377\310\310\303\377" + "\306\305\275\377\275\274\255\377\272\270\250\377\237\236\213\377\216" + "\214x\377\214\213w\377\216\214x\377zyh\377lk]\377NME\377\0\0\0y\204\0" + "\0\0\0\12\0\0\0""2\0\0\0\377BBB\377{{s\377\237\237\226\377\322\321\307" + "\377\334\333\322\377\314\313\276\377\321\320\302\377\304\303\266\377" + "\203\261\257\226\377\7\232\230\203\377\200\177m\377}|k\377ji^\377@@>" + "\377\0\0\0\377\0\0\0""3\205\0\0\0\0\23\0\0\0\37777-\377\17\17\17\377" + "874\377xxu\377\231\230\223\377\212\211~\377\263\262\244\377\252\252\236" + "\377\243\241\212\377\207\206s\377\217\215y\377baU\377TTQ\37711.\377\0" + "\0\0\377++#\377\0\0\0\377\0\0\0\17\205\0\0\0\0\11\0\0\0\377qq]\377WW" + "K\377BB:\377&&\40\377!!!\377\33\33\33\377\0\0\0\377$$$\377\204\0\0\0" + "\377\5\26\26\23\377**\"\37700'\377\77\77""4\377\0\0\0\377\206\0\0\0\0" + "\22\0\0\0\377zze\377[[Q\377\205\205v\377vvk\377\230\230\216\377uuk\377" + "QQF\377\177\177t\377jjX\377@@5\377bbQ\377TTE\377>>3\377UUF\37744+\377" + "QQC\377\0\0\0\377\206\0\0\0\0\22\0\0\0\377\205\205n\377]]T\377\221\221" + "\202\377\202\202x\377\252\252\243\377zzq\377NNE\377\212\212\200\377|" + "|m\377BB7\377``R\377ZZJ\377<<2\377NNA\37755,\377TTE\377\0\0\0\377\204" + "\0\0\0\0\24\0\0\0\2\0\0\0\3\0\0\0\377\205\205n\377[[Q\377\254\254\234" + "\377\205\205{\377\273\273\263\377\243\243\232\377TTK\377\223\223\212" + "\377\221\221\205\377BB7\377``R\377hhV\377AA6\377jjX\377CC7\377jjX\377" + "\0\0\0\377\203\0\0\0\0\177\0\0\0\1\0\0\0\5\0\0\0\10\0\0\0\377\205\205" + "n\377[[Q\377\244\244\223\377\205\205{\377\301\301\266\377\261\261\242" + "\377kk_\377\261\261\242\377\235\235\213\377TTE\377\204\204p\377qq^\377" + "CC7\377jjX\377CC7\377jjX\377\0\0\0\377\0\0\0\6\0\0\0\3\0\0\0\0\0\0\0" + "\2\0\0\0\12\0\0\0\24\0\0\0\377\205\205n\377[[Q\377\245\245\224\377\205" + "\205{\377\302\302\267\377\261\261\242\377kk_\377\261\261\242\377\235" + "\235\213\377TTE\377\204\204p\377qq^\377CC7\377jjX\377CC7\377jjX\377\0" + "\0\0\377\0\0\0\26\0\0\0\13\0\0\0\2\0\0\0\3\0\0\0\26\0\0\0+\0\0\0\377" + "\205\205n\377[[Q\377\235\235\213\377\205\205{\377\301\301\266\377\261" + "\261\242\377kk_\377\261\261\242\377\235\235\213\377TTE\377\204\204p\377" + "qq^\377CC7\377jjX\377CC7\377jjX\377\0\0\0\377\0\0\0""1\0\0\0\32\0\0\0" + "\4\0\0\0\7\0\0\0#\0\0\0I\0\0\0\377{{e\377[[Q\377\244\244\223\377\205" + "\205{\377\301\301\266\377\261\261\242\377kk_\377\261\261\242\377\235" + "\235\213\377TTE\377\204\204p\377qq^\377CC7\377jjX\377CC7\377__O\377\0" + "\0\0\377\0\0\0H\0\0\0*\0\0\0\7\0\0\0\12\0\0\0,\0\0\0X\0\0\0\377ffT\377" + "[[Q\377\244\244\223\377\202\202x\377\301\301\266\377\261\261\242\377" + "kk_\377\261\261\242\377\235\235\213\377TTE\377\204\204p\377qq^\377CC" + "7\377jjX\377II<\377WWH\377\0\0\0\377\0\0\0P\0\0\0*\0\0\0\7\0\0\0\6\0" + "\0\0\35\0\0\0C\0\0\0r\0\0\0\377WWJ\377}}m\377\27vvi\377\263\263\250\377" + "\241\241\224\377mma\377\263\263\245\377\235\235\213\377TTE\377\204\204" + "p\377iiW\377AA5\377VVG\377DD8\377\0\0\0\377\0\0\0\177\0\0\0C\0\0\0\35" + "\0\0\0\4\0\0\0\1\0\0\0\13\0\0\0\34\0\0\0""2\0\0\0g\0\0\0\320\202\0\0" + "\0\377\6YYQ\377XXI\377NN@\377ttg\377aaS\377NNA\377\202II<\377\202\0\0" + "\0\377\5\0\0\0\304\0\0\0t\0\0\0>\0\0\0%\0\0\0\11\202\0\0\0\0\10\0\0\0" + "\1\0\0\0\6\0\0\0\17\0\0\0\31\0\0\0\37\0\0\0r\0\0\0\264\0\0\0\302\206" + "\0\0\0\377\11\0\0\0\271\0\0\0\217\0\0\0S\0\0\0,\0\0\0*\0\0\0\33\0\0\0" + "\10\0\0\0\1\0\0\0\0"}; + + diff --git a/src/icons/stock_delete.png b/src/icons/stock_delete.png new file mode 100644 index 0000000..6b638c5 Binary files /dev/null and b/src/icons/stock_delete.png differ diff --git a/src/icons/stock_delete_16.h b/src/icons/stock_delete_16.h new file mode 100644 index 0000000..7bb7de2 --- /dev/null +++ b/src/icons/stock_delete_16.h @@ -0,0 +1,61 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_delete_16) +#endif +#ifdef __GNUC__ +static const guint8 stock_delete_16[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_delete_16[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (877) */ + "\0\0\3\205" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (64) */ + "\0\0\0@" + /* width (16) */ + "\0\0\0\20" + /* height (16) */ + "\0\0\0\20" + /* pixel_data: */ + "\203\0\0\0\0\12\6\6\6\21\6\6\6K\16\16\16\377BBB\377JJJ\377>>>\377,,," + "\377\0\0\0\377\6\6\6>\6\6\6!\205\0\0\0\0\1\6\6\6:\202\0\0\0\377\6\205" + "\203q\377\360\355\330\377\362\361\351\377\356\355\344\377\307\306\272" + "\377SRF\377\202\0\0\0\377\1\6\6\6K\203\0\0\0\0\16\6\6\6>\20\20\20\377" + "\302\300\245\377\301\277\245\377zyg\377HG@\377''%\37765.\377\207\205" + "r\377\240\236\210\377\243\241\212\377\245\243\214\377\0\0\0\377\6\6\6" + "!\202\0\0\0\0\16+++\377\323\321\274\377\334\332\304\377\342\340\317\377" + "\271\267\242\377db[\377986\377<<3\377a`S\377\232\230\203\377\267\265" + "\233\377\263\262\230\377\244\242\213\377\0\0\0\377\202\0\0\0\0\16\0\0" + "\0\377\350\344\323\377\364\361\351\377\362\361\344\377\352\351\332\377" + "\345\342\322\377\325\322\276\377\303\301\247\377\257\255\224\377\300" + "\276\244\377\306\304\254\377\270\266\234\377ywf\377\0\0\0\377\202\0\0" + "\0\0\16\0\0\0\377\212\211u\377\361\355\322\377\373\370\351\377\371\365" + "\355\377\370\367\356\377\370\367\354\377\365\363\346\377\354\352\331" + "\377\336\335\313\377\235\233\211\377cbT\377XWJ\377\0\0\0\377\202\0\0" + "\0\0\16\0\0\0\202RQF\377sqe\377\237\235\221\377\344\341\310\377\351\345" + "\317\377\342\340\313\377\321\321\313\377\216\215\177\377vud\377hgX\377" + "CB8\377)(\"\377\0\0\0\202\203\0\0\0\0\14\37\37\33\377440\377KID\377h" + "gb\377pof\377kkd\377nl]\377XWK\377991\377\27\26\25\377\32\32\26\377\37" + "\36\31\377\204\0\0\0\0\14**#\377\240\240\225\377MM@\377660\377**&\377" + "//*\377\17\17\15\377\13\13\11\377\20\20\15\377>>8\37744+\377$$\37\377" + "\204\0\0\0\0\14++$\377\245\244\231\377\267\266\256\377qpg\377\241\241" + "\222\377tsc\377@@5\377ii[\37799/\377ii_\377\77\77""4\377&&\40\377\204" + "\0\0\0\0\14,,%\377\255\255\243\377\331\331\322\377\213\213\177\377\326" + "\325\316\377\210\210{\377XWO\377\223\223\207\377JJ=\377xxk\377PPB\377" + "))\"\377\204\0\0\0\0\14,,%\377\255\255\243\377\332\332\323\377\217\216" + "\201\377\335\335\324\377\254\253\231\377]]R\377\240\240\223\377MM@\377" + "yyl\377PPB\377))\"\377\204\0\0\0\0\14++$\377~~u\377\330\330\321\377\215" + "\215\200\377\335\335\323\377\254\253\231\377]]R\377\240\240\223\377M" + "M@\377xxk\377DD8\377((\"\377\204\0\0\0\0\14''!\377CC=\377\216\216\206" + "\377^^U\377\332\332\320\377\254\253\231\377]]R\377\224\224\204\377MM" + "@\377]]S\377((\"\377##\35\377\204\0\0\0\0\1\6\6\6K\202\6\6\6\377\11B" + "A:\377\217\217{\377zzl\377DD<\377ll^\37755,\377..)\377\0\0\0\377\6\6" + "\6>\206\0\0\0\0\5\6\6\6W\6\6\6\377\0\0\0\377\17\17\17\377\5\5\5\377\202" + "\0\0\0\377\1\6\6\6K\204\0\0\0\0"}; + + diff --git a/src/icons/stock_delete_16.png b/src/icons/stock_delete_16.png new file mode 100644 index 0000000..af10be0 Binary files /dev/null and b/src/icons/stock_delete_16.png differ diff --git a/src/icons/stock_hand-signed.h b/src/icons/stock_hand-signed.h new file mode 100644 index 0000000..d0bb5e0 --- /dev/null +++ b/src/icons/stock_hand-signed.h @@ -0,0 +1,82 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_hand_signed) +#endif +#ifdef __GNUC__ +static const guint8 stock_hand_signed[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_hand_signed[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1135) */ + "\0\0\4\207" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\3\0\0\0\0\0\0\0\377\376\376\376\377\225\361\361\361\377\4\0\0\0\0\0" + "\0\0\377\376\376\376\377\361\361\361\377\204\232\232\232\377\1\361\361" + "\361\377\205\232\232\232\377\202\361\361\361\377\210\232\232\232\377" + "\3\0\0\0\0\0\0\0\377\376\376\376\377\225\361\361\361\377\4\0\0\0\0\0" + "\0\0\377\376\376\376\377\361\361\361\377\207\232\232\232\377\1\361\361" + "\361\377\204\232\232\232\377\1\361\361\361\377\207\232\232\232\377\3" + "\0\0\0\0\0\0\0\377\376\376\376\377\225\361\361\361\377\4\0\0\0\0\0\0" + "\0\377\376\376\376\377\361\361\361\377\203\232\232\232\377\1\361\361" + "\361\377\202\232\232\232\377\1\361\361\361\377\207\232\232\232\377\206" + "\361\361\361\377\3\0\0\0\0\0\0\0\377\376\376\376\377\225\361\361\361" + "\377\3\0\0\0\0\0\0\0\377\376\376\376\377\225\361\361\361\377\3\0\0\0" + "\0\0\0\0\377\376\376\376\377\225\361\361\361\377\4\0\0\0\0\0\0\0\377" + "\376\376\376\377\361\361\361\377\204\357\357\357\377\1\361\361\361\377" + "\202\357\357\357\377\1\361\361\361\377\204\357\357\357\377\1\361\361" + "\361\377\202\357\357\357\377\1\361\361\361\377\204\357\357\357\377\3" + "\0\0\0\0\0\0\0\377\376\376\376\377\204\361\361\361\377\4\250\257\305" + "\377p|\244\377O^\220\377\337\341\346\377\207\361\361\361\377\2\224\235" + "\271\377\341\343\347\377\204\361\361\361\377\4\0\0\0\0\0\0\0\377\376" + "\376\376\377\361\361\361\377\202\357\357\357\377\3v\201\247\377\253\262" + "\306\377\361\361\361\377\202\357\357\357\377\1\361\361\361\377\203\357" + "\357\357\377\5bo\233\377\351\352\354\377\357\357\357\377\206\220\260" + "\377\361\361\361\377\204\357\357\357\377\3\0\0\0\0\0\0\0\377\376\376" + "\376\377\202\357\357\357\377\3\340\341\346\377Ra\221\377\356\356\356" + "\377\206\357\357\357\377\5\356\356\356\377Xg\225\377\357\357\357\377" + "\340\341\346\377dq\234\377\203\357\357\357\377\202\356\356\356\377\4" + "\0\0\0\0\0\0\0\377\375\375\375\377\356\356\356\377\202\353\353\353\377" + "\3\225\235\270\377\334\335\342\377\356\356\356\377\202\353\353\353\377" + "\11\356\356\356\377\353\353\353\377\351\351\351\377\353\353\353\377M" + "\\\216\377\356\356\356\377^k\230\377\211\222\261\377\356\356\356\377" + "\204\353\353\353\377\24\0\0\0\0\0\0\0\377\375\375\375\377\355\355\355" + "\377\235\245\275\377\246\255\303\377\354\354\354\377\347\347\350\377" + "5G\200\377\\j\227\377\244\253\302\377\337\341\345\377\333\334\342\377" + "7I\201\377\272\277\317\377#7v\377\236\246\276\377\15#i\377u\201\246\377" + "\2\31b\377\203\354\354\354\377\5\353\353\353\377\0\0\0\0\0\0\0\377\375" + "\375\375\377\353\353\353\377\202fs\234\377\16\335\336\341\377\203\215" + "\255\377w\203\247\377*=y\377\36""2s\3773E\177\377Zh\225\377\271\276\315" + "\377\4\33c\377\201\213\254\377\353\353\353\377\2\31b\377~\210\252\377" + "\0\27a\377\204\350\350\350\377\20\0\0\0\0\0\0\0\377\375\375\375\377\352" + "\352\352\377\331\333\340\377\13!g\377\2\31b\377\235\244\274\377\275\301" + "\317\377\25*m\377\34""0q\377\23(l\377^l\230\377\253\261\305\377\0\27" + "a\377\306\311\324\377\202\352\352\352\377\3\307\312\325\377\0\27a\377" + "\352\352\352\377\202\351\351\351\377\4\350\350\350\377\0\0\0\0\0\0\0" + "\377\375\375\375\377\205\350\350\350\377\2\344\344\345\377\277\303\320" + "\377\204\350\350\350\377\1\177\211\253\377\211\350\350\350\377\3\0\0" + "\0\0\0\0\0\377\375\375\375\377\202\347\347\347\377\203\346\346\346\377" + "\202\347\347\347\377\1\346\346\346\377\202\347\347\347\377\203\346\346" + "\346\377\202\347\347\347\377\1\346\346\346\377\202\347\347\347\377\203" + "\346\346\346\377\4\0\0\0\0\0\0\0\377\375\375\375\377\346\346\346\377" + "\203\345\345\345\377\2\344\344\344\377\346\346\346\377\202\345\345\345" + "\377\1\346\346\346\377\203\345\345\345\377\2\344\344\344\377\346\346" + "\346\377\202\345\345\345\377\1\346\346\346\377\203\345\345\345\377\4" + "\344\344\344\377\0\0\0\0\0\0\0\377\312\312\312\377\203\275\275\275\377" + "\202\274\274\274\377\206\275\275\275\377\202\274\274\274\377\206\275" + "\275\275\377\202\274\274\274\377\2\0\0\0\0\0\0\0c\226\0\0\0\377\5\0\0" + "\0\0\0\0\0\12\0\0\0\37\0\0\0""1\0\0\0""8\223\0\0\0""9\4\0\0\0\0\0\0\0" + "\3\0\0\0\12\0\0\0\22\202\0\0\0\25\222\0\0\0\26"}; + + diff --git a/src/icons/stock_hand-signed.png b/src/icons/stock_hand-signed.png new file mode 100644 index 0000000..02774f2 Binary files /dev/null and b/src/icons/stock_hand-signed.png differ diff --git a/src/icons/stock_inbox.h b/src/icons/stock_inbox.h new file mode 100644 index 0000000..eda8d69 --- /dev/null +++ b/src/icons/stock_inbox.h @@ -0,0 +1,57 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_inbox) +#endif +#ifdef __GNUC__ +static const guint8 stock_inbox[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_inbox[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (703) */ + "\0\0\2\327" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (64) */ + "\0\0\0@" + /* width (16) */ + "\0\0\0\20" + /* height (16) */ + "\0\0\0\20" + /* pixel_data: */ + "\206\0\0\0\0\1\0\0\0.\202\0\0\0\377\1\0\0\0\23\212\0\0\0\0\7\0\0\0.\0" + "\0\0\377\245\236\222\377\335\333\326\377\366\366\365\377\0\0\0\377\0" + "\0\0\23\207\0\0\0\0\12\0\0\0.\0\0\0\377\252\244\231\377\343\341\336\377" + "\204|m\377+)$\377\256\250\235\377\364\363\362\377\0\0\0\377\0\0\0\23" + "\204\0\0\0\0\5\0\0\0.960\377\267\262\250\377\343\341\336\377\316\312" + "\303\377\202\15\14\12\377\6)'\"\377qk^\377\331\327\322\377\364\363\362" + "\377\0\0\0\377\0\0\0\23\203\0\0\0\0\16\0\0\0\377\372\372\371\377\310" + "\304\275\377\0\0\0\377\12\12\11\377\30\26\23\377FA9\377xqc\377\204|m" + "\377\220\210y\377\341\336\332\377\364\363\362\377\0\0\0\377\0\0\0\23" + "\202\0\0\0\0\21\0\0\0\377\377\377\377\377\345\343\340\377FA9\377HC;\377" + "c]R\377xqc\377\207\177p\377\224\214}\377\234\225\207\377\236\230\213" + "\377\341\336\332\377\364\363\362\377\0\0\0\377\0\0\0\23\0\0\0\0\0\0\0" + "\377\202\377\377\377\377\15\345\343\340\377(&!\377c]R\377zse\377\217" + "\207x\377\235\226\210\377\240\232\215\377\241\233\216\377\244\235\221" + "\377\331\327\322\377\366\365\364\377\0\0\0\377\0\0\0\23\211\0\0\0\377" + "\6\212\204z\377\245\236\222\377\250\241\225\377\252\244\231\377\341\336" + "\332\377\362\361\357\377\202\0\0\0\377\207\377\377\377\377\13\0\0\0\377" + "qlc\377\210\203{\377\257\251\236\377\261\254\241\377\304\300\271\377" + "\331\327\322\377hhh\377\0\0\0\377\377\377\377\377\357\357\357\377\204" + "Ki\203\377\10\314\314\314\377\0\0\0\377hd\\\377\212\205|\377\272\266" + "\254\377\323\321\314\377\210\205\201\377WRH\377\202\0\0\0\377\1\377\377" + "\377\377\202\357\357\357\377\203Ki\203\377\6\314\314\314\377\0\0\0\377" + "d_V\377\270\265\256\377\223\220\213\377XUN\377\202\0\0\0\377\4\0\0\0" + "4\0\0\0\377\377\377\377\377\357\357\357\377\204Ki\203\377\4\314\314\314" + "\377\0\0\0\377urk\377^ZT\377\202\0\0\0\377\1\0\0\0C\202\0\0\0\0\2\0\0" + "\0\377\377\377\377\377\203Ki\203\377\3\357\357\357\377Ki\203\377\314" + "\314\314\377\203\0\0\0\377\1\0\0\0C\204\0\0\0\0\4\0\0\0\377\377\377\377" + "\377\357\357\357\377Ki\203\377\203\357\357\357\377\3\314\314\314\377" + "\0\0\0\377\0\0\0N\206\0\0\0\0\2\0\0\0\377\377\377\377\377\206\314\314" + "\314\377\1\30\30\30\377\207\0\0\0\0\202\0\0\0\377\1\11\11\11\377\206" + "\0\0\0\377\207\0\0\0\0"}; + + diff --git a/src/icons/stock_inbox.png b/src/icons/stock_inbox.png new file mode 100644 index 0000000..3afaeb6 Binary files /dev/null and b/src/icons/stock_inbox.png differ diff --git a/src/icons/stock_insert-file.h b/src/icons/stock_insert-file.h new file mode 100644 index 0000000..d1db71a --- /dev/null +++ b/src/icons/stock_insert-file.h @@ -0,0 +1,92 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_insert_file) +#endif +#ifdef __GNUC__ +static const guint8 stock_insert_file[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_insert_file[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1419) */ + "\0\0\5\243" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\206\0\0\0\0\1\0\0\0j\220\0\0\0\377\1\0\0\0U\204\0\0\0\0\10\0\0\0\203" + "\0\0\0\377[[[\377\363\357\343\377\360\353\327\377\371\363\334\377\367" + "\355\312\377\243\222V\377\202\0\0\0\377\1\251tk\377\203\321\217\204\377" + "\3\321\220\204\377\321\217\204\377\323\221\206\377\202\277\325\350\377" + "\1\0\0\0\377\203\0\0\0\0\14\0\0\0\203333\377\365\361\345\377\366\361" + "\340\377\363\344\255\377\337\267J\377\331\247.\377\321\224\14\377\324" + "\261P\377\342\313y\377\215~J\377'\26\24\377\202\307tg\377\7\307tf\377" + "\307tg\377\310vh\377\315\206z\377\215\251\276\377Ti|\377\0\0\0\377\202" + "\0\0\0\0\10\0\0\0I\0\0\0\377\362\350\320\377\367\355\312\377\356\326" + "\200\377\325\235\34\377\321\224\14\377\270\202\12\377\203\0\0\0\377\2" + "\311\264k\377***\377\205\376\376\376\377\4\371\371\371\377\204\240\265" + "\377Odu\377\0\0\0\377\202\0\0\0\0\15\0\0\0\232\272\263\242\377\372\365" + "\343\377\356\326\200\377\340\267L\377\321\224\14\377\230k\7\377\0\0\0" + "\377Rbo\377\327\327\327\377\226\226\226\377\0\0\0\377\256\256\256\377" + "\204\327\327\327\377\5\326\326\326\377\361\361\361\377\201\232\256\377" + "I`r\377\0\0\0\377\202\0\0\0\0\2\0\0\0\377\362\351\316\377\202\356\326" + "\200\377\202\321\224\14\377\4\0\0\0\377x\202\212\377\215\251\276\377" + "\327\327\327\377\204\376\376\376\377\2\374\374\374\377\364\364\364\377" + "\202\353\353\353\377\4\361\361\361\377\201\232\256\377I`r\377\0\0\0\377" + "\202\0\0\0\0\2\0\0\0\377\370\361\326\377\202\356\326\200\377\202\321" + "\224\14\377\3\0\0\0\377\317\340\355\377\215\251\276\377\204\327\327\327" + "\377\1\324\324\324\377\204\305\305\305\377\3\361\361\361\377\201\232" + "\256\377I`r\377\204\0\0\0\377\1\364\344\255\377\202\356\326\200\377\202" + "\321\224\14\377\204\0\0\0\377\202\376\376\376\377\1\362\362\362\377\205" + "\353\353\353\377\3\356\356\356\377\201\232\256\377I`r\377\202\0\0\0\377" + "\1\367\363\337\377\204\356\326\200\377\203\321\224\14\377\1\274\237_" + "\377\203\0\0\0\377\1\213\221\231\377\206\277\313\326\377\7\241\266\304" + "\377\201\232\256\377I`r\377\0\0\0\377\0\0\0\0\0\0\0\377\371\366\352\377" + "\203\356\326\200\377\202\321\224\14\377\7\276\245o\377\0\0\0\377\375" + "\375\375\377\340\340\340\377\301\301\301\377\0\0\0\377Wgu\377\203\201" + "\232\256\377\2~\231\255\377}\227\254\377\202\201\232\256\377\2I`r\377" + "\0\0\0\377\202\0\0\0\0\7\0\0\0\377\371\365\344\377\361\335\227\377\356" + "\326\200\377\321\224\14\377\274\243k\377\0\0\0\377\202\361\361\361\377" + "\15\303\303\303\377\373\373\373\377\250\250\250\377\0\0\0\377\220\220" + "\220\377\353\353\353\377\327\327\327\377\234\235\235\377/FV\377\200\206" + "\214\377\201\232\256\377I`r\377\0\0\0\377\203\0\0\0\0\5""000\377\371" + "\365\346\377\361\335\227\377\303\251p\377\0\0\0\377\203\361\361\361\377" + "\15\255\255\255\377vvv\377]]]\377@@@\377\0\0\0\377\326\326\326\377\305" + "\305\305\377\271\271\271\377\23-<\377Xm\200\377\227\245\260\377I`r\377" + "\0\0\0\377\203\0\0\0\0\202\0\0\0\377\2\362\357\344\377\0\0\0\377\203" + "\361\361\361\377\6\360\360\360\377\342\342\342\377\205\205\205\377KK" + "I\377\26\26\26\377\0\0\0\377\202\305\305\305\377\6\277\277\277\377\21" + "(5\377Zp\202\377\235\251\260\377I`r\377\0\0\0\377\203\0\0\0\0\3\0\0\0" + "\377\376\376\376\377\0\0\0\377\206\357\357\357\377\202\356\356\356\377" + "\12\305\305\305\377\0\0\0\377\305\305\305\377\302\302\302\377\262\262" + "\262\377\21,:\377Zp\202\377\237\251\260\377I`r\377\0\0\0\377\203\0\0" + "\0\0\2\0\0\0\377\376\376\376\377\203\357\357\357\377\205\356\356\356" + "\377\13\354\354\354\377\304\304\304\377\0\0\0\377\206\206\206\377nnn" + "\377zzz\377-9I\377>O\\\377\200\207\217\377\32""1@\377\0\0\0\377\203\0" + "\0\0\0\2\0\0\0\377\375\375\375\377\210\356\356\356\377\2\353\353\353" + "\377\303\303\303\377\204\0\0\0\377\2\0\0\0\340\0\0\0\341\202\0\0\0\377" + "\1\0\0\0G\203\0\0\0\0\3\0\0\0\377\375\375\375\377\355\355\355\377\203" + "\354\354\354\377\202\353\353\353\377\203\352\352\352\377\2\301\301\301" + "\377\0\0\0\377\213\0\0\0\0\3\0\0\0\377\375\375\375\377\353\353\353\377" + "\210\350\350\350\377\2\300\300\300\377\0\0\0\377\213\0\0\0\0\2\0\0\0" + "\377\375\375\375\377\202\352\352\352\377\202\351\351\351\377\203\350" + "\350\350\377\202\347\347\347\377\2\277\277\277\377\0\0\0\377\213\0\0" + "\0\0\3\0\0\0\377\375\375\375\377\350\350\350\377\207\346\346\346\377" + "\3\345\345\345\377\276\276\276\377\0\0\0\377\213\0\0\0\0\2\0\0\0\377" + "\375\375\375\377\202\347\347\347\377\203\346\346\346\377\202\345\345" + "\345\377\202\344\344\344\377\2\275\275\275\377\0\0\0\377\213\0\0\0\0" + "\3\0\0\0\377\375\375\375\377\346\346\346\377\203\345\345\345\377\202" + "\344\344\344\377\203\343\343\343\377\2\273\273\273\377\0\0\0\377\213" + "\0\0\0\0\2\0\0\0\377\302\302\302\377\203\275\275\275\377\202\274\274" + "\274\377\204\273\273\273\377\2\240\240\240\377\0\0\0\324\213\0\0\0\0" + "\1\0\0\0k\213\0\0\0\377\1\0\0\0d\210\0\0\0\0"}; + + diff --git a/src/icons/stock_insert-file.png b/src/icons/stock_insert-file.png new file mode 100644 index 0000000..34aed99 Binary files /dev/null and b/src/icons/stock_insert-file.png differ diff --git a/src/icons/stock_mail-compose.h b/src/icons/stock_mail-compose.h new file mode 100644 index 0000000..325c182 --- /dev/null +++ b/src/icons/stock_mail-compose.h @@ -0,0 +1,118 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_mail_compose) +#endif +#ifdef __GNUC__ +static const guint8 stock_mail_compose[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_mail_compose[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1816) */ + "\0\0\7""0" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\216\0\0\0\0\203\0\0\0\377\211\0\0\0\0\214\0\0\0\377\3\321\257a\377\361" + "\343\254\377\373\251\17\377\203\0\0\0\377\1\0\0\0V\205\0\0\0\0\1\0\0" + "\0\377\211\377\377\377\377\12\265\265\265\377\240k\11\377\361\343\254" + "\377\373\251\17\377\201`\0\377\0\0\0\377\340\340\340\377\301\301\301" + "\377\0\0\0\377\0\0\0D\204\0\0\0\0\3\0\0\0\377\377\377\377\377\364\364" + "\364\377\205\377\377\377\377\202\375\375\375\377\13\0\0\0\377\346\303" + "p\377\361\323\207\377\325\217\14\377\0\0\0\377\262\262\262\377\303\303" + "\303\377\373\373\373\377\250\250\250\377\0\0\0\377\0\0\0V\203\0\0\0\0" + "\3\0\0\0\377\377\377\377\377\366\366\366\377\203\377\377\377\377\203" + "\375\375\375\377\14\263\263\263\377\240k\11\377\361\343\254\377\373\251" + "\17\377\201`\0\377\0\0\0\377\372\372\372\377\255\255\255\377vvv\377]" + "]]\377@@@\377\0\0\0\377\203\0\0\0\0\4\0\0\0\377\377\377\377\377\366\366" + "\366\377\377\377\377\377\204\373\373\373\377\15\370\370\370\377\0\0\0" + "\377\346\303p\377\361\320~\377\325\217\14\377\0\0\0\377\254\254\254\377" + "\370\370\366\377\342\342\342\377\205\205\205\377KKI\377\26\26\26\377" + "\0\0\0\377\203\0\0\0\0\3\0\0\0\377\377\377\377\377\364\364\364\377\202" + "\375\375\375\377\202\373\373\373\377\16\373\373\372\377}}}\377\240k\11" + "\377\361\343\254\377\373\251\17\377\201`\0\377\0\0\0\377\364\364\364" + "\377\362\362\362\377\360\360\360\377\357\357\357\377\325\325\325\377" + "\255\255\255\377\0\0\0\377\203\0\0\0\0\4\0\0\0\377\377\377\377\377\362" + "\362\362\377\371\371\371\377\202\366\366\366\377\17\366\366\363\377\363" + "\363\363\377\0\0\0\377\346\303p\377\361\320~\377\325\217\14\377\0\0\0" + "\377\207\207\206\377\345\345\345\377\342\342\342\377\337\337\337\377" + "\353\353\353\377\342\342\342\377\301\301\301\377\0\0\0\377\203\0\0\0" + "\0\3\0\0\0\377\377\377\377\377\360\360\360\377\203\370\370\370\377\12" + "\370\370\366\377\256\256\256\377{S\7\377\361\343\254\377\373\251\17\377" + "\201`\0\377\0\0\0\377\355\355\355\377\353\353\353\377\353\353\351\377" + "\202\351\351\347\377\3\340\340\336\377\277\277\277\377\0\0\0\377\203" + "\0\0\0\0\25\0\0\0\377\377\377\377\377\357\357\357\377\363\363\363\377" + "\360\360\360\377\360\360\355\377\355\355\355\377\0\0\0\377\346\303p\377" + "\361\322\204\377\312\207\13\377\0\0\0\377\236\236\234\377\334\334\331" + "\377\331\331\331\377\331\331\326\377\326\326\324\377\345\345\344\377" + "\331\331\327\377\272\272\272\377\0\0\0\377\203\0\0\0\0\3\0\0\0\377\377" + "\377\377\377\355\355\355\377\202\364\364\364\377\10\362\362\362\377\252" + "\252\252\377\240k\11\377\361\343\254\377\373\251\17\377\201`\0\377\0" + "\0\0\377\347\347\345\377\202\345\345\344\377\6\344\344\342\377\340\340" + "\336\377\342\342\340\377\331\331\327\377\272\272\272\377\0\0\0\377\203" + "\0\0\0\0\25\0\0\0\377\377\377\377\377\353\353\353\377\356\356\354\377" + "\347\347\345\377\345\345\345\377\0\0\0\377\346\303p\377\361\322\204\377" + "\312\207\13\377\0\0\0\377\227\227\226\377\324\324\321\377\342\342\340" + "\377\340\340\336\377\336\336\334\377\336\336\333\377\336\336\334\377" + "\331\331\327\377\275\275\274\377\0\0\0\377\203\0\0\0\0\7\0\0\0\377\377" + "\377\377\377\347\347\347\377\357\357\355\377\353\353\353\377\246\246" + "\245\377\272|\12\377\202\373\251\17\377\7z[\0\377\0\0\0\377\342\342\340" + "\377\340\340\336\377\336\336\334\377\336\336\333\377\334\334\331\377" + "\202\333\333\331\377\3\325\325\323\377\274\274\274\377\0\0\0\377\203" + "\0\0\0\0\25\0\0\0\377\377\377\377\377\345\345\345\377\343\343\343\377" + "\331\331\331\377\0\0\0\377\362\346\266\377{{{\377pK\5\377\0\0\0\377g" + "gd\377\313\313\307\377\311\311\307\377\334\334\331\377\333\333\327\377" + "\331\331\327\377\327\327\325\377\331\331\327\377\327\327\323\377\264" + "\264\264\377\0\0\0\377\203\0\0\0\0\25\0\0\0\377\377\377\377\377\342\342" + "\340\377\345\345\344\377\344\344\344\377\0\0\0\377\333\325\301\377~o" + "W\377uX\0\377\0\0\0\377\334\334\331\377\333\333\331\377\333\333\327\377" + "\331\331\325\377\327\327\323\377\325\325\323\377\325\325\321\377\327" + "\327\323\377\320\320\316\377\272\272\270\377\0\0\0\377\203\0\0\0\0\7" + "\0\0\0\377\377\377\377\377\336\336\334\377\335\335\333\377\321\321\317" + "\377\16\16\16\377SSS\377\202\0\0\0\377\4\215\215\213\377\304\304\301" + "\377\304\304\277\377\301\301\274\377\202\325\325\321\377\6\323\323\320" + "\377\321\321\320\377\323\323\320\377\320\320\316\377\272\272\270\377" + "\0\0\0\377\203\0\0\0\0\5\0\0\0\377\377\377\377\377\334\334\333\377\340" + "\340\334\377\335\335\333\377\202\0\0\0\377\5\211\211\207\377\311\311" + "\306\377\315\315\312\377\320\320\315\377\316\316\313\377\204\320\320" + "\315\377\5\317\317\314\377\320\320\315\377\320\320\314\377\266\266\266" + "\377\0\0\0\377\203\0\0\0\0\25\0\0\0\377\377\377\377\377\333\333\331\377" + "\324\324\320\377\303\303\300\377[[[\377\221\221\215\377\221\221\217\377" + "\237\237\235\377\257\257\254\377\271\271\266\377\276\276\273\377\303" + "\303\300\377\305\305\302\377\310\310\305\377\312\312\307\377\313\313" + "\310\377\314\314\311\377\314\314\310\377\262\262\262\377\0\0\0\377\203" + "\0\0\0\0\25\0\0\0\377\377\377\377\377\322\322\320\377\322\322\317\377" + "\317\317\314\377\277\277\275\377\237\237\234\377\210\210\206\377\207" + "\207\205\377\214\214\212\377\225\225\223\377\234\234\232\377\250\250" + "\245\377\261\261\256\377\265\265\263\377\273\273\270\377\276\276\273" + "\377\277\277\274\377\302\302\276\377\262\262\262\377\0\0\0\377\203\0" + "\0\0\0\11\0\0\0\377\377\377\377\377\321\321\316\377\320\320\314\377\314" + "\314\311\377\317\317\314\377\303\303\300\377\256\256\253\377\235\235" + "\232\377\202\227\227\224\377\12\224\224\221\377\232\232\227\377\237\237" + "\234\377\243\243\240\377\250\250\245\377\252\252\247\377\260\260\254" + "\377\270\270\265\377\260\260\256\377\0\0\0\377\203\0\0\0\0\3\0\0\0\377" + "\377\377\377\377\322\322\317\377\203\321\321\316\377\6\317\317\315\377" + "\313\313\310\377\276\276\274\377\264\264\260\377\254\254\250\377\244" + "\244\242\377\202\240\240\234\377\7\244\244\240\377\244\244\242\377\250" + "\250\246\377\253\253\247\377\262\262\256\377\244\244\244\377\0\0\0\377" + "\203\0\0\0\0\6\0\0\0\377\377\377\377\377\321\321\320\377\321\321\316" + "\377\320\320\316\377\316\316\314\377\202\316\316\312\377\15\314\314\310" + "\377\306\306\302\377\274\274\271\377\263\263\257\377\264\264\260\377" + "\253\253\251\377\246\246\243\377\240\240\235\377\246\246\243\377\252" + "\252\247\377\254\254\251\377\241\241\241\377\0\0\0\377\203\0\0\0\0\4" + "\0\0\0\377\377\377\377\377\261\261\257\377\264\264\263\377\203\264\264" + "\261\377\202\263\263\261\377\4\262\262\256\377\256\256\252\377\247\247" + "\244\377\242\242\240\377\202\245\245\241\377\6\231\231\227\377\222\222" + "\220\377\224\224\220\377\235\235\233\377\205\205\203\377\0\0\0\377\203" + "\0\0\0\0\224\0\0\0\377\2\0\0\0r\0\0\0\0"}; + + diff --git a/src/icons/stock_mail-compose.png b/src/icons/stock_mail-compose.png new file mode 100644 index 0000000..b0d4e13 Binary files /dev/null and b/src/icons/stock_mail-compose.png differ diff --git a/src/icons/stock_mail-compose_16.h b/src/icons/stock_mail-compose_16.h new file mode 100644 index 0000000..5aef80b --- /dev/null +++ b/src/icons/stock_mail-compose_16.h @@ -0,0 +1,58 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_mail_compose_16) +#endif +#ifdef __GNUC__ +static const guint8 stock_mail_compose_16[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_mail_compose_16[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (710) */ + "\0\0\2\336" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (64) */ + "\0\0\0@" + /* width (16) */ + "\0\0\0\20" + /* height (16) */ + "\0\0\0\20" + /* pixel_data: */ + "\2\0\0\0\0\0\0\0\"\211\0\0\0\377\1\0\0\0\32\205\0\0\0\0\1\0\0\0\377\206" + "\376\376\376\377\5\375\375\375\377\340\340\340\377\301\301\301\377\0" + "\0\0\377\0\0\0\"\204\0\0\0\0\2\0\0\0\377\376\376\376\377\206\361\361" + "\361\377\5\303\303\303\377\373\373\373\377\250\250\250\377\0\0\0\377" + "\0\0\0\32\203\0\0\0\0\3\0\0\0\377\376\376\376\377\361\361\361\377\203" + "\314\314\314\377\202\361\361\361\377\4\255\255\255\377vvv\377]]]\377" + "@@@\377\202\0\0\0\377\4\0\0\0N\0\0\0\0\0\0\0\377\376\376\376\377\205" + "\361\361\361\377\14\360\360\360\377\342\342\342\377\205\205\205\377K" + "KI\377\0\0\0\377\373\347;\377\362\266M\377\0\0\0\377\0\0\0\0\0\0\0\377" + "\376\376\376\377\361\361\361\377\204\312\312\312\377\13\357\357\357\377" + "\312\312\312\377\311\311\311\377\0\0\0\377\374\353=\377\367\265D\377" + "aT.\365\0\0\0\377\0\0\0\0\0\0\0\377\376\376\376\377\203\357\357\357\377" + "\204\356\356\356\377\12\0\0\0\377\374\351;\377\367\265E\377l_4\377\0" + "\0\0\377\0\0\0""0\0\0\0\0\0\0\0\377\375\375\375\377\356\356\356\377\202" + "\311\311\311\377\1\356\356\356\377\202\311\311\311\377\6\0\0\0\377\371" + "\3379\377\364\262D\377f]>\377\0\0\0\377\0\0\0%\202\0\0\0\0\3\0\0\0\377" + "\375\375\375\377\355\355\355\377\203\354\354\354\377\5\353\353\353\377" + "\0\0\0\377\366\3226\377\357\264M\377\\O+\377\202\0\0\0\377\203\0\0\0" + "\0\3\0\0\0\377\375\375\375\377\353\353\353\377\203\304\304\304\377\7" + "\350\350\350\377\0\0\0\377\327\256t\377eY0\377\0\0\0\377\300\300\300" + "\377\0\0\0\377\203\0\0\0\0\2\0\0\0\377\375\375\375\377\202\352\352\352" + "\377\202\351\351\351\377\2\0\0\0\377OA\25\377\202\0\0\0\377\3\347\347" + "\347\377\277\277\277\377\0\0\0\377\203\0\0\0\0\6\0\0\0\377\375\375\375" + "\377\350\350\350\377\302\302\302\377\346\346\346\377\302\302\302\377" + "\202\0\0\0\377\202\346\346\346\377\3\345\345\345\377\276\276\276\377" + "\0\0\0\377\203\0\0\0\0\2\0\0\0\377\375\375\375\377\202\347\347\347\377" + "\203\346\346\346\377\202\345\345\345\377\202\344\344\344\377\2\275\275" + "\275\377\0\0\0\377\203\0\0\0\0\3\0\0\0\377\375\375\375\377\346\346\346" + "\377\203\345\345\345\377\202\344\344\344\377\203\343\343\343\377\2\273" + "\273\273\377\0\0\0\377\203\0\0\0\0\2\0\0\0\377\302\302\302\377\203\275" + "\275\275\377\202\274\274\274\377\204\273\273\273\377\2\240\240\240\377" + "\0\0\0\324\203\0\0\0\0\1\0\0\0A\213\0\0\0\377\1\0\0\0""9\202\0\0\0\0"}; + + diff --git a/src/icons/stock_mail-compose_16.png b/src/icons/stock_mail-compose_16.png new file mode 100644 index 0000000..7c7d597 Binary files /dev/null and b/src/icons/stock_mail-compose_16.png differ diff --git a/src/icons/stock_mail-forward.h b/src/icons/stock_mail-forward.h new file mode 100644 index 0000000..7756403 --- /dev/null +++ b/src/icons/stock_mail-forward.h @@ -0,0 +1,83 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_mail_forward) +#endif +#ifdef __GNUC__ +static const guint8 stock_mail_forward[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_mail_forward[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1303) */ + "\0\0\5/" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\262\0\0\0\0\1\0\0\0\1\221\0\0\0\2\1\0\0\0\1\204\0\0\0\0\1\0\0\0f\221" + "\0\0\0\377\3\0\0\0m\0\0\0\10\0\0\0\2\202\0\0\0\0\3\0\0\0\1\0\0\0\377" + "SSS\377\204\377\377\377\377\31\376\376\376\377\375\375\375\377\374\374" + "\374\377\373\373\373\377\371\371\371\377\370\370\370\377\367\367\367" + "\377\366\366\366\377\365\365\365\377\364\364\364\377\343\343\343\377" + "SSS\377\0\0\0\377\0\0\0\31\0\0\0\10\0\0\0\1\0\0\0\0\0\0\0\2\0\0\0\377" + "\356\356\356\377OOO\377\377\377\377\377\365\365\365\377\364\364\364\377" + "\363\363\363\377\202\362\362\362\377\1\361\361\361\377\202\360\360\360" + "\377\22\357\357\357\377\356\356\356\377\355\355\355\377\343\343\343\377" + "OOO\377\256\256\256\377\0\0\0\377\0\0\0(\0\0\0\17\0\0\0\2\0\0\0\0\0\0" + "\0\2\0\0\0\377\377\377\377\377\344\344\344\377CCC\377\375\375\375\377" + "\363\363\363\377\202\362\362\362\377\3\361\361\361\377\360\360\360\377" + "\357\357\357\377\202\356\356\356\377\22\355\355\355\377\343\343\343\377" + "OOO\377\254\254\254\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0" + "\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\365\365\365\377\344\344" + "\344\377OOO\377\373\373\373\377\361\361\361\377\202\360\360\360\377\2" + "\357\357\357\377\356\356\356\377\202\355\355\355\377%\343\343\343\377" + "CCC\377\240\240\240\377\324\324\324\377\310\310\310\377\0\0\0\377\0\0" + "\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\373\373" + "\373\377\365\365\365\377\344\344\344\377CCC\377\370\370\370\377\360\360" + "\360\377\357\357\357\377\356\356\356\377\355\355\355\377\354\354\354" + "\377\343\343\343\377OOO\377\225\225\225\377\324\324\324\377\344\344\344" + "\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0" + "\2\0\0\0\377\377\377\377\377\203\365\365\365\377\12\344\344\344\377>" + ">>\377\365\365\365\377\356\356\356\377\355\355\355\377\354\354\354\377" + "\343\343\343\377MMM\377\201\201\201\377\306\306\306\377\202\344\344\344" + "\377\12\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0" + "\0\2\0\0\0\377\377\377\377\377\373\373\373\377\202\365\365\365\377\10" + "\344\344\344\377jjj\377CCC\377\363\363\363\377\354\354\354\377\343\343" + "\343\377OOO\377jjj\377\202\306\306\306\377\13\365\365\365\377\0\0\0\377" + "\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0" + "\0\0\377\377\377\377\377\202\365\365\365\377\14\344\344\344\377ccc\377" + "\377\377\377\377\271\271\271\377OOO\377\360\360\360\377OOO\377\225\225" + "\225\377\306\306\306\377sss\377\306\306\306\377\365\365\365\377\203\0" + "\0\0\377\21\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377" + "\377\377\373\373\373\377\344\344\344\377ccc\377\377\377\377\377\365\365" + "\365\377\344\344\344\377\324\324\324\377OOO\377\254\254\254\377\306\306" + "\306\377\202\344\344\344\377\17|||\377\344\344\344\377\0\0\0\377[\210" + "\262\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377" + "\377\377\377\377\344\344\344\377sss\377\377\377\377\377\204\365\365\365" + "\377\1\344\344\344\377\207\0\0\0\377\13\236\270\321\377P\200\255\377" + "\0\0\0\377\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377" + "|||\377\377\377\377\377\206\344\344\344\377\4\0\0\0\377\265\311\334\377" + "\257\305\332\377\262\307\333\377\203\266\312\335\377\11\244\275\325\377" + "\234\267\321\377P\200\255\377\10\15\21\377\0\0\0\2\0\0\0\0\0\0\0\2\0" + "\0\0\377sss\377\210\274\274\274\377\3\0\0\0\377\233\266\320\377\240\272" + "\323\377\202\234\267\321\377\12\233\266\320\377\232\265\317\377\240\272" + "\323\377\234\267\321\377\227\263\316\377_\213\264\377\0\0\0\377\0\0\0" + "\0\0\0\0\2\0\0\0m\212\0\0\0\377\2\221\260\314\377\236\270\321\377\202" + "\232\265\317\377\1\233\266\320\377\203\234\267\321\377\10\240\272\323" + "\377\225\262\316\377Ly\243\377\0\0\0\377\0\0\0\1\0\0\0\10\0\0\0\31\0" + "\0\0(\207\0\0\0-\2\0\0\0\377It\234\377\210\77e\210\377\2*C[\377\0\0\0" + "\377\202\0\0\0\0\3\0\0\0\2\0\0\0\10\0\0\0\16\207\0\0\0\21\2\0\0\0\377" + "Eo\226\377\2057Yx\377\202\77e\210\377\2*C[\377\0\0\0\377\204\0\0\0\0" + "\1\0\0\0\1\210\0\0\0\2\207\0\0\0\377\3\77e\210\377*C[\377\0\0\0\377\224" + "\0\0\0\0\3\0\0\0\377*C[\377\0\0\0\377\225\0\0\0\0\202\0\0\0\377\226\0" + "\0\0\0\1\0\0\0\377\236\0\0\0\0"}; + + diff --git a/src/icons/stock_mail-forward.png b/src/icons/stock_mail-forward.png new file mode 100644 index 0000000..f848a60 Binary files /dev/null and b/src/icons/stock_mail-forward.png differ diff --git a/src/icons/stock_mail-receive.h b/src/icons/stock_mail-receive.h new file mode 100644 index 0000000..f3b9e6d --- /dev/null +++ b/src/icons/stock_mail-receive.h @@ -0,0 +1,87 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_mail_receive) +#endif +#ifdef __GNUC__ +static const guint8 stock_mail_receive[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_mail_receive[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1446) */ + "\0\0\5\276" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\210\0\0\0\0\207\0\0\0\377\221\0\0\0\0\2\0\0\0\377\311\333\307\377\202" + "\311\332\307\377\3\201\232y\377XuM\377\0\0\0\377\221\0\0\0\0\7\0\0\0" + "\377\262\311\260\377\251\304\247\377\255\310\253\377[yP\377MgD\377\0" + "\0\0\377\221\0\0\0\0\2\0\0\0\377\273\320\271\377\202\256\307\254\377" + "\3[yP\377MgD\377\0\0\0\377\216\0\0\0\0\1\0\0\0\324\203\0\0\0\377\5\271" + "\316\267\377\256\307\254\377\252\307\250\377[yP\377MgD\377\204\0\0\0" + "\377\214\0\0\0\0\6\0\0\0\377\315\336\313\377\261\311\260\377\265\316" + "\265\377\260\311\260\377\255\306\253\377\203[yP\377\2MhD\377\0\0\0\377" + "\212\0\0\0\0\1\0\0\0\1\203\0\0\0\2\5\0\0\0\377\315\336\313\377\264\313" + "\262\377\263\312\261\377\255\306\253\377\202[yP\377\2MhD\377\0\0\0\377" + "\205\0\0\0\2\1\0\0\0\1\204\0\0\0\0\1\0\0\0f\206\0\0\0\377\5\315\336\313" + "\377\252\307\250\377\260\307\256\377[yP\377MhD\377\206\0\0\0\377\3\0" + "\0\0m\0\0\0\10\0\0\0\2\202\0\0\0\0\3\0\0\0\1\0\0\0\377SSS\377\204\377" + "\377\377\3770\376\376\376\377\0\0\0\377\315\336\313\377\255\310\253\377" + "TqJ\377\0\0\0\377\367\367\367\377\366\366\366\377\365\365\365\377\364" + "\364\364\377\343\343\343\377SSS\377\0\0\0\377\0\0\0\31\0\0\0\10\0\0\0" + "\1\0\0\0\0\0\0\0\2\0\0\0\377\356\356\356\377OOO\377\377\377\377\377\365" + "\365\365\377\364\364\364\377\363\363\363\377\362\362\362\377\0\0\0\377" + "\317\337\314\377\0\0\0\377\360\360\360\377\357\357\357\377\356\356\356" + "\377\355\355\355\377\343\343\343\377OOO\377\256\256\256\377\0\0\0\377" + "\0\0\0(\0\0\0\17\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\344" + "\344\344\377CCC\377\375\375\375\377\363\363\363\377\202\362\362\362\377" + "\3\361\361\361\377---\377\357\357\357\377\202\356\356\356\377\22\355" + "\355\355\377\343\343\343\377OOO\377\254\254\254\377\310\310\310\377\0" + "\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377" + "\377\365\365\365\377\344\344\344\377OOO\377\373\373\373\377\361\361\361" + "\377\202\360\360\360\377\2\357\357\357\377\356\356\356\377\202\355\355" + "\355\377%\343\343\343\377CCC\377\240\240\240\377\324\324\324\377\310" + "\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0" + "\377\377\377\377\377\373\373\373\377\365\365\365\377\344\344\344\377" + "CCC\377\370\370\370\377\360\360\360\377\357\357\357\377\356\356\356\377" + "\355\355\355\377\354\354\354\377\343\343\343\377OOO\377\225\225\225\377" + "\324\324\324\377\344\344\344\377\310\310\310\377\0\0\0\377\0\0\0-\0\0" + "\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\203\365\365\365" + "\377\12\344\344\344\377>>>\377\365\365\365\377\356\356\356\377\355\355" + "\355\377\354\354\354\377\343\343\343\377MMM\377\201\201\201\377\306\306" + "\306\377\202\344\344\344\377\12\310\310\310\377\0\0\0\377\0\0\0-\0\0" + "\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\373\373\373\377" + "\202\365\365\365\377\10\344\344\344\377jjj\377CCC\377\363\363\363\377" + "\354\354\354\377\343\343\343\377OOO\377jjj\377\202\306\306\306\377\13" + "\365\365\365\377\344\344\344\377\310\310\310\377\0\0\0\377\0\0\0-\0\0" + "\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\202\365\365\365" + "\377\40\344\344\344\377ccc\377\377\377\377\377\271\271\271\377OOO\377" + "\360\360\360\377OOO\377\225\225\225\377\306\306\306\377sss\377\306\306" + "\306\377\365\365\365\377\344\344\344\377\310\310\310\377\0\0\0\377\0" + "\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\373" + "\373\373\377\344\344\344\377ccc\377\377\377\377\377\365\365\365\377\344" + "\344\344\377\324\324\324\377OOO\377\254\254\254\377\306\306\306\377\202" + "\344\344\344\377\1|||\377\202\344\344\344\377\14\310\310\310\377\0\0" + "\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377" + "\377\344\344\344\377sss\377\377\377\377\377\204\365\365\365\377\202\344" + "\344\344\377\203\365\365\365\377\16\344\344\344\377|||\377\344\344\344" + "\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0" + "\2\0\0\0\377\377\377\377\377|||\377\377\377\377\377\214\344\344\344\377" + "\12|||\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0" + "\0\0\0\2\0\0\0\377sss\377\217\274\274\274\377\10ccc\377\0\0\0\377\0\0" + "\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0m\221\0\0\0\377\11\0\0\0\207" + "\0\0\0(\0\0\0\17\0\0\0\2\0\0\0\0\0\0\0\1\0\0\0\10\0\0\0\31\0\0\0(\217" + "\0\0\0-\4\0\0\0(\0\0\0\31\0\0\0\10\0\0\0\1\202\0\0\0\0\3\0\0\0\2\0\0" + "\0\10\0\0\0\16\217\0\0\0\21\3\0\0\0\16\0\0\0\10\0\0\0\2\204\0\0\0\0\1" + "\0\0\0\1\221\0\0\0\2\1\0\0\0\1\202\0\0\0\0"}; + + diff --git a/src/icons/stock_mail-receive.png b/src/icons/stock_mail-receive.png new file mode 100644 index 0000000..20580e0 Binary files /dev/null and b/src/icons/stock_mail-receive.png differ diff --git a/src/icons/stock_mail-reply-to-all.h b/src/icons/stock_mail-reply-to-all.h new file mode 100644 index 0000000..ae86e8a --- /dev/null +++ b/src/icons/stock_mail-reply-to-all.h @@ -0,0 +1,82 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_mail_reply_to_all) +#endif +#ifdef __GNUC__ +static const guint8 stock_mail_reply_to_all[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_mail_reply_to_all[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1373) */ + "\0\0\5u" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\237\0\0\0\0\2\0\0\0\33\0\0\0|\202\0\0\0\377\2\0\0\0m\0\0\0\33\222\0" + "\0\0\0\6\0\0\0m\255\251\234\377\346\341\320\376\360\352\331\376\251\245" + "\230\377\0\0\0\253\216\0\0\0\0\6$$#\20\21\21\21^\24\24\26\212\26\26\30" + "\224\10\10\11\377\333\326\307\377\203\360\352\331\377\1\0\0\0\377\216" + "\0\0\0\0\12\33\33\34\214\5\5\5\371ZZZ\376ggg\376\2\2\2\377\200}v\377" + "\324\232>\377\322\227;\377\360\352\331\377\0\0\0\377\216\0\0\0\0\12\4" + "\4\2\360mkk\377zyy\377\3\1\0\377\4\1\0\377('$\377\335\2301\377\332\226" + "/\377\360\352\331\377\0\0\0\377\216\0\0\0\0\13\32\16\1\372\274m\33\377" + "\276n\23\377\276m\23\377\275l\23\377><6\377\326\220)\377\321\215'\377" + "\360\352\331\377spg\377\0\0\0""3\211\0\0\0\0\1\0\0\0\1\203\0\0\0\0\10" + "\21\11\0\374\317y\34\377\316y\34\377\315v\32\377\304p\23\377JA6\377\240" + "s\36\377\226i\32\377\202\360\352\331\377\2\0\0\0\377\0\1\0\377\206\0" + "\0\0\377\3\0\0\0^\0\0\0\6\0\0\0\3\203\0\0\0\0\14)\27\3\377\272f\23\377" + "\214N\15\377\204J\15\377\277m\23\377\37*\15\3770b\40\377OrB\377D\215" + "D\377.\200.\377:o:\377\0\0\0\377\205\377\377\377\377+SSS\377\0\0\0\377" + "\0\0\0)\0\0\0\11\0\0\0\0\0\0\0\1\"\"$V\27\20\11\377\254`\14\377q\77\12" + "\377i:\12\377\252_\12\377\31+\20\377'Y&\377h\247i\3775\2126\377.\200" + ".\377-\201-\377\0\0\0\377\373\373\373\377\367\367\367\377\372\372\372" + "\377\365\365\365\377OOO\377\256\256\256\377\0\0\0\377\0\0\0O\0\0\0\22" + "\0\0\0\0\0\0\0)\24\25\27\360ov|\377SLF\377$\26\11\377#\30\16\377HFD\377" + "5JM\377\5\13\7\377O~O\3779\2229\377-\205-\377)\177)\377\0\0\0\377\203" + "\365\365\365\377\25OOO\377\254\254\254\377\310\310\310\377\0\0\0\377" + "\0\0\0W\275\275\275>\0\0\0\0\25\26\30\361l|\213\377r\216\251\377]kx\377" + "\310\313\315\377\303\307\315\377\200\220\242\377Zr\213\377Ndy\377\21" + "\27\24\3772\2072\377+\177,\377+w,\377\0\0\0\377\202\365\365\365\377\22" + "CCC\377\240\240\240\377\324\324\324\377\310\310\310\377\0\0\0\377\0\0" + "\0W\0\0\0\24\0\0\0\0%(*\375\212\236\264\377j\206\241\377Yj|\377\335\341" + "\344\377\334\342\347\377\211\234\254\377h~\226\377Tm\207\377\20\26\35" + "\377\203\0\0\0\377\25\214\214\214\377\365\365\365\377OOO\377\225\225" + "\225\377\324\324\324\377\344\344\344\377\310\310\310\377\0\0\0\377\0" + "\0\0W\0\0\0\24\0\0\0\0#(-\377\200\226\253\377]y\226\377Sh}\377\315\323" + "\331\377\313\322\334\377\211\233\253\377\314\324\334\377\\t\214\377\21" + "\30\40\377\204\365\365\365\377\3MMM\377\201\201\201\377\306\306\306\377" + "\202\344\344\344\377\17\310\310\310\377\0\0\0\377\0\0\0W\0\0\0\24\0\0" + "\0\0\37%+\377k\201\227\377Sm\211\377J]o\377\201\221\237\377\202\223\244" + "\377Zp\207\377Id\201\377D]y\377\16\25\33\377\203\365\365\365\377\16O" + "OO\377jjj\377\240\240\240\377\344\344\344\377\365\365\365\377\344\344" + "\344\377\310\310\310\377\0\0\0\377\0\0\0W\0\0\0\24\0\0\0\0\0\0\0g\16" + "\24\32\376\15\23\31\377\203\16\24\33\377\21\16\24\32\377\16\23\32\377" + "\17\24\32\377ccc\377OOO\377\365\365\365\377OOO\377\225\225\225\377\306" + "\306\306\377sss\377\306\306\306\377\365\365\365\377\344\344\344\377\310" + "\310\310\377\0\0\0\377\0\0\0W\0\0\0\24\203\0\0\0\0\17\0\0\0\377\377\377" + "\377\377\373\373\373\377\365\365\365\377ccc\377\306\306\306\377\365\365" + "\365\377\344\344\344\377\324\324\324\377OOO\377\254\254\254\377\306\306" + "\306\377\344\344\344\377\365\365\365\377|||\377\202\344\344\344\377\4" + "\310\310\310\377\0\0\0\377\0\0\0W\0\0\0\24\203\0\0\0\0\5\0\0\0\377\377" + "\377\377\377\365\365\365\377sss\377\324\324\324\377\204\365\365\365\377" + "\202\344\344\344\377\204\365\365\365\377\6|||\377\344\344\344\377\310" + "\310\310\377\0\0\0\377\0\0\0W\0\0\0\24\203\0\0\0\0\4\0\0\0\377\377\377" + "\377\377|||\377\324\324\324\377\214\344\344\344\377\5|||\377\310\310" + "\310\377\0\0\0\377\0\0\0W\0\0\0\24\203\0\0\0\0\2\0\0\0\377sss\377\217" + "\274\274\274\377\4ccc\377\0\0\0\377\0\0\0W\0\0\0\23\203\0\0\0\0\1\0\0" + "\0^\221\0\0\0\377\3\0\0\0\267\0\0\0R\0\0\0\20\203\0\0\0\0\3\0\0\0\7\0" + "\0\0)\0\0\0O\217\0\0\0W\3\0\0\0R\0\0\0""3\0\0\0\11\203\0\0\0\0\3\0\0" + "\0\1\0\0\0\7\0\0\0\17\217\0\0\0\21\3\0\0\0\20\0\0\0\11\0\0\0\1\230\0" + "\0\0\0"}; + + diff --git a/src/icons/stock_mail-reply-to-all.png b/src/icons/stock_mail-reply-to-all.png new file mode 100644 index 0000000..e268577 Binary files /dev/null and b/src/icons/stock_mail-reply-to-all.png differ diff --git a/src/icons/stock_mail-reply.h b/src/icons/stock_mail-reply.h new file mode 100644 index 0000000..c053124 --- /dev/null +++ b/src/icons/stock_mail-reply.h @@ -0,0 +1,83 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_mail_reply) +#endif +#ifdef __GNUC__ +static const guint8 stock_mail_reply[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_mail_reply[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1293) */ + "\0\0\5%" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\264\0\0\0\0\1\0\0\0\1\221\0\0\0\2\1\0\0\0\1\204\0\0\0\0\1\0\0\0f\221" + "\0\0\0\377\3\0\0\0m\0\0\0\10\0\0\0\2\202\0\0\0\0\3\0\0\0\1\0\0\0\377" + "SSS\377\204\377\377\377\377\17\376\376\376\377\375\375\375\377\374\374" + "\374\377\373\373\373\377\371\371\371\377\370\370\370\377\367\367\367" + "\377\366\366\366\377\365\365\365\377\364\364\364\377\343\343\343\377" + "SSS\377\0\0\0\377\0\0\0\31\0\0\0\10\202\0\0\0\0\10\0\0\0\2\0\0\0\377" + "\356\356\356\377OOO\377\377\377\377\377\365\365\365\377\364\364\364\377" + "\363\363\363\377\202\362\362\362\377\1\361\361\361\377\202\360\360\360" + "\377\11\357\357\357\377\356\356\356\377\355\355\355\377\343\343\343\377" + "OOO\377\256\256\256\377\0\0\0\377\0\0\0(\0\0\0\17\202\0\0\0\0\7\0\0\0" + "\2\0\0\0\377\377\377\377\377\344\344\344\377CCC\377\375\375\375\377\363" + "\363\363\377\202\362\362\362\377\3\361\361\361\377\360\360\360\377\357" + "\357\357\377\202\356\356\356\377\10\355\355\355\377\343\343\343\377O" + "OO\377\254\254\254\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\202" + "\0\0\0\0\10\0\0\0\2\0\0\0\377\377\377\377\377\365\365\365\377\344\344" + "\344\377OOO\377\373\373\373\377\361\361\361\377\202\360\360\360\377\2" + "\357\357\357\377\356\356\356\377\202\355\355\355\377\10\343\343\343\377" + "CCC\377\240\240\240\377\324\324\324\377\310\310\310\377\0\0\0\377\0\0" + "\0-\0\0\0\21\202\0\0\0\0\26\0\0\0\2\0\0\0\377\377\377\377\377\373\373" + "\373\377\365\365\365\377\344\344\344\377CCC\377\370\370\370\377\360\360" + "\360\377\357\357\357\377\356\356\356\377\355\355\355\377\354\354\354" + "\377\343\343\343\377OOO\377\225\225\225\377\324\324\324\377\344\344\344" + "\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\202\0\0\0\0\3\0\0\0\2" + "\0\0\0\377\377\377\377\377\203\365\365\365\377\12\344\344\344\377>>>" + "\377\365\365\365\377\356\356\356\377\355\355\355\377\354\354\354\377" + "\343\343\343\377MMM\377\201\201\201\377\306\306\306\377\202\344\344\344" + "\377\4\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\202\0\0\0\0\16\0\0\0" + "\2\0\0\0\377\377\377\377\377\373\373\373\377=\32\23\377\365\365\365\377" + "\344\344\344\377jjj\377CCC\377\363\363\363\377\354\354\354\377\343\343" + "\343\377OOO\377jjj\377\202\306\306\306\377\6\365\365\365\377\344\344" + "\344\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\202\0\0\0\0\3\0\0" + "\0\2\0\0\0\377\377\377\377\377\202\0\0\0\377\21\344\344\344\377ccc\377" + "\377\377\377\377\271\271\271\377OOO\377\360\360\360\377OOO\377\225\225" + "\225\377\306\306\306\377sss\377\306\306\306\377\365\365\365\377\344\344" + "\344\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\202\0\0\0\0\1\0\0" + "\0\2\202\0\0\0\377\12\352\300\272\377\0\0\0\377ccc\377\377\377\377\377" + "\365\365\365\377\344\344\344\377\324\324\324\377OOO\377\254\254\254\377" + "\306\306\306\377\202\344\344\344\377\1|||\377\202\344\344\344\377\4\310" + "\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\202\0\0\0\0\4\0\0\0\2\0\0\0\377" + "\352\300\272\377\337\236\225\377\207\0\0\0\377\1\344\344\344\377\203" + "\365\365\365\377\7\344\344\344\377|||\377\344\344\344\377\310\310\310" + "\377\0\0\0\377\0\0\0-\0\0\0\21\202\0\0\0\0\13\0\0\0\377\352\300\272\377" + "\340\241\230\377\341\245\235\377\342\247\236\377\343\253\243\377\342" + "\247\236\377\343\252\242\377\337\236\225\377\314bS\377\0\0\0\377\206" + "\344\344\344\377\14|||\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21" + "\0\0\0\0\0\0\0\377\352\300\272\377\335\227\215\377\337\237\227\377\337" + "\236\225\377\336\232\220\377\202\335\226\214\377\4\336\232\220\377\335" + "\224\212\377\261C4\377\0\0\0\377\207\274\274\274\377\10ccc\377\0\0\0" + "\377\0\0\0-\0\0\0\21C\31\23\377\352\302\274\377\336\232\220\377\337\234" + "\222\377\202\335\231\217\377\1\335\227\215\377\204\336\232\220\377\1" + "\265E5\377\211\0\0\0\377\6\0\0\0\207\0\0\0(\0\0\0\17\0\0\0\0\0\0\0\377" + "e&\35\377\210\230:,\377\2|/$\377\0\0\0\377\210\0\0\0-\3\0\0\0(\0\0\0" + "\31\0\0\0\10\202\0\0\0\0\2\0\0\0\377e&\35\377\202\230:,\377\205c%\35" + "\377\2l)\37\377\0\0\0\377\210\0\0\0\21\3\0\0\0\16\0\0\0\10\0\0\0\2\203" + "\0\0\0\0\3\0\0\0\377e&\35\377\230:,\377\207\0\0\0\377\211\0\0\0\2\1\0" + "\0\0\1\205\0\0\0\0\3\0\0\0\377e&\35\377\0\0\0\377\226\0\0\0\0\202\0\0" + "\0\377\227\0\0\0\0\1\0\0\0\377\251\0\0\0\0"}; + + diff --git a/src/icons/stock_mail-reply.png b/src/icons/stock_mail-reply.png new file mode 100644 index 0000000..47eecc6 Binary files /dev/null and b/src/icons/stock_mail-reply.png differ diff --git a/src/icons/stock_mail-send.h b/src/icons/stock_mail-send.h new file mode 100644 index 0000000..1aff2bd --- /dev/null +++ b/src/icons/stock_mail-send.h @@ -0,0 +1,84 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_mail_send) +#endif +#ifdef __GNUC__ +static const guint8 stock_mail_send[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_mail_send[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1369) */ + "\0\0\5q" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\243\0\0\0\0\1O\77\12\377\226\0\0\0\0\3\0\0\0\377\364\346\265\377\0\0" + "\0\377\224\0\0\0\0\5\0\0\0\377\364\345\263\377\355\326\204\377u_\20\377" + "\0\0\0\377\222\0\0\0\0\7\0\0\0\377\364\345\263\377\355\325\200\377\356" + "\326\205\377\257\215\30\377u_\20\377\0\0\0\377\220\0\0\0\0\5\0\0\0\377" + "\364\345\263\377\357\331\214\377\356\330\213\377\355\325\202\377\202" + "\257\215\30\377\2u_\20\377\0\0\0\377\213\0\0\0\0\1\0\0\0\1\202\0\0\0" + "\2\6\0\0\0\377\364\345\263\377\356\330\211\377\357\332\222\377\356\330" + "\211\377\355\325\202\377\203\257\215\30\377\2u_\20\377\0\0\0\377\204" + "\0\0\0\2\1\0\0\0\1\204\0\0\0\0\1\0\0\0f\206\0\0\0\377\5\360\333\223\377" + "\355\326\204\377\355\325\200\377\257\215\30\377s]\20\377\206\0\0\0\377" + "\3\0\0\0m\0\0\0\10\0\0\0\2\202\0\0\0\0\3\0\0\0\1\0\0\0\377SSS\377\204" + "\377\377\377\377\2\0\0\0\377\360\334\227\377\202\355\326\204\377D\257" + "\215\30\377s]\20\377\0\0\0\377\366\366\366\377\365\365\365\377\364\364" + "\364\377\343\343\343\377SSS\377\0\0\0\377\0\0\0\31\0\0\0\10\0\0\0\1\0" + "\0\0\0\0\0\0\2\0\0\0\377\356\356\356\377OOO\377\377\377\377\377\365\365" + "\365\377\364\364\364\377\0\0\0\377\356\330\211\377\355\323|\377\355\326" + "\204\377\257\215\30\377s]\20\377\0\0\0\377\356\356\356\377\355\355\355" + "\377\343\343\343\377OOO\377\256\256\256\377\0\0\0\377\0\0\0(\0\0\0\17" + "\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\344\344\344\377CC" + "C\377\375\375\375\377\363\363\363\377\0\0\0\377\344\277=\377\313\244" + "\34\377\321\250\34\377\220t\23\377|d\21\377\0\0\0\377\355\355\355\377" + "\343\343\343\377OOO\377\254\254\254\377\310\310\310\377\0\0\0\377\0\0" + "\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\365\365" + "\365\377\344\344\344\377OOO\377\373\373\373\377\207\0\0\0\377%\343\343" + "\343\377CCC\377\240\240\240\377\324\324\324\377\310\310\310\377\0\0\0" + "\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377" + "\373\373\373\377\365\365\365\377\344\344\344\377CCC\377\370\370\370\377" + "\360\360\360\377\357\357\357\377\356\356\356\377\355\355\355\377\354" + "\354\354\377\343\343\343\377OOO\377\225\225\225\377\324\324\324\377\344" + "\344\344\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0" + "\0\0\0\0\2\0\0\0\377\377\377\377\377\203\365\365\365\377\12\344\344\344" + "\377>>>\377\365\365\365\377\356\356\356\377\355\355\355\377\354\354\354" + "\377\343\343\343\377MMM\377\201\201\201\377\306\306\306\377\202\344\344" + "\344\377\12\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0" + "\0\0\0\2\0\0\0\377\377\377\377\377\373\373\373\377\202\365\365\365\377" + "\10\344\344\344\377jjj\377CCC\377\363\363\363\377\354\354\354\377\343" + "\343\343\377OOO\377jjj\377\202\306\306\306\377\13\365\365\365\377\344" + "\344\344\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0" + "\0\0\0\0\2\0\0\0\377\377\377\377\377\202\365\365\365\377\40\344\344\344" + "\377ccc\377\377\377\377\377\271\271\271\377OOO\377\360\360\360\377OO" + "O\377\225\225\225\377\306\306\306\377sss\377\306\306\306\377\365\365" + "\365\377\344\344\344\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0" + "\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\373\373\373\377\344" + "\344\344\377ccc\377\377\377\377\377\365\365\365\377\344\344\344\377\324" + "\324\324\377OOO\377\254\254\254\377\306\306\306\377\202\344\344\344\377" + "\1|||\377\202\344\344\344\377\14\310\310\310\377\0\0\0\377\0\0\0-\0\0" + "\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\344\344\344\377" + "sss\377\377\377\377\377\204\365\365\365\377\202\344\344\344\377\203\365" + "\365\365\377\16\344\344\344\377|||\377\344\344\344\377\310\310\310\377" + "\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377" + "\377\377|||\377\377\377\377\377\214\344\344\344\377\12|||\377\310\310" + "\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377" + "sss\377\217\274\274\274\377\10ccc\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0" + "\2\0\0\0\0\0\0\0\2\0\0\0m\221\0\0\0\377\11\0\0\0\207\0\0\0(\0\0\0\17" + "\0\0\0\2\0\0\0\0\0\0\0\1\0\0\0\10\0\0\0\31\0\0\0(\217\0\0\0-\4\0\0\0" + "(\0\0\0\31\0\0\0\10\0\0\0\1\202\0\0\0\0\3\0\0\0\2\0\0\0\10\0\0\0\16\217" + "\0\0\0\21\3\0\0\0\16\0\0\0\10\0\0\0\2\204\0\0\0\0\1\0\0\0\1\221\0\0\0" + "\2\1\0\0\0\1\202\0\0\0\0"}; + + diff --git a/src/icons/stock_mail-send.png b/src/icons/stock_mail-send.png new file mode 100644 index 0000000..e1fa151 Binary files /dev/null and b/src/icons/stock_mail-send.png differ diff --git a/src/icons/stock_mail.h b/src/icons/stock_mail.h new file mode 100644 index 0000000..5114b0d --- /dev/null +++ b/src/icons/stock_mail.h @@ -0,0 +1,68 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_mail) +#endif +#ifdef __GNUC__ +static const guint8 stock_mail[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_mail[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1031) */ + "\0\0\4\37" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\343\0\0\0\0\1\0\0\0\1\221\0\0\0\2\1\0\0\0\1\204\0\0\0\0\1\0\0\0f\221" + "\0\0\0\377\3\0\0\0m\0\0\0\10\0\0\0\2\202\0\0\0\0\3\0\0\0\1\0\0\0\377" + "SSS\377\217\377\377\377\377#SSS\377\0\0\0\377\0\0\0\31\0\0\0\10\0\0\0" + "\1\0\0\0\0\0\0\0\2\0\0\0\377\356\356\356\377OOO\377\365\365\365\377\373" + "\373\373\377\365\365\365\377\373\373\373\377\367\367\367\377\373\373" + "\373\377\365\365\365\377\373\373\373\377\365\365\365\377\373\373\373" + "\377\367\367\367\377\372\372\372\377\365\365\365\377OOO\377\256\256\256" + "\377\0\0\0\377\0\0\0(\0\0\0\17\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377" + "\377\377\377\344\344\344\377CCC\377\213\365\365\365\377\16OOO\377\254" + "\254\254\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0" + "\0\0\0\0\2\0\0\0\377\377\377\377\377\365\365\365\377\344\344\344\377" + "OOO\377\211\365\365\365\377\20CCC\377\240\240\240\377\324\324\324\377" + "\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0" + "\0\0\377\377\377\377\377\373\373\373\377\365\365\365\377\344\344\344" + "\377CCC\377\207\365\365\365\377\15OOO\377\225\225\225\377\324\324\324" + "\377\344\344\344\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0" + "\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\203\365\365\365\377\2\344" + "\344\344\377>>>\377\205\365\365\365\377\3MMM\377\201\201\201\377\306" + "\306\306\377\202\344\344\344\377\12\310\310\310\377\0\0\0\377\0\0\0-" + "\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\373\373\373" + "\377\203\365\365\365\377\2jjj\377CCC\377\203\365\365\365\377\17OOO\377" + "jjj\377\240\240\240\377\344\344\344\377\365\365\365\377\344\344\344\377" + "\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0" + "\0\0\377\377\377\377\377\203\365\365\365\377\1ccc\377\202\271\271\271" + "\377\37OOO\377\365\365\365\377OOO\377\225\225\225\377\306\306\306\377" + "sss\377\306\306\306\377\365\365\365\377\344\344\344\377\310\310\310\377" + "\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377" + "\377\377\373\373\373\377\365\365\365\377ccc\377\306\306\306\377\365\365" + "\365\377\344\344\344\377\324\324\324\377OOO\377\254\254\254\377\306\306" + "\306\377\344\344\344\377\365\365\365\377|||\377\202\344\344\344\377\14" + "\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0" + "\0\0\377\377\377\377\377\365\365\365\377sss\377\324\324\324\377\204\365" + "\365\365\377\202\344\344\344\377\204\365\365\365\377\15|||\377\344\344" + "\344\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0" + "\0\0\2\0\0\0\377\377\377\377\377|||\377\324\324\324\377\214\344\344\344" + "\377\12|||\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0" + "\0\0\0\0\0\2\0\0\0\377sss\377\217\274\274\274\377\10ccc\377\0\0\0\377" + "\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0m\221\0\0\0\377\11\0\0" + "\0\207\0\0\0(\0\0\0\17\0\0\0\2\0\0\0\0\0\0\0\1\0\0\0\10\0\0\0\31\0\0" + "\0(\217\0\0\0-\4\0\0\0(\0\0\0\31\0\0\0\10\0\0\0\1\202\0\0\0\0\3\0\0\0" + "\2\0\0\0\10\0\0\0\16\217\0\0\0\21\3\0\0\0\16\0\0\0\10\0\0\0\2\204\0\0" + "\0\0\1\0\0\0\1\221\0\0\0\2\1\0\0\0\1\262\0\0\0\0"}; + + diff --git a/src/icons/stock_mail.png b/src/icons/stock_mail.png new file mode 100644 index 0000000..5f8922e Binary files /dev/null and b/src/icons/stock_mail.png differ diff --git a/src/icons/stock_mail_receive_all.h b/src/icons/stock_mail_receive_all.h new file mode 100644 index 0000000..3f8227f --- /dev/null +++ b/src/icons/stock_mail_receive_all.h @@ -0,0 +1,91 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_mail_receive_all) +#endif +#ifdef __GNUC__ +static const guint8 stock_mail_receive_all[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_mail_receive_all[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1563) */ + "\0\0\6""3" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\264\0\0\0\0\5\16\17\16\3537<6\377493\377\36%\33\377\6\10\5\353\205\0" + "\0\0\0\5\16\17\16\3537<6\377493\377\36%\33\377\6\10\5\353\211\0\0\0\0" + "\5/4/\353\272\317\270\377\255\305\252\377`|V\377\24\33\22\353\205\0\0" + "\0\0\5/4/\353\272\317\270\377\255\305\252\377`|V\377\24\33\22\353\207" + "\0\0\0\0\7\0\0\0\24\0\0\0\27.3-\355\263\312\261\377\241\273\235\377U" + "qJ\377\23\32\21\355\202\0\0\0\27\10\0\0\0\0\0\0\0\24\0\0\0\27.3-\355" + "\263\312\261\377\241\273\235\377UqJ\377\23\32\21\355\202\0\0\0\27\205" + "\0\0\0\0\23\0\0\0\254.2-\377]h\\\377\263\313\262\377\237\273\234\377" + "WtL\377,;'\377\21\27\17\377\0\0\0\277\0\0\0\0\0\0\0\254.2-\377]h\\\377" + "\263\313\262\377\237\273\234\377WtL\377,;'\377\21\27\17\377\0\0\0\277" + "\205\0\0\0\0\24\0\0\0\36=B<\301\266\312\265\377\263\313\262\377\241\273" + "\236\377[yP\377PkF\377\27\37\24\301\0\0\0\36\0\0\0\0\0\0\0\36=B<\301" + "\266\312\265\377\263\313\262\377\241\273\236\377[yP\377PkF\377\27\37" + "\24\301\0\0\0\40\0\0\0\1\204\0\0\0\0\7\0\0\0f\0\0\0\377372\377\254\301" + "\252\377\242\274\237\377NhE\377\23\32\21\377\205\0\0\0\377\11""372\377" + "\254\301\252\377\242\274\237\377NhE\377\23\32\21\377\0\0\0\377\0\0\0" + "m\0\0\0\10\0\0\0\2\202\0\0\0\0\11\0\0\0\1\0\0\0\377SSS\377\337\337\337" + "\377qvp\377\233\256\230\377MVK\377\336\336\336\377\376\376\376\377\202" + "\374\374\374\377\26\372\372\372\377\367\367\367\377\330\330\330\377o" + "tn\377\233\256\230\377KSH\377\306\306\306\377SSS\377\0\0\0\377\0\0\0" + "\31\0\0\0\10\0\0\0\1\0\0\0\0\0\0\0\2\0\0\0\377\356\356\356\377OOO\377" + "\332\332\332\377twt\377\320\320\320\377\363\363\363\377\362\362\362\377" + "\203\361\361\361\377\23\360\360\360\377\357\357\357\377\313\313\313\377" + "sur\377\302\302\302\377OOO\377\256\256\256\377\0\0\0\377\0\0\0(\0\0\0" + "\17\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\344\344\344\377" + "CCC\377\375\375\375\377\363\363\363\377\202\362\362\362\377\202\361\361" + "\361\377\1\357\357\357\377\202\356\356\356\377\22\355\355\355\377\343" + "\343\343\377OOO\377\254\254\254\377\310\310\310\377\0\0\0\377\0\0\0-" + "\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\365\365\365" + "\377\344\344\344\377OOO\377\373\373\373\377\361\361\361\377\202\360\360" + "\360\377\2\357\357\357\377\356\356\356\377\202\355\355\355\377%\343\343" + "\343\377CCC\377\240\240\240\377\324\324\324\377\310\310\310\377\0\0\0" + "\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377" + "\373\373\373\377\365\365\365\377\344\344\344\377CCC\377\370\370\370\377" + "\360\360\360\377\357\357\357\377\356\356\356\377\355\355\355\377\354" + "\354\354\377\343\343\343\377OOO\377\225\225\225\377\324\324\324\377\344" + "\344\344\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0" + "\0\0\0\0\2\0\0\0\377\377\377\377\377\203\365\365\365\377\12\344\344\344" + "\377>>>\377\365\365\365\377\356\356\356\377\355\355\355\377\354\354\354" + "\377\343\343\343\377MMM\377\201\201\201\377\306\306\306\377\202\344\344" + "\344\377\12\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0" + "\0\0\0\2\0\0\0\377\377\377\377\377\373\373\373\377\202\365\365\365\377" + "\10\344\344\344\377jjj\377CCC\377\363\363\363\377\354\354\354\377\343" + "\343\343\377OOO\377jjj\377\202\306\306\306\377\13\365\365\365\377\344" + "\344\344\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0" + "\0\0\0\0\2\0\0\0\377\377\377\377\377\202\365\365\365\377\40\344\344\344" + "\377ccc\377\377\377\377\377\271\271\271\377OOO\377\360\360\360\377OO" + "O\377\225\225\225\377\306\306\306\377sss\377\306\306\306\377\365\365" + "\365\377\344\344\344\377\310\310\310\377\0\0\0\377\0\0\0-\0\0\0\21\0" + "\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\373\373\373\377\344" + "\344\344\377ccc\377\377\377\377\377\365\365\365\377\344\344\344\377\324" + "\324\324\377OOO\377\254\254\254\377\306\306\306\377\202\344\344\344\377" + "\1|||\377\202\344\344\344\377\14\310\310\310\377\0\0\0\377\0\0\0-\0\0" + "\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\344\344\344\377" + "sss\377\377\377\377\377\204\365\365\365\377\202\344\344\344\377\203\365" + "\365\365\377\16\344\344\344\377|||\377\344\344\344\377\310\310\310\377" + "\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377" + "\377\377|||\377\377\377\377\377\214\344\344\344\377\12|||\377\310\310" + "\310\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377" + "sss\377\217\274\274\274\377\10ccc\377\0\0\0\377\0\0\0-\0\0\0\21\0\0\0" + "\2\0\0\0\0\0\0\0\2\0\0\0m\221\0\0\0\377\11\0\0\0\207\0\0\0(\0\0\0\17" + "\0\0\0\2\0\0\0\0\0\0\0\1\0\0\0\10\0\0\0\31\0\0\0(\217\0\0\0-\4\0\0\0" + "(\0\0\0\31\0\0\0\10\0\0\0\1\202\0\0\0\0\3\0\0\0\2\0\0\0\10\0\0\0\16\217" + "\0\0\0\21\3\0\0\0\16\0\0\0\10\0\0\0\2\204\0\0\0\0\1\0\0\0\1\221\0\0\0" + "\2\1\0\0\0\1\202\0\0\0\0"}; + + diff --git a/src/icons/stock_mail_receive_all.png b/src/icons/stock_mail_receive_all.png new file mode 100644 index 0000000..0004bf2 Binary files /dev/null and b/src/icons/stock_mail_receive_all.png differ diff --git a/src/icons/stock_mail_send_queue.h b/src/icons/stock_mail_send_queue.h new file mode 100644 index 0000000..3fd224a --- /dev/null +++ b/src/icons/stock_mail_send_queue.h @@ -0,0 +1,92 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_mail_send_queue) +#endif +#ifdef __GNUC__ +static const guint8 stock_mail_send_queue[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_mail_send_queue[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1550) */ + "\0\0\6&" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\243\0\0\0\0\1O\77\12\377\226\0\0\0\0\3\0\0\0\377\364\346\265\377\0\0" + "\0\377\224\0\0\0\0\5\0\0\0\377\364\345\263\377\355\326\204\377u_\20\377" + "\0\0\0\377\222\0\0\0\0\7\0\0\0\377\364\345\263\377\355\325\200\377\356" + "\326\205\377\257\215\30\377u_\20\377\0\0\0\377\220\0\0\0\0\5\0\0\0\377" + "\364\345\263\377\357\331\214\377\356\330\213\377\355\325\202\377\202" + "\257\215\30\377\2u_\20\377\0\0\0\377\213\0\0\0\0\1\0\0\0\1\202\0\0\0" + "\2\6\0\0\0\377\364\345\263\377\356\330\211\377\357\332\222\377\356\330" + "\211\377\355\325\202\377\203\257\215\30\377\2u_\20\377\0\0\0\377\204" + "\0\0\0\2\1\0\0\0\1\204\0\0\0\0\1\0\0\0f\206\0\0\0\377\5\360\333\223\377" + "\355\326\204\377\355\325\200\377\257\215\30\377s]\20\377\206\0\0\0\377" + "\3\0\0\0m\0\0\0\10\0\0\0\2\202\0\0\0\0\3\0\0\0\1\0\0\0\377SSS\377\204" + "\377\377\377\377\2\0\0\0\377\360\334\227\377\202\355\326\204\377D\257" + "\215\30\377s]\20\377\0\0\0\377\366\366\366\377\365\365\365\377\364\364" + "\364\377\343\343\343\377SSS\377\0\0\0\377\0\0\0\31\0\0\0\10\0\0\0\1\0" + "\0\0\0\0\0\0\2\0\0\0\377\356\356\356\377OOO\377\377\377\377\377\365\365" + "\365\377\364\364\364\377\0\0\0\377\356\330\211\377\355\323|\377\355\326" + "\204\377\257\215\30\377s]\20\377\0\0\0\377\356\356\356\377\355\355\355" + "\377\343\343\343\377OOO\377\256\256\256\377\0\0\0\377\0\0\0(\0\0\0\17" + "\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\344\344\344\377CC" + "C\377\375\375\375\377\363\363\363\377\0\0\0\377\344\277=\377\313\244" + "\34\377\321\250\34\377\220t\23\377|d\21\377\0\0\0\377\355\355\355\377" + "\343\343\343\377OOO\377\254\254\254\377\310\310\310\377\0\0\0\377\0\0" + "\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\365\365" + "\365\377\344\344\344\377OOO\377\373\373\373\377\207\0\0\0\377%\343\343" + "\343\377CCC\377\240\240\240\377\324\324\324\377\310\310\310\377\0\0\0" + "\377\0\0\0-\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377" + "\373\373\373\377\365\365\365\377\344\344\344\377CCC\377\370\370\370\377" + "\360\360\360\377\357\357\357\377\356\356\356\377\352\352\352\377\326" + "\326\326\377\242\242\242\377III\377SST\377YYY\377nnn\377\217\217\217" + "\377\3\3\3\377\0\0\0/\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377" + "\377\377\377\203\365\365\365\377\11\344\344\344\377>>>\377\365\365\365" + "\377\356\356\356\377\353\353\353\377\306\306\306\377mmm\377sss\377\372" + "\372\371\377\202\377\377\377\377\13\362\365\364\377xxw\377\23\23\23\377" + "\4\4\4Q\1\1\1\23\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\373" + "\373\373\377\202\365\365\365\377\7\344\344\344\377jjj\377CCC\377\363" + "\363\363\377\332\332\332\377qpo\377\252\247\247\377\203\377\377\377\377" + "\1\355\354\354\377\202\377\377\377\377\10\226\226\226\377\24\24\24\250" + "\16\16\16&\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\202\365" + "\365\365\377\7\344\344\344\377ccc\377\377\377\377\377\271\271\271\377" + "OOO\377\275\275\275\377USV\377\204\377\377\377\377\2www\377\327\327\327" + "\377\202\377\377\377\377\"HHH\355\32\32\32N\0\0\0\2\0\0\0\0\0\0\0\2\0" + "\0\0\377\377\377\377\377\373\373\373\377\344\344\344\377ccc\377\377\377" + "\377\377\365\365\365\377\344\344\344\377\324\324\324\377BBB\377\304\304" + "\304\377\377\377\377\377\375\375\375\377\377\377\377\377\266\266\266" + "\377EEE\377\376\376\377\377\370\371\371\377\377\377\377\377\253\254\254" + "\377)))\216\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377\344\344" + "\344\377sss\377\377\377\377\377\204\365\365\365\377\23yyy\377\301\301" + "\301\377\377\377\377\377\370\370\371\377\377\377\377\377\40\40!\377$" + "$!\377\377\377\377\377\367\370\370\377\377\377\377\377\252\252\252\377" + "'''\240\0\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377\377\377\377\377|||\377\377" + "\377\377\377\205\344\344\344\377\21\211\211\211\377\255\255\255\377\377" + "\377\377\377\372\373\373\377\366\366\366\377\40\40\40\377\0\0\0\377\216" + "\217\217\377\367\367\364\377\377\377\377\377\226\226\226\377%%%\213\0" + "\0\0\2\0\0\0\0\0\0\0\2\0\0\0\377sss\377\207\274\274\274\377\2\220\220" + "\220\377rrr\377\203\377\377\377\377\3\361\360\361\377\341\341\341\377" + "\330\326\331\377\202\377\377\377\377\6YYY\363\31\31\31Y\0\0\0\2\0\0\0" + "\0\0\0\0\2\0\0\0m\210\0\0\0\377\3\2\2\2\377\6\6\6\377\232\232\232\377" + "\206\377\377\377\377\11\215\216\216\373\0\0\0\255\10\7\10'\0\0\0\2\0" + "\0\0\0\0\0\0\1\0\0\0\10\0\0\0\31\0\0\0(\206\0\0\0-\15\0\0\0:\0\0\0p\6" + "\6\6\311vvv\366\345\344\344\377\371\371\371\377\364\364\367\377\336\336" + "\336\377jkk\366\0\0\0\314\0\0\0g\0\0\0\27\0\0\0\1\202\0\0\0\0\3\0\0\0" + "\2\0\0\0\10\0\0\0\16\206\0\0\0\21\14\0\0\0\40\0\0\0A\0\0\0w\0\0\0\254" + "\26\26\26\323(((\343%%%\344\21\21\21\326\0\0\0\262\0\0\0\200\0\0\0F\0" + "\0\0\26\204\0\0\0\0\1\0\0\0\1\207\0\0\0\2\15\0\0\0\14\0\0\0\35\0\0\0" + "/\1\1\1>\6\6\6R\7\7\7`\6\6\6b\4\4\4[\0\0\0J\0\0\0""9\0\0\0#\0\0\0\16" + "\0\0\0\0"}; + + diff --git a/src/icons/stock_mail_send_queue.png b/src/icons/stock_mail_send_queue.png new file mode 100644 index 0000000..8754c5d Binary files /dev/null and b/src/icons/stock_mail_send_queue.png differ diff --git a/src/icons/stock_notspam.h b/src/icons/stock_notspam.h new file mode 100644 index 0000000..9923722 --- /dev/null +++ b/src/icons/stock_notspam.h @@ -0,0 +1,81 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_notspam) +#endif +#ifdef __GNUC__ +static const guint8 stock_notspam[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_notspam[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1250) */ + "\0\0\4\372" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\346\0\0\0\0\6\22\23\23<\35\36\36\346389\3759\77@\363156X\22\25\25!\222" + "\0\0\0\0\7\27\30\31\312\262\270\271\337\325\343\347\377\252\272\275\375" + "dij\365\35\36\36\346\0\0\0\40\221\0\0\0\0\13\14\15\15\311\222\234\236" + "\360\347\360\363\377\355\360\361\377\345\355\357\377\305\331\335\377" + ".12\365\77CC\203\34\35\36N\32\33\33P\0\0\0\4\215\0\0\0\0\15""3557\37" + "!!\361\337\353\356\377\267\304\310\377\322\331\332\377\341\357\362\377" + "\266\313\317\374\377\250\302\310\377" + "\262\275\300\377\317\345\351\377\302\331\336\377\201\230\234\377\242" + "\300\305\377\205\251\261\377\345\354\356\377\347\362\364\377\233\256" + "\263\377\310\341\347\377:>\77\355\14\16\16\201\206\0\0\0\0\22\0\0\0""0" + "\0\0\0\377377\377\263\302\305\377\335\355\360\377\330\344\346\377\341" + "\357\362\377\332\354\357\377\325\351\355\377\322\347\354\377\225\255" + "\262\377\333\342\342\377\355\357\360\377\242\261\263\377\217\244\250" + "\377\327\342\345\377y\217\224\373\25\30\31\311\204\0\0\0\0\1\0\0\0""0" + "\202\0\0\0\377\21\363\265\247\377\206^U\377\257\271\273\377\361\370\371" + "\377\367\373\373\377\331\353\357\377\354\364\366\377\363\367\367\377" + "\351\362\364\377\356\362\363\377\351\355\356\377\305\310\311\377\302" + "\306\310\377\340\347\347\377\335\352\355\377z\235\245\377\31\36\37\375" + "\204\0\0\0\0\15\0\0\0\377\363\265\247\377\353\201i\377\2706\30\377\222" + "N<\377\237\245\246\377\343\356\360\377\354\365\367\377\326\352\355\377" + "\313\344\351\377\331\347\351\377\341\353\355\377\325\232\231\377\210" + "\244\0\0\377\1\212\0\0\232\202\0\0\0\0\15\0\0\0\377\2645\30\377\347j" + "M\377\353\201i\377\265\77$\377\313pZ\377\304\330\333\377\322\346\351" + "\377\312\343\350\377\251\310\317\377\177\244\252\377\202\264\276\377" + "\244\0\0\377\207\337B\36\377\5\335;\33\377\244\0\0\377\0\0\0""0\0\0\0" + "\0\0\0\0\377\203\2645\30\377\14\347jM\377\361\250\227\377\347eG\377\305" + "\213}\377\250\253\254\377p\241\253\377\210\265\276\377\225\307\321\377" + "\244\0\0\377\337B\36\377\377\377\377\377\370\331\322\377\202\314\0\0" + "\377\7\370\331\322\377\377\377\377\377\331.\25\377\244\0\0\377\0\0\0" + "\377\0\0\0\0\0\0\0\377\205\2645\30\377\11\347jM\377\361\250\227\377\341" + "pU\377\335\215z\377\330\347\352\377\330\351\355\377\244\0\0\377\337B" + "\36\377\370\331\322\377\204\377\377\377\377\6\370\331\322\377\331.\25" + "\377\244\0\0\377\0\0\0\377\0\0\0\1\0\0\0""7\202\0\0\0\377\205\2645\30" + "\377\7\347jM\377\361\250\227\377\347eG\377\345\242\223\377\244\0\0\377" + "\337B\36\377\314\0\0\377\204\377\377\377\377\10\314\0\0\377\331.\25\377" + "\244\0\0\377\0\0\0\377\0\0\0\2\0\0\0\3\0\0\0\34\0\0\0d\202\0\0\0\377" + "\205\2645\30\377\5\347jM\377\361\250\227\377\244\0\0\377\337B\36\377" + "\314\0\0\377\204\377\377\377\377\12\314\0\0\377\331.\25\377\244\0\0\377" + "\0\0\0g\0\0\0\3\0\0\0\0\0\0\0\6\0\0\0\24\0\0\0(\0\0\0}\202\0\0\0\377" + "\205\2645\30\377\3\244\0\0\377\337B\36\377\370\331\322\377\204\377\377" + "\377\377\5\370\331\322\377\331.\25\377\244\0\0\377\0\0\0\21\0\0\0\1\202" + "\0\0\0\0\5\0\0\0\1\0\0\0\6\0\0\0\20\0\0\0&\3\5\5\212\202\0\0\0\377\1" + "\242C.\377\202\2645\30\377\4\244\0\0\377\337B\36\377\377\377\377\377" + "\370\331\322\377\202\314\0\0\377\5\370\331\322\377\377\377\377\377\331" + ".\25\377\244\0\0\377\0\0\0\5\206\0\0\0\0\4\0\0\0\11\0\0\0\35\0\0\0""8" + "\0\0\0\210\202\0\0\0\377\3\2645\30\377\244\0\0\377\335;\33\377\207\331" + ".\25\377\1\244\0\0\377\210\0\0\0\0\7\0\0\0\3\0\0\0\16\0\0\0\36\0\0\0" + "3\0\0\0\210\0\0\0\377Q\0\0\377\210\244\0\0\377\1\243\0\0\252\212\0\0" + "\0\0\13\0\0\0\3\0\0\0\16\0\0\0\37\0\0\0/\0\0\0""6\0\0\0""3\0\0\0)\0\0" + "\0\31\0\0\0\13\0\0\0\3\0\0\0\1\217\0\0\0\0\2\0\0\0\3\0\0\0\10\202\0\0" + "\0\11\2\0\0\0\6\0\0\0\2\210\0\0\0\0"}; + + diff --git a/src/icons/stock_notspam.png b/src/icons/stock_notspam.png new file mode 100644 index 0000000..0801b6d Binary files /dev/null and b/src/icons/stock_notspam.png differ diff --git a/src/icons/stock_outbox.h b/src/icons/stock_outbox.h new file mode 100644 index 0000000..09be125 --- /dev/null +++ b/src/icons/stock_outbox.h @@ -0,0 +1,57 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_outbox) +#endif +#ifdef __GNUC__ +static const guint8 stock_outbox[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_outbox[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (703) */ + "\0\0\2\327" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (64) */ + "\0\0\0@" + /* width (16) */ + "\0\0\0\20" + /* height (16) */ + "\0\0\0\20" + /* pixel_data: */ + "\206\0\0\0\0\1\0\0\0.\202\0\0\0\377\1\0\0\0\23\212\0\0\0\0\7\0\0\0.\0" + "\0\0\377\236\235\232\377\333\332\331\377\366\366\366\377\0\0\0\377\0" + "\0\0\23\207\0\0\0\0\12\0\0\0.\0\0\0\377\244\243\240\377\342\341\340\377" + "{zw\377)('\377\250\247\244\377\363\363\363\377\0\0\0\377\0\0\0\23\204" + "\0\0\0\0\5\0\0\0.664\377\262\261\256\377\342\341\340\377\312\312\310" + "\377\202\14\14\14\377\6'&%\377jif\377\327\326\325\377\363\363\363\377" + "\0\0\0\377\0\0\0\23\203\0\0\0\0\16\0\0\0\377\372\372\372\377\304\304" + "\302\377\0\0\0\377\12\12\12\377\26\26\26\377AA\77\377pol\377{zw\377\207" + "\206\203\377\337\336\335\377\363\363\363\377\0\0\0\377\0\0\0\23\202\0" + "\0\0\0\21\0\0\0\377\377\377\377\377\344\343\342\377AA\77\377CCA\377]" + "\\Y\377pol\377~}z\377\213\212\207\377\224\223\220\377\227\226\223\377" + "\337\336\335\377\363\363\363\377\0\0\0\377\0\0\0\23\0\0\0\0\0\0\0\377" + "\202\377\377\377\377\15\344\343\342\377&%$\377]\\Y\377rqn\377\206\205" + "\202\377\225\224\221\377\231\230\225\377\232\231\226\377\235\234\231" + "\377\327\326\325\377\365\365\365\377\0\0\0\377\0\0\0\23\211\0\0\0\377" + "\6\204\203\200\377\236\235\232\377\241\240\235\377\244\243\240\377\337" + "\336\335\377\361\361\361\377\202\0\0\0\377\207\377\377\377\377\14\0\0" + "\0\377lkh\377\204\203\200\377\251\250\245\377\253\252\247\377\300\300" + "\276\377\327\326\325\377jif\377\0\0\0\377\377\377\377\377\357\357\357" + "\377\337B\36\377\203\357\357\357\377\10\314\314\314\377\0\0\0\377dc`" + "\377\205\204\201\377\264\264\262\377\321\320\317\377\207\206\203\377" + "RQN\377\202\0\0\0\377\1\377\377\377\377\203\337B\36\377\10\357\357\357" + "\377\337B\36\377\314\314\314\377\0\0\0\377_^[\377\264\264\262\377\221" + "\220\215\377UTQ\377\202\0\0\0\377\4\0\0\0""4\0\0\0\377\377\377\377\377" + "\357\357\357\377\204\337B\36\377\4\314\314\314\377\0\0\0\377rqn\377[" + "ZW\377\202\0\0\0\377\1\0\0\0C\202\0\0\0\0\2\0\0\0\377\377\377\377\377" + "\202\357\357\357\377\203\337B\36\377\1\314\314\314\377\203\0\0\0\377" + "\1\0\0\0C\204\0\0\0\0\3\0\0\0\377\377\377\377\377\357\357\357\377\204" + "\337B\36\377\3\314\314\314\377\0\0\0\377\0\0\0N\206\0\0\0\0\2\0\0\0\377" + "\377\377\377\377\206\314\314\314\377\1\30\30\30\377\207\0\0\0\0\202\0" + "\0\0\377\1\11\11\11\377\206\0\0\0\377\207\0\0\0\0"}; + + diff --git a/src/icons/stock_outbox.png b/src/icons/stock_outbox.png new file mode 100644 index 0000000..0455a35 Binary files /dev/null and b/src/icons/stock_outbox.png differ diff --git a/src/icons/stock_person.h b/src/icons/stock_person.h new file mode 100644 index 0000000..6c51137 --- /dev/null +++ b/src/icons/stock_person.h @@ -0,0 +1,63 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_person) +#endif +#ifdef __GNUC__ +static const guint8 stock_person[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_person[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (676) */ + "\0\0\2\274" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (64) */ + "\0\0\0@" + /* width (16) */ + "\0\0\0\20" + /* height (16) */ + "\0\0\0\20" + /* pixel_data: */ + "\205\0\0\0\0\2\210\212\205Y\210\212\205\277\202\210\212\205\364\2\210" + "\212\205\277\210\212\205Y\211\0\0\0\0\3\210\212\205\214\257\260\255\377" + "\363\363\362\377\202\376\376\376\377\3\363\363\362\377\257\260\255\377" + "\210\212\205\214\207\0\0\0\0\12\210\212\205Y\257\260\255\377\376\376" + "\376\377\370\370\367\377\371\371\371\377\373\373\373\377\371\371\371" + "\377\376\376\376\377\257\260\255\377\210\212\205Y\206\0\0\0\0\12\210" + "\212\205\277\363\363\362\377\363\364\363\377\363\363\362\377\371\372" + "\371\377\373\374\373\377\366\367\366\377\365\366\365\377\363\363\362" + "\377\210\212\205\277\206\0\0\0\0\12\210\212\205\364\376\376\376\377\353" + "\354\352\377\360\361\357\377\365\364\363\377\365\366\365\377\362\363" + "\362\377\356\357\356\377\376\376\376\377\210\212\205\364\206\0\0\0\0" + "\12\210\212\205\364\376\376\376\377\350\351\346\377\353\354\352\377\356" + "\357\355\377\357\357\356\377\355\355\353\377\351\353\351\377\376\376" + "\376\377\210\212\205\364\206\0\0\0\0\12\210\212\205\277\363\363\362\377" + "\356\356\355\377\345\347\344\377\347\351\346\377\350\351\347\377\347" + "\350\345\377\356\357\356\377\363\363\362\377\210\212\205\277\206\0\0" + "\0\0\12\211\213\206\224\257\260\255\377\376\376\376\377\354\355\353\377" + "\343\344\341\377\343\345\341\377\355\355\354\377\376\376\376\377\257" + "\260\255\377\211\213\206\224\205\0\0\0\0\5\214\216\211=\222\224\217\377" + "\272\273\270\377\257\260\255\377\363\363\362\377\202\376\376\376\377" + "\5\363\363\362\377\257\260\255\377\272\273\270\377\222\224\217\377\214" + "\216\211=\204\0\0\0\0\5\214\216\211\263\355\356\355\377\340\342\336\377" + "\272\274\267\377\233\235\230\377\202\213\215\210\377\5\233\234\230\377" + "\270\272\266\377\337\340\335\377\355\356\355\377\214\216\211\263\204" + "\0\0\0\0\7\214\216\211\361\376\376\376\377\334\335\332\377\335\336\332" + "\377\334\335\332\377\333\335\331\377\333\334\331\377\202\332\334\330" + "\377\3\332\333\327\377\376\376\376\377\214\216\211\361\204\0\0\0\0\2" + "\214\216\211\361\376\376\376\377\204\351\352\347\377\204\351\351\347" + "\377\2\376\376\376\377\214\216\211\361\204\0\0\0\0\2\214\216\211\373" + "\376\376\376\377\203\343\344\341\377\2\343\344\340\377\343\343\340\377" + "\203\342\343\340\377\2\376\376\376\377\214\216\211\373\204\0\0\0\0\7" + "\214\216\211\376\376\376\376\377\334\335\332\377\335\336\332\377\334" + "\335\332\377\333\335\331\377\333\334\331\377\202\332\334\330\377\3\332" + "\333\327\377\376\376\376\377\214\216\211\376\204\0\0\0\0\2\214\216\211" + "\361\357\360\356\377\210\376\376\376\377\2\357\360\356\377\214\216\211" + "\361\204\0\0\0\0\1\221\223\216\266\212\221\223\216\377\1\221\223\216" + "\243\202\0\0\0\0"}; + + diff --git a/src/icons/stock_person.png b/src/icons/stock_person.png new file mode 100644 index 0000000..f2169a8 Binary files /dev/null and b/src/icons/stock_person.png differ diff --git a/src/icons/stock_spam.h b/src/icons/stock_spam.h new file mode 100644 index 0000000..a4a0333 --- /dev/null +++ b/src/icons/stock_spam.h @@ -0,0 +1,86 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_spam) +#endif +#ifdef __GNUC__ +static const guint8 stock_spam[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_spam[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (1374) */ + "\0\0\5v" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (96) */ + "\0\0\0`" + /* width (24) */ + "\0\0\0\30" + /* height (24) */ + "\0\0\0\30" + /* pixel_data: */ + "\346\0\0\0\0\6\22\23\23<\35\36\36\346389\3759\77@\363156X\22\25\25!\222" + "\0\0\0\0\7\27\30\31\312\262\270\271\337\325\343\347\377\252\272\275\375" + "dij\365\35\36\36\346\0\0\0\40\221\0\0\0\0\13\14\15\15\311\222\234\236" + "\360\347\360\363\377\355\360\361\377\345\355\357\377\305\331\335\377" + ".12\365\77CC\203\34\35\36N\32\33\33P\0\0\0\4\215\0\0\0\0\15""3557\37" + "!!\361\337\353\356\377\267\304\310\377\322\331\332\377\341\357\362\377" + "\266\313\317\374\377\250\302\310\377" + "\262\275\300\377\317\345\351\377\302\331\336\377\201\230\234\377\242" + "\300\305\377\205\251\261\377\345\354\356\377\347\362\364\377\233\256" + "\263\377\310\341\347\377:>\77\355\14\16\16\201\206\0\0\0\0\22\0\0\0""0" + "\0\0\0\377377\377\263\302\305\377\335\355\360\377\330\344\346\377\341" + "\357\362\377\332\354\357\377\325\351\355\377\322\347\354\377\225\255" + "\262\377\333\342\342\377\355\357\360\377\242\261\263\377\217\244\250" + "\377\327\342\345\377y\217\224\373\25\30\31\311\204\0\0\0\0\1\0\0\0""0" + "\202\0\0\0\377\21\363\265\247\377\206^U\377\257\271\273\377\361\370\371" + "\377\367\373\373\377\331\353\357\377\354\364\366\377\363\367\367\377" + "\351\362\364\377\356\362\363\377\351\355\356\377\305\310\311\377\302" + "\306\310\377\340\347\347\377\335\352\355\377z\235\245\377\31\36\37\375" + "\204\0\0\0\0\26\0\0\0\377\363\265\247\377\353\201i\377\2706\30\377\222" + "N<\377\237\245\246\377\343\356\360\377\354\365\367\377\326\352\355\377" + "\313\344\351\377\331\347\351\377\341\353\355\377\317\345\351\377\270" + "\313\320\377\275\313\317\377\253\263\265\377\343\345\345\377\334\353" + "\357\377j\227\237\377\23\30\31\377\0\0\0\377\0\0\0""0\202\0\0\0\0\31" + "\0\0\0\377\2645\30\377\347jM\377\353\201i\377\265\77$\377\313pZ\377\304" + "\330\333\377\322\346\351\377\312\343\350\377\251\310\317\377\177\244" + "\252\377\202\264\276\377\342\357\360\377\323\347\352\377\257\325\336" + "\377\307\342\347\377\347\361\362\377\227\303\313\377c\177\206\377\31" + "\23\21\377\357\231\205\377\0\0\0\377\0\0\0""0\0\0\0\0\0\0\0\377\203\264" + "5\30\377\25\347jM\377\361\250\227\377\347eG\377\305\213}\377\250\253" + "\254\377p\241\253\377\210\265\276\377\225\307\321\377\255\324\334\377" + "\334\352\357\377\265\330\340\377\312\344\351\377\225\276\306\377f\226" + "\240\377XXW\377Q\31\14\377\331@\35\377\354\212t\377\0\0\0\377\0\0\0\0" + "\0\0\0\377\205\2645\30\377\23\347jM\377\361\250\227\377\341pU\377\335" + "\215z\377\330\347\352\377\330\351\355\377\326\352\356\377\331\353\356" + "\377\344\360\363\377\317\343\350\377[\224\240\377\\~\205\377=\"\35\377" + "x#\20\377\331@\35\377\352z`\377\0\0\0\377\0\0\0\1\0\0\0""7\202\0\0\0" + "\377\205\2645\30\377\23\347jM\377\361\250\227\377\347eG\377\345\242\223" + "\377\353\331\325\377\306\323\326\377y\237\250\377U\214\230\377Ehn\377" + "'\40\37\377]2(\377\270o]\377\361\245\223\377\357\231\205\377\0\0\0\377" + "\0\0\0\2\0\0\0\3\0\0\0\34\0\0\0d\202\0\0\0\377\205\2645\30\377\23\347" + "jM\377\361\250\227\377\345\215x\377\300\300\301\3772FK\377Jnu\377(!!" + "\377J.'\377\306\226\213\377\335\250\233\377_\34\15\377\0\0\0\377\0\0" + "\0g\0\0\0\3\0\0\0\0\0\0\0\6\0\0\0\24\0\0\0(\0\0\0}\202\0\0\0\377\205" + "\2645\30\377\14\362\257\240\377\243oc\3774\34\27\3770\32\25\377gLE\377" + "\344\256\241\377\215*\23\377\0\0\0\377\0\0\0\216\0\0\0""9\0\0\0\21\0" + "\0\0\1\202\0\0\0\0\5\0\0\0\1\0\0\0\6\0\0\0\20\0\0\0&\3\5\5\212\202\0" + "\0\0\377\1\242C.\377\202\2645\30\377\13\344\247\231\377\331|g\377\266" + "jY\377\262l\\\377\210(\22\377\0\0\0\377\0\0\0\216\0\0\0\77\0\0\0/\0\0" + "\0\31\0\0\0\5\206\0\0\0\0\4\0\0\0\11\0\0\0\35\0\0\0""8\0\0\0\210\202" + "\0\0\0\377\3\2645\30\377\333A\35\377\364\272\254\377\202\0\0\0\377\6" + "\0\0\0\213\0\0\0A\0\0\0""6\0\0\0\34\0\0\0\12\0\0\0\3\210\0\0\0\0\5\0" + "\0\0\3\0\0\0\16\0\0\0\36\0\0\0""3\0\0\0\210\203\0\0\0\377\6\0\0\0\217" + "\0\0\0J\0\0\0""1\0\0\0\33\0\0\0\16\0\0\0\4\214\0\0\0\0\13\0\0\0\3\0\0" + "\0\16\0\0\0\37\0\0\0/\0\0\0""6\0\0\0""3\0\0\0)\0\0\0\31\0\0\0\13\0\0" + "\0\3\0\0\0\1\217\0\0\0\0\2\0\0\0\3\0\0\0\10\202\0\0\0\11\2\0\0\0\6\0" + "\0\0\2\210\0\0\0\0"}; + + diff --git a/src/icons/stock_spam.png b/src/icons/stock_spam.png new file mode 100644 index 0000000..52364ab Binary files /dev/null and b/src/icons/stock_spam.png differ diff --git a/src/icons/stock_spam_16.h b/src/icons/stock_spam_16.h new file mode 100644 index 0000000..7285c1e --- /dev/null +++ b/src/icons/stock_spam_16.h @@ -0,0 +1,73 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_spam_16) +#endif +#ifdef __GNUC__ +static const guint8 stock_spam_16[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_spam_16[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (840) */ + "\0\0\3`" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (64) */ + "\0\0\0@" + /* width (16) */ + "\0\0\0\20" + /* height (16) */ + "\0\0\0\20" + /* pixel_data: */ + "\202\0\0\0\0\6\210\212\205<\210\212\205\346\210\212\205\375\210\212\205" + "\363\210\212\205Xz|w!\212\0\0\0\0\7\210\212\205\312\366\367\367\337\351" + "\360\362\377\324\334\336\375\210\212\205\365\210\212\205\346\210\212" + "\205\40\211\0\0\0\0\13\210\212\205\311\370\371\371\360\347\360\363\377" + "\355\360\361\377\345\355\357\377\357\364\365\377\210\212\205\365\210" + "\212\205\203\210\212\205/\210\212\205\4\210\212\205\2\205\0\0\0\0\10" + "\210\212\2057\210\212\205\361\366\371\372\377\267\304\310\377\322\331" + "\332\377\341\357\362\377\354\362\363\374\210\212\205\363\202\210\212" + "\205\361\177\210\212\205\220\210\212\205\300\210\212\205&\0\0\0\0\210" + "\212\205\16\210\212\205\33\210\212\205\265\210\212\205\365\365\366\366" + "\377\303\331\335\377\234\254\260\377\303\315\316\377\307\341\347\377" + "\330\342\344\377\360\363\364\377\361\365\366\377\210\212\205\376\320" + "\323\322\376\210\212\205\320\210\212\205\31\210\212\205S\210\212\205" + "\373\210\212\205\365\351\360\361\375\330\335\337\377\317\345\351\377" + "\302\331\336\377\201\230\234\377\242\300\305\377\205\251\261\377\345" + "\354\356\377\347\362\364\377\233\256\263\377\310\341\347\377\210\212" + "\205\355\210\212\205\201\210\212\205\324\271\273\270\376\335\355\360" + "\377\330\344\346\377\341\357\362\377\332\354\357\377\325\351\355\377" + "\322\347\354\377\225\255\262\377\333\342\342\377\355\357\360\377\242" + "\261\263\377\217\244\250\377\327\342\345\377\356\361\361\373\210\212" + "\205\311\210\212\205\327\365\366\366\376\361\370\371\377\367\373\373" + "\377\331\353\357\377\354\364\366\377\363\367\367\377\351\362\364\377" + "\356\362\363\377\351\355\356\377\305\310\311\377\302\306\310\377\340" + "\347\347\377\335\352\355\377\355\361\362\377\210\212\205\374\210\212" + "\205\342\363\363\364\375\343\356\360\377\354\365\367\377\326\352\355" + "\377\313\344\351\377\331\347\351\377\341\353\355\377\317\345\351\377" + "\270\313\320\377\275\313\317\377\253\263\265\377\343\345\345\377\334" + "\353\357\377\316\322\322\376\210\212\205\367\210\212\205\365\313\317" + "\316\374\304\330\333\377\322\346\351\377\312\343\350\377\251\310\317" + "\377\177\244\252\377\202\264\276\377\342\357\360\377\323\347\352\377" + "\257\325\336\377\307\342\347\377\347\361\362\377\344\357\361\377\250" + "\253\250\367\210\212\205\267\210\212\205X\251\255\252\376\331\347\351" + "\377\233\273\301\377\225\271\301\377p\241\253\377\210\265\276\377\225" + "\307\321\377\255\324\334\377\334\352\357\377\265\330\340\377\312\344" + "\351\377\225\276\306\377\315\322\321\377\210\212\205\346\210\212\205" + "|\210\212\2055\210\212\205\377\363\366\367\377\335\351\353\377\265\312" + "\317\377\311\335\341\377\330\347\352\377\330\351\355\377\326\352\356" + "\377\331\353\356\377\344\360\363\377\24\317\343\350\377\254\311\317\377" + "\254\256\253\373\210\212\205\311prn3y{v\12\210\212\205\222\210\212\205" + "\377\372\372\373\377\351\356\357\377\255\276\301\377\277\316\321\377" + "\312\324\327\377\321\330\332\377\306\324\327\375y\240\251\376\323\341" + "\344\377\326\330\326\371\210\212\205\344\210\212\205v\203\0\0\0\0\1\210" + "\212\205N\202\210\212\205\377\11\355\357\357\377\364\365\366\377\372" + "\372\373\377\373\374\374\377\373\373\373\373\326\327\326\373\274\275" + "\273\373\210\212\205\346\210\212\205\210\206\0\0\0\0\1\210\212\205\204" + "\203\210\212\205\376\5\210\212\205\373\210\212\205\353\210\212\205\323" + "\210\212\205h\210\212\205'\223\0\0\0\0"}; + + diff --git a/src/icons/stock_spam_16.png b/src/icons/stock_spam_16.png new file mode 100644 index 0000000..eadd20d Binary files /dev/null and b/src/icons/stock_spam_16.png differ diff --git a/src/icons/stock_sylpheed.h b/src/icons/stock_sylpheed.h new file mode 100644 index 0000000..7434d1d --- /dev/null +++ b/src/icons/stock_sylpheed.h @@ -0,0 +1,256 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_sylpheed) +#endif +#ifdef __GNUC__ +static const guint8 stock_sylpheed[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_sylpheed[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (4686) */ + "\0\0\22f" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (192) */ + "\0\0\0\300" + /* width (48) */ + "\0\0\0""0" + /* height (48) */ + "\0\0\0""0" + /* pixel_data: */ + "\377\0\0\0\0\377\0\0\0\0\350\0\0\0\0\15\5\5\273\10\5\5\2675\5\5\261]" + "\5\5\254y\5\5\247\207\4\4\243\211\4\4\242\202\4\4\242u\4\4\242b\4\4\243" + "K\4\4\2453\4\4\246\32\5\5\247\4\205\0\0\0\0\3\4\4\234\1\4\4\225\40\4" + "\4\223\36\231\0\0\0\0\27\5\5\276\"\5\5\266s\5\5\253\255\5\5\246\263\5" + "\5\244\263\5\5\240\263\5\5\234\263\4\4\231\263\4\4\225\263\4\4\222\263" + "\4\4\216\263\4\4\213\263\4\4\207\263\4\4\203\263\4\4\200\262\4\4\203" + "\242\4\4\205\222\3\3\205\211\3\3\202\212\3\3{\224\3\3o\250\3\3\206c\4" + "\4\230\4\227\0\0\0\0\27\5\5\302\12\5\5\272n\5\5\260\263\5\5\255\263\5" + "\5\254\237\5\5\254\216\5\5\252\205\5\5\247\207\5\5\244\215\4\4\237\230" + "\4\4\230\244\4\4\221\261\4\4\215\263\4\4\211\263\4\4\206\263\4\4\203" + "\263\4\4\177\263\4\4{\263\3\4x\263\3\3x\251\3\3\202\210\4\4\216T\4\4" + "\230\27\230\0\0\0\0\5\5\5\302\22\5\5\270\226\5\5\266\217\5\5\272@\5\5" + "\273\13\207\0\0\0\0\11\5\5\257\1\4\4\254\13\4\4\251\27\4\4\245\40\4\4" + "\242%\4\4\241%\4\4\240\35\4\4\240\17\4\4\240\1\232\0\0\0\0\4\5\5\303" + "\6\5\5\272\221\5\5\274Z\5\5\276\2\254\0\0\0\0\2\5\5\276V\5\5\273h\255" + "\0\0\0\0\3\5\5\303\1\5\5\270\223\5\5\277\11\244\0\0\0\0\2\0\0\0\1\0\0" + "\0\2\207\0\0\0\3\2\4\4\256!\4\4\267g\230\0\0\0\3\2\0\0\0\2\0\0\0\1\212" + "\0\0\0\0\1\0\0\0L\211\0\0\0\377\2\1\1*\377\0\0!\377\230\0\0\0\377\3\0" + "\0\0""8\0\0\0\12\0\0\0\2\210\0\0\0\0\7\0\0\0\1\0\0\0\377\325\325\325" + "\377\374\374\374\377\375\375\375\377\374\374\374\377\374\374\375\377" + "\203\375\375\375\377\13\374\374\374\377\276\276\352\377\336\336\360\377" + "\363\363\363\377\356\356\356\377\353\353\353\377\352\352\352\377\354" + "\354\354\377\357\357\357\377\362\362\362\377\364\364\364\377\204\365" + "\365\365\377\3\364\364\364\377\363\363\363\377\363\364\363\377\202\363" + "\363\363\377\202\364\364\364\377\2\365\365\365\377\366\366\366\377\202" + "\367\367\367\377\5\302\302\302\377\0\0\0\377\0\0\0\40\0\0\0\12\0\0\0" + "\1\207\0\0\0\0\35\0\0\0\2\0\0\0\377\374\373\374\377\370\370\370\377\370" + "\371\370\377\366\367\366\377\365\364\364\377\364\364\365\377\364\363" + "\364\377\362\362\362\377\361\361\361\377\306\305\345\377\302\304\340" + "\377\344\344\344\377\341\341\341\377\336\336\336\377\334\334\334\377" + "\337\337\337\377\340\340\340\377\340\341\341\377\264\264\327\377\206" + "\206\307\377~~\303\377nn\273\377QQ\251\377UT\251\377tt\270\377\251\250" + "\315\377\331\332\333\377\202\333\333\333\377\12\335\337\335\377\341\341" + "\341\377\342\342\342\377\330\330\331\377\342\342\342\377\235\235\235" + "\377\0\0\0\377\0\0\0""7\0\0\0\24\0\0\0\2\207\0\0\0\0)\0\0\0\3\0\0\0\377" + "\340\341\340\377\376\375\376\377\370\367\367\377\367\366\367\377\364" + "\364\364\377\363\364\364\377\363\363\363\377\361\361\361\377\357\356" + "\357\377\346\346\352\377\233\233\326\377\343\343\343\377\337\337\337" + "\377\336\337\336\377\336\336\336\377\336\336\335\377\324\324\335\377" + "\221\221\314\377\262\260\324\377\333\333\336\377\337\337\337\377\334" + "\334\334\377\326\326\331\377\242\243\311\377SS\244\377BA\226\377_^\250" + "\377\300\276\317\377\325\327\326\377\327\327\333\377\334\334\333\377" + "\340\340\340\377\333\333\335\377\337\337\337\377\233\234\233\377\0\0" + "\0\377\0\0\0@\0\0\0\30\0\0\0\3\207\0\0\0\0)\0\0\0\3\0\0\0\377\303\303" + "\303\377\377\377\377\377\374\373\374\377\365\366\365\377\364\364\364" + "\377\362\362\362\377\362\361\361\377\357\357\357\377\356\356\356\377" + "\353\353\353\377\240\240\326\377\317\317\336\377\337\336\336\377\335" + "\336\336\377\335\334\335\377\335\336\335\377\266\266\326\377\335\334" + "\336\377\340\342\340\377\336\336\334\377\333\333\333\377\331\327\331" + "\377\325\325\325\377\324\324\324\377\311\310\317\377ih\256\377@\77\220" + "\377II\226\377\262\262\307\377\316\320\320\377\325\325\325\377\325\327" + "\323\377\343\343\343\377\216\216\216\377\211\212\211\377\0\0\0\377\0" + "\0\0B\0\0\0\31\0\0\0\3\207\0\0\0\0)\0\0\0\3\0\0\0\377\350\350\350\377" + "\272\272\272\377\377\377\377\377\370\370\370\377\363\363\363\377\361" + "\362\361\377\360\360\361\377\357\357\357\377\354\354\355\377\351\351" + "\351\377\340\340\345\377\225\225\320\377\330\327\335\377\335\335\334" + "\377\334\334\333\377\336\334\336\377\337\336\337\377\335\336\335\377" + "\335\333\333\377\334\332\332\377\327\327\330\377\326\325\324\377\323" + "\323\323\377\321\317\321\377\315\315\316\377\304\305\311\377TS\236\377" + "<<\213\377LL\225\377\273\273\303\377\312\312\312\377\326\325\325\377" + "\272\272\272\377\244\244\244\377\247\247\247\377\0\0\0\377\0\0\0B\0\0" + "\0\31\0\0\0\3\207\0\0\0\0)\0\0\0\3\0\0\0\377\377\377\377\377\343\343" + "\341\377\265\265\265\377\377\377\377\377\366\366\366\377\362\362\362" + "\377\360\360\360\377\356\356\355\377\354\354\354\377\351\351\351\377" + "\345\345\345\377\333\334\337\377\246\246\321\377\312\312\327\377\332" + "\332\332\377\332\332\334\377\333\333\334\377\334\334\333\377\331\331" + "\331\377\322\323\326\377\270\272\317\377\323\323\323\377\320\321\321" + "\377\316\316\316\377\311\314\314\377\311\307\311\377\224\224\271\377" + ";;\207\377:;\204\377pp\246\377\306\306\307\377\253\253\253\377\217\220" + "\217\377\305\305\304\377\246\246\246\377\0\0\0\377\0\0\0B\0\0\0\31\0" + "\0\0\3\207\0\0\0\0)\0\0\0\3\0\0\0\377\377\377\377\377\370\370\370\377" + "\342\342\342\377\272\272\272\377\377\377\377\377\364\364\364\377\357" + "\357\356\377\354\354\354\377\352\352\351\377\350\347\347\377\345\345" + "\344\377\340\341\340\377\336\336\336\377\277\277\324\377\260\260\320" + "\377\251\251\316\377\234\235\312\377\222\222\306\377\217\217\304\377" + "\251\247\312\377\322\322\321\377\321\323\321\377\320\320\320\377\315" + "\317\316\377\316\316\316\377\311\310\311\377\271\270\300\377;:\206\377" + "88\201\377>>\203\377\237\237\245\377\204\205\204\377\256\260\256\377" + "\305\305\306\377\235\235\235\377\0\0\0\377\0\0\0B\0\0\0\31\0\0\0\3\207" + "\0\0\0\0)\0\0\0\3\0\0\0\377\377\377\377\377\370\370\370\377\370\370\367" + "\377\343\343\343\377\272\272\272\377\377\377\377\377\362\362\362\377" + "\354\354\354\377\350\350\350\377\347\346\346\377\343\343\343\377\337" + "\337\337\377\334\334\335\377\332\332\332\377\332\331\331\377\327\326" + "\326\377\326\326\327\377\321\323\326\377\326\324\326\377\323\322\322" + "\377\323\323\320\377\321\321\321\377\316\321\321\377\317\317\316\377" + "\315\315\314\377\313\314\311\377\307\307\305\377==\206\377;;\203\377" + "00u\377dd\177\377\242\240\240\377\265\266\265\377\300\277\277\377\232" + "\232\232\377\0\0\0\377\0\0\0B\0\0\0\31\0\0\0\3\207\0\0\0\0\16\0\0\0\3" + "\0\0\0\377\377\377\377\377\367\370\370\377\367\367\367\377\365\367\365" + "\377\343\343\343\377\272\272\272\377\377\377\377\377\356\356\356\377" + "\347\350\347\377\343\346\343\377\342\342\342\377\337\337\337\377\202" + "\333\333\333\377\31\330\330\327\377\326\326\326\377\324\326\324\377\325" + "\323\323\377\322\320\322\377\322\321\322\377\317\317\317\377\320\316" + "\316\377\317\317\317\377\316\316\320\377\320\320\317\377\314\317\317" + "\377\310\310\310\377>>\206\37722x\377''k\377ss\225\377\247\247\251\377" + "\256\256\256\377\271\271\271\377\227\230\227\377\0\0\0\377\0\0\0B\0\0" + "\0\31\0\0\0\3\207\0\0\0\0\3\0\0\0\3\0\0\0\377\377\377\377\377\202\367" + "\367\367\377$\365\365\365\377\363\365\365\377\335\333\335\377\264\264" + "\264\377\375\375\375\377\353\353\353\377\342\342\342\377\336\337\337" + "\377\335\334\335\377\331\332\331\377\327\330\331\377\325\326\327\377" + "\326\325\325\377\323\323\323\377\320\322\322\377\321\323\322\377\317" + "\320\320\377\315\317\317\377\320\315\315\377\320\317\320\377\321\317" + "\321\377\321\321\321\377\321\322\322\377\307\307\323\37755{\377''l\377" + "00s\377\177\177\237\377\243\242\243\377\250\250\250\377\264\262\262\377" + "\224\225\225\377\0\0\0\377\0\0\0B\0\0\0\31\0\0\0\3\207\0\0\0\0\20\0\0" + "\0\3\0\0\0\377\377\377\377\377\367\367\370\377\365\367\365\377\364\364" + "\364\377\362\362\362\377\343\341\353\377\254\254\316\377\201\201\253" + "\377\302\302\347\377\307\307\334\377\327\327\330\377\330\330\330\377" + "\327\327\327\377\326\326\326\377\202\323\323\323\377\1\322\321\322\377" + "\202\320\320\320\377\202\316\316\316\377\22\317\317\317\377\320\316\316" + "\377\317\320\317\377\325\325\322\377\333\333\333\377\213\213\250\377" + "**p\37744w\37755w\377\222\222\241\377\237\240\240\377\244\244\244\377" + "\260\256\260\377\223\223\223\377\0\0\0\377\0\0\0B\0\0\0\31\0\0\0\3\207" + "\0\0\0\0\27\0\0\0\3\0\0\0\377\377\377\377\377\367\367\367\377\364\364" + "\364\377\325\323\352\377zz\311\377II\257\377HG\255\377@@\244\37744\227" + "\377JJ\253\377LL\251\377uu\270\377\254\253\307\377\316\315\316\377\316" + "\316\317\377\315\317\315\377\316\316\317\377\317\316\316\377\314\317" + "\314\377\316\316\316\377\317\315\317\377\202\316\316\316\377\20\323\323" + "\320\377\340\336\336\377\263\263\263\377TU\211\37789|\377::|\377HH\207" + "\377\240\240\240\377\235\235\235\377\241\241\242\377\252\254\254\377" + "\221\221\221\377\0\0\0\377\0\0\0B\0\0\0\31\0\0\0\3\207\0\0\0\0)\0\0\0" + "\3\0\0\0\377\377\377\377\377\366\366\364\377\270\270\341\377NN\262\377" + "II\255\377\\\\\266\377\212\212\310\377\231\231\313\377\206\206\271\377" + "]]\237\377ff\267\377AA\236\377==\231\377\\\\\251\377\244\244\300\377" + "\307\307\311\377\310\313\310\377\311\312\311\377\312\312\312\377\313" + "\313\315\377\314\314\314\377\316\316\316\377\321\321\321\377\337\337" + "\337\377\255\255\253\377\213\214\221\377BB\204\377==\177\37799z\377{" + "{\241\377\243\243\243\377\233\234\234\377\241\241\242\377\254\253\254" + "\377\221\223\223\377\0\0\0\377\0\0\0B\0\0\0\31\0\0\0\3\207\0\0\0\0)\0" + "\0\0\3\0\0\0\377\376\376\376\377\342\342\356\377PP\261\377II\254\377" + "\225\225\316\377\334\334\336\377\332\332\332\377\323\323\323\377\317" + "\317\321\377\254\253\253\377\234\234\234\377\307\307\316\377\250\250" + "\314\377ww\266\377HH\232\377ee\247\377\263\261\277\377\302\305\305\377" + "\307\307\307\377\313\312\312\377\321\317\317\377\336\335\335\377\332" + "\332\332\377\260\260\260\377\222\223\222\377ii\232\377\77\77\201\377" + "<<}\377KK\210\377\245\246\247\377\237\237\237\377\236\237\236\377\242" + "\242\242\377\256\256\254\377\224\224\224\377\0\0\0\377\0\0\0B\0\0\0\31" + "\0\0\0\3\207\0\0\0\0)\0\0\0\3\0\0\0\377\376\376\376\377\271\271\336\377" + "JI\252\377\213\213\312\377\337\337\337\377\330\331\331\377\320\321\320" + "\377\313\313\313\377\271\271\271\377\216\220\220\377\302\302\302\377" + "\254\254\254\377\252\252\252\377\275\275\275\377\300\300\302\377\233" + "\233\276\377`_\247\377\225\224\274\377\314\314\314\377\322\322\322\377" + "\323\323\323\377\313\313\313\377\244\244\244\377\262\262\262\377\262" + "\262\311\37777z\3779:z\377@@~\377\231\233\254\377\246\246\246\377\241" + "\241\241\377\236\240\236\377\246\246\246\377\261\261\261\377\227\227" + "\227\377\0\0\0\377\0\0\0B\0\0\0\31\0\0\0\3\207\0\0\0\0)\0\0\0\3\0\0\0" + "\377\376\376\376\377\316\316\346\377HH\247\377\330\330\337\377\333\332" + "\332\377\322\322\322\377\312\312\312\377\271\271\271\377\232\232\232" + "\377\322\322\322\377\307\307\305\377\277\300\277\377\260\260\260\377" + "\254\254\254\377\244\245\244\377\246\246\246\377\243\243\244\377pp\234" + "\377^^\225\377\235\236\237\377\232\233\232\377\244\244\244\377\275\276" + "\275\377\264\265\305\377LL\216\377EE\206\37787v\377\216\215\245\377\255" + "\256\256\377\244\244\244\377\241\243\241\377\240\242\242\377\251\251" + "\251\377\266\266\266\377\233\233\233\377\0\0\0\377\0\0\0B\0\0\0\31\0" + "\0\0\3\207\0\0\0\0)\0\0\0\3\0\0\0\377\375\375\375\377\356\356\356\377" + "{{\301\377\315\315\333\377\327\327\327\377\316\320\320\377\273\273\273" + "\377\233\233\233\377\324\324\324\377\311\312\312\377\315\315\315\377" + "\315\313\313\377\313\313\313\377\312\313\312\377\313\312\313\377\312" + "\312\312\377\313\313\313\377\314\312\314\377\253\253\304\377vv\260\377" + "\316\320\323\377\322\322\322\377\252\252\304\377HI\213\377\77\77\200" + "\377MM\213\377\244\243\270\377\227\226\226\377\236\235\235\377\246\246" + "\246\377\242\244\242\377\246\247\247\377\260\260\260\377\272\272\273" + "\377\237\236\237\377\0\0\0\377\0\0\0B\0\0\0\31\0\0\0\3\207\0\0\0\0\16" + "\0\0\0\3\0\0\0\377\375\375\375\377\356\356\356\377\336\334\343\377\215" + "\215\305\377\323\323\326\377\300\302\300\377\236\236\236\377\327\327" + "\327\377\316\317\316\377\322\322\322\377\323\323\323\377\323\323\325" + "\377\202\323\323\323\377\31\320\320\320\377\320\317\320\377\317\320\317" + "\377\317\317\317\377\314\316\316\377\265\263\305\377yz\256\377vv\255" + "\377@@\201\377@@\200\377tt\250\377\272\272\300\377\273\273\273\377\262" + "\262\262\377\216\220\216\377\231\232\232\377\247\246\247\377\255\257" + "\255\377\266\267\267\377\300\300\300\377\242\242\242\377\0\0\0\377\0" + "\0\0B\0\0\0\31\0\0\0\3\207\0\0\0\0)\0\0\0\3\0\0\0\377\375\375\375\377" + "\356\354\354\377\350\350\350\377\324\324\334\377\201\201\267\377\214" + "\214\242\377\334\334\334\377\322\322\322\377\325\325\325\377\327\327" + "\327\377\333\331\331\377\333\333\333\377\333\332\333\377\332\332\332" + "\377\327\327\327\377\326\326\326\377\323\323\323\377\321\321\321\377" + "\272\272\310\377|{\257\377DD\207\377AA\202\377nn\244\377\253\254\274" + "\377\277\300\300\377\272\272\272\377\263\263\265\377\255\255\255\377" + "\253\253\253\377\215\215\215\377\243\241\241\377\265\266\265\377\277" + "\276\276\377\305\303\305\377\246\246\246\377\0\0\0\377\0\0\0B\0\0\0\31" + "\0\0\0\3\207\0\0\0\0)\0\0\0\3\0\0\0\377\375\375\375\377\355\355\355\377" + "\350\351\351\377\323\323\323\377\252\252\252\377\271\271\325\377\231" + "\231\303\377\253\252\312\377\315\315\326\377\335\335\335\377\335\337" + "\335\377\337\337\337\377\336\336\336\377\336\334\336\377\322\322\330" + "\377\262\262\315\377\216\214\274\377]]\236\377@@\204\377ZZ\232\377\213" + "\213\262\377\226\226\266\377\246\246\272\377\274\274\274\377\271\270" + "\270\377\265\265\263\377\260\260\260\377\256\256\257\377\257\257\255" + "\377\261\261\260\377\227\227\227\377\260\260\257\377\304\304\304\377" + "\311\307\307\377\252\252\250\377\0\0\0\377\0\0\0B\0\0\0\31\0\0\0\3\207" + "\0\0\0\0\6\0\0\0\3\0\0\0\377\376\376\376\377\356\356\356\377\332\332" + "\332\377\261\261\261\377\202\334\334\334\377!\333\332\333\377\325\325" + "\332\377\270\266\320\377\256\256\315\377\256\256\316\377\252\253\314" + "\377\244\244\313\377\230\230\304\377\223\223\300\377\236\236\304\377" + "\253\253\310\377\271\270\312\377\310\311\316\377\313\314\313\377\310" + "\310\306\377\302\302\302\377\230\227\261\377\262\262\266\377\266\266" + "\266\377\263\261\261\377\261\261\261\377\263\263\263\377\265\265\265" + "\377\271\271\273\377\276\277\277\377\242\242\243\377\271\273\273\377" + "\312\312\312\377\253\253\253\377\0\0\0\377\0\0\0B\0\0\0\31\0\0\0\3\207" + "\0\0\0\0\14\0\0\0\3\0\0\0\377\376\376\376\377\335\335\335\377\265\265" + "\265\377\364\366\364\377\345\343\343\377\340\340\340\377\337\337\337" + "\377\337\335\335\377\336\336\336\377\337\335\335\377\202\337\337\337" + "\377\33\336\336\340\377\336\335\335\377\334\334\334\377\333\333\333\377" + "\330\330\330\377\325\323\325\377\317\320\320\377\311\313\313\377\305" + "\305\305\377\301\301\277\377\271\272\272\377\237\237\261\377\265\266" + "\265\377\264\265\265\377\266\267\267\377\271\273\273\377\275\275\275" + "\377\301\301\303\377\305\305\305\377\311\311\311\377\247\247\247\377" + "\276\276\276\377\263\262\263\377\0\0\0\377\0\0\0B\0\0\0\31\0\0\0\3\207" + "\0\0\0\0\13\0\0\0\3\0\0\0\377\377\377\377\377\254\256\254\377\373\373" + "\373\377\353\353\353\377\351\351\351\377\345\345\345\377\342\342\342" + "\377\340\342\340\377\340\336\340\377\202\336\336\336\377\34\334\336\336" + "\377\335\335\335\377\333\333\334\377\333\333\333\377\331\331\331\377" + "\327\327\325\377\322\322\322\377\317\317\320\377\311\313\313\377\306" + "\306\306\377\301\301\301\377\275\276\275\377\273\273\273\377\272\272" + "\272\377\274\273\273\377\276\276\276\377\302\300\300\377\305\305\305" + "\377\310\310\310\377\312\313\312\377\312\312\312\377\315\315\315\377" + "\251\252\251\377\250\250\250\377\0\0\0\377\0\0\0B\0\0\0\31\0\0\0\3\207" + "\0\0\0\0\15\0\0\0\3\0\0\0\377\273\273\273\377\266\265\265\377\262\262" + "\262\377\251\251\251\377\250\250\250\377\252\252\252\377\250\250\250" + "\377\251\250\251\377\251\251\252\377\251\252\252\377\252\252\252\377" + "\203\253\253\253\377\3\253\253\252\377\253\253\253\377\252\253\253\377" + "\202\251\251\251\377\2\250\250\250\377\247\246\247\377\202\244\244\244" + "\377\20\244\243\244\377\245\245\245\377\247\247\247\377\253\251\253\377" + "\256\256\256\377\262\262\262\377\264\264\264\377\267\270\270\377\271" + "\271\271\377\274\274\274\377\276\276\275\377\224\224\224\377\0\0\0\377" + "\0\0\0A\0\0\0\31\0\0\0\3\207\0\0\0\0\2\0\0\0\2\0\0\0C\243\0\0\0\377\4" + "\0\0\0\177\0\0\0""8\0\0\0\24\0\0\0\2\207\0\0\0\0\6\0\0\0\1\0\0\0\12\0" + "\0\0!\0\0\0""7\0\0\0@\0\0\0A\236\0\0\0B\5\0\0\0@\0\0\0""8\0\0\0\"\0\0" + "\0\13\0\0\0\1\210\0\0\0\0\4\0\0\0\2\0\0\0\12\0\0\0\24\0\0\0\30\237\0" + "\0\0\31\4\0\0\0\30\0\0\0\24\0\0\0\13\0\0\0\3\212\0\0\0\0\2\0\0\0\1\0" + "\0\0\2\241\0\0\0\3\2\0\0\0\2\0\0\0\1\377\0\0\0\0\306\0\0\0\0"}; + + diff --git a/src/icons/stock_sylpheed.png b/src/icons/stock_sylpheed.png new file mode 100644 index 0000000..e70a0e3 Binary files /dev/null and b/src/icons/stock_sylpheed.png differ diff --git a/src/icons/stock_sylpheed_16.h b/src/icons/stock_sylpheed_16.h new file mode 100644 index 0000000..368fc09 --- /dev/null +++ b/src/icons/stock_sylpheed_16.h @@ -0,0 +1,67 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_sylpheed_16) +#endif +#ifdef __GNUC__ +static const guint8 stock_sylpheed_16[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_sylpheed_16[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (766) */ + "\0\0\3\26" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (64) */ + "\0\0\0@" + /* width (16) */ + "\0\0\0\20" + /* height (16) */ + "\0\0\0\20" + /* pixel_data: */ + "\227\0\0\0\0\10\265\265\346\377\214\214\325\377\203\203\316\377\220\220" + "\320\377\246\246\326\377\275\275\336\377\307\307\343\377\302\302\336" + "\377\206\0\0\0\0\11\317\317\362\377\241\241\343\377\301\301\352\377\312" + "\312\354\377\301\301\347\377\265\265\336\377\254\254\330\377\255\255" + "\325\377\277\277\336\377\207\0\0\0\0\1\310\310\360\377\212\0\0\0\0\2" + "\310\310\310\377\225\225\225\377\202\227\227\227\377\2\204\204\233\377" + "\214\214\225\377\202\224\224\224\377\206\225\225\225\377f\230\230\230" + "\377\0\0\0\0\223\223\223\377\367\367\367\377\367\366\367\377\365\365" + "\365\377\331\331\352\377\334\334\344\377\340\340\340\377\324\324\337" + "\377\277\276\332\377\261\261\321\377\213\213\300\377\267\267\317\377" + "\337\340\340\377\342\342\343\377\235\235\235\377\0\0\0\0\221\221\221" + "\377\345\345\345\377\365\365\365\377\360\360\360\377\344\344\351\377" + "\306\306\333\377\333\333\334\377\327\327\334\377\332\332\333\377\324" + "\324\325\377\305\305\314\377^]\236\377\236\237\271\377\311\311\310\377" + "\177\177\177\377\0\0\0\0\227\227\227\377\357\357\357\377\342\342\342" + "\377\360\360\360\377\350\347\347\377\333\333\337\377\312\311\326\377" + "\277\277\322\377\301\300\320\377\320\321\321\377\315\315\315\377\212" + "\211\253\377NN\210\377\236\236\240\377\215\215\215\377\0\0\0\0\227\227" + "\227\377\370\371\371\377\352\353\353\377\326\326\334\377\337\340\344" + "\377\334\334\334\377\327\327\327\377\323\323\323\377\320\320\321\377" + "\317\316\317\377\321\321\321\377\213\213\253\377>>z\377\237\236\245\377" + "\204\204\204\377\0\0\0\0\227\227\227\377\356\356\364\377\213\213\313" + "\377sr\273\377oo\263\377||\266\377\266\266\307\377\314\315\315\377\315" + "\315\315\377\317\316\316\377\311\311\310\377[\\\214\377ZZ\212\377\237" + "\237\240\377\177\177\200\377\0\0\0\0\227\227\227\377\243\243\324\377" + "\254\254\317\377\321\321\321\377\262\262\262\377\263\263\272\377\222" + "\222\261\377\224\224\264\377\276\276\303\377\305\305\305\377\221\221" + "\247\377FF\202\377\224\225\242\377\241\242\242\377\203\203\203\377\0" + "\0\0\0\227\227\227\377\314\314\342\377\307\307\323\377\272\272\272\377" + "\202\313\313\313\377,\306\306\306\377\301\301\304\377\240\241\272\377" + "\226\227\262\377__\225\377\224\224\246\377\237\237\237\377\253\254\253" + "\377\213\213\213\377\0\0\0\0\227\227\227\377\355\355\355\377\266\266" + "\304\377\276\276\316\377\317\317\326\377\322\322\331\377\310\310\324" + "\377\253\253\304\377\203\203\253\377\203\203\251\377\267\267\272\377" + "\261\261\262\377\250\250\250\377\264\264\263\377\223\223\223\377\0\0" + "\0\0\227\227\227\377\333\334\333\377\343\343\343\377\337\337\337\377" + "\326\325\333\377\323\323\333\377\316\316\327\377\316\316\323\377\312" + "\313\314\377\275\275\277\377\263\264\267\377\271\271\271\377\301\302" + "\302\377\277\300\300\377\217\217\217\377\0\0\0\0\256\256\256\377ppp\377" + "\202iii\377\1ijj\377\203jjj\377\1iii\377\202ggg\377\4jjj\377mnn\377p" + "pp\377ggg\377\221\0\0\0\0"}; + + diff --git a/src/icons/stock_sylpheed_16.png b/src/icons/stock_sylpheed_16.png new file mode 100644 index 0000000..8b02fee Binary files /dev/null and b/src/icons/stock_sylpheed_16.png differ diff --git a/src/icons/stock_sylpheed_32.h b/src/icons/stock_sylpheed_32.h new file mode 100644 index 0000000..9c8c33b --- /dev/null +++ b/src/icons/stock_sylpheed_32.h @@ -0,0 +1,157 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_sylpheed_32) +#endif +#ifdef __GNUC__ +static const guint8 stock_sylpheed_32[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_sylpheed_32[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (2565) */ + "\0\0\12\35" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (128) */ + "\0\0\0\200" + /* width (32) */ + "\0\0\0\40" + /* height (32) */ + "\0\0\0\40" + /* pixel_data: */ + "\321\0\0\0\0\3\265\264\345\377\252\256\344\377\262\261\342\377\232\0" + "\0\0\0\3\211\210\327\377ij\312\377__\304\377\203WX\274\377\12X\\\271" + "\377c_\270\377gf\270\377kk\270\377ru\273\377\205\203\305\377\216\216" + "\312\377\225\221\310\377\205\206\301\377\212\216\302\377\216\0\0\0\0" + "\4\210\204\332\377ji\317\377y{\326\377\217\215\335\377\202\227\224\327" + "\377\2\210\212\322\377|w\300\377\202pp\275\377\202kk\270\377\4jm\262" + "\377kj\252\377tr\262\377\212\216\302\377\217\0\0\0\0\2\217\215\335\377" + "\260\263\352\377\235\0\0\0\0\2\245\241\346\377\314\313\360\377\236\0" + "\0\0\0\1\272\271\353\377\236\0\0\0\0\2\237\233\305\377\247\252\306\377" + "\226\0\0\0\0\2ab`\377^`]\377\205dec\377\4^_h\377SPv\377^_h\377^`]\377" + "\202\\^[\377\1^`]\377\214ab`\377\3ceb\377^`]\377\\^[\377\203\0\0\0\0" + "\4`]a\377\365\363\367\377\371\366\373\377\371\373\370\377\202\371\366" + "\373\377\23\365\367\364\377\351\352\364\377\313\306\345\377\336\337\351" + "\377\355\353\357\377\336\333\340\377\355\353\357\377\336\333\340\377" + "\355\353\357\377\305\310\345\377\254\247\322\377\262\261\342\377\225" + "\221\310\377\223\227\314\377\264\263\327\377\331\331\344\377\344\336" + "\335\377\355\353\357\377\341\346\351\377\202\355\353\357\377\2\336\333" + "\340\377fge\377\203\0\0\0\0\35`]a\377\362\364\361\377\371\373\370\377" + "\365\363\367\377\362\364\361\377\357\362\356\377\375\367\366\377\346" + "\354\356\377\305\310\345\377\312\312\341\377\334\336\332\377\337\341" + "\335\377\334\336\332\377\330\335\340\377\263\257\315\377\271\265\323" + "\377\313\313\325\377\307\307\322\377\304\310\327\377\201}\262\377b\\" + "\250\377kj\252\377\265\265\314\377\323\326\322\377\337\331\330\377\334" + "\336\332\377\324\331\333\377\322\324\320\377`]a\377\203\0\0\0\0\35QR" + "P\377\337\341\335\377\371\373\370\377\365\367\364\377\365\363\367\377" + "\356\363\366\377\366\360\357\377\354\356\353\377\325\321\343\377\276" + "\271\330\377\322\323\335\377\336\333\340\377\334\336\332\377\304\310" + "\327\377\322\323\335\377\332\337\342\377\330\333\327\377\327\325\331" + "\377\323\326\322\377\314\321\323\377\247\252\306\377WZ\244\377DG\225" + "\377\247\247\275\377\317\315\321\377\333\326\324\377\330\333\327\377" + "\216\220\215\377TUS\377\203\0\0\0\0\4`]a\377\337\331\330\377\332\337" + "\342\377\371\373\370\377\202\357\362\356\377\1\354\356\353\377\202\346" + "\354\356\377\202\306\306\335\377\22\324\331\333\377\333\331\335\377\336" + "\333\340\377\337\341\335\377\334\336\332\377\324\321\326\377\322\317" + "\324\377\322\324\320\377\317\315\321\377\306\314\316\377\216\216\260" + "\377HD\216\377WW\225\377\252\256\276\377\306\310\305\377\260\256\262" + "\377\253\260\262\377`]a\377\203\0\0\0\0\16fge\377\375\367\366\377\327" + "\325\331\377\336\333\340\377\375\367\366\377\356\363\366\377\366\360" + "\357\377\352\345\343\377\341\346\351\377\332\337\342\377\315\315\327" + "\377\306\302\324\377\302\277\320\377\271\265\323\377\202\263\257\315" + "\377\15\273\300\317\377\317\315\321\377\322\324\320\377\321\314\312\377" + "\311\316\321\377\265\271\311\377LR\224\377@=\206\377}y\242\377\233\240" + "\242\377\260\253\251\377\272\274\271\377ab`\377\203\0\0\0\0\16efd\377" + "\365\367\364\377\357\362\356\377\330\333\327\377\330\335\340\377\366" + "\360\357\377\351\353\350\377\346\354\356\377\352\345\343\377\344\336" + "\335\377\336\333\340\377\327\325\331\377\322\323\335\377\317\315\321" + "\377\202\307\307\322\377\15\317\315\321\377\322\324\320\377\317\315\321" + "\377\311\316\321\377\321\314\312\377\315\307\306\377\\W\220\377:9\200" + "\377LK|\377\210\211\222\377\253\260\262\377\263\270\272\377`]a\377\203" + "\0\0\0\0\14fge\377\371\366\373\377\356\363\366\377\362\364\361\377\330" + "\333\327\377\330\335\340\377\366\360\357\377\346\350\345\377\341\343" + "\340\377\337\341\335\377\330\335\340\377\323\326\322\377\202\324\331" + "\333\377\3\327\325\331\377\316\323\326\377\322\317\324\377\202\315\317" + "\314\377\12\311\316\321\377\315\317\314\377\306\314\316\377\\W\220\377" + "-.s\377JF}\377\226\227\241\377\260\253\251\377\255\257\254\377XZW\377" + "\203\0\0\0\0\1hig\377\202\371\373\370\377\17\362\364\361\377\355\353" + "\357\377\304\301\305\377\304\310\327\377\331\331\344\377\324\331\333" + "\377\333\331\335\377\323\326\322\377\331\327\333\377\330\322\321\377" + "\323\316\314\377\315\317\314\377\323\316\314\377\315\317\314\377\317" + "\315\321\377\202\323\316\314\377\11\322\324\320\377\277\277\311\377I" + "J\207\377-.s\377WU\207\377\226\227\241\377\236\243\245\377\260\256\262" + "\377`]a\377\203\0\0\0\0\35fge\377\375\367\366\377\365\367\364\377\325" + "\325\354\377\231\235\322\377|w\300\377ba\263\377pp\275\377z\200\277\377" + "\223\223\302\377\265\265\314\377\314\321\323\377\316\323\326\377\324" + "\321\326\377\320\322\317\377\314\321\323\377\323\316\314\377\322\317" + "\324\377\314\321\323\377\324\321\326\377\317\315\321\377\214\217\252" + "\377=>\177\3779:{\377gd\213\377\233\240\242\377\236\240\235\377\236\243" + "\245\377TUS\377\203\0\0\0\0\35fge\377\371\373\370\377\317\316\346\377" + "ba\263\377WX\274\377io\301\377\200\206\306\377sp\244\377b\\\250\377U" + "S\252\377QR\242\377\201}\262\377\277\277\311\377\310\312\307\377\306" + "\314\316\377\321\314\312\377\306\314\316\377\312\314\311\377\323\316" + "\314\377\323\326\322\377\251\253\250\377gd\213\3779:{\377CE\200\377\202" + "\206\241\377\236\240\235\377\242\243\254\377\243\245\242\377`]a\377\203" + "\0\0\0\0\35fge\377\351\352\364\377ki\275\377gf\270\377\270\273\330\377" + "\333\326\324\377\327\325\331\377\275\302\305\377\247\251\245\377\270" + "\265\272\377\247\252\306\377\201}\262\377gf\246\377\210\210\266\377\265" + "\266\300\377\315\307\306\377\317\315\321\377\327\325\331\377\320\322" + "\317\377\260\256\262\377\202\206\241\377IJ\207\3779:{\377ij\226\377\242" + "\243\254\377\236\240\235\377\242\243\254\377\243\245\242\377VXU\377\203" + "\0\0\0\0\20efd\377\325\325\354\377ba\263\377\263\257\315\377\323\326" + "\322\377\314\321\323\377\306\310\305\377\260\253\251\377\263\270\272" + "\377\272\274\271\377\255\257\254\377\263\270\272\377\251\252\264\377" + "\211\211\253\377\204\200\251\377\247\247\275\377\202\272\274\271\377" + "\13\260\256\262\377\247\253\273\377ij\226\3779:{\377WU\207\377\233\240" + "\242\377\260\253\251\377\236\240\235\377\260\253\251\377\255\257\254" + "\377`]a\377\203\0\0\0\0\35fge\377\351\352\364\377\205\203\305\377\322" + "\323\335\377\333\326\324\377\304\301\305\377\253\260\262\377\303\275" + "\274\377\321\314\312\377\317\315\321\377\303\275\274\377\275\277\274" + "\377\272\274\271\377\263\270\272\377\247\253\273\377\211\215\265\377" + "\233\233\261\377\275\277\274\377\261\261\273\377mj\236\377GF\211\377" + "gd\230\377\220\215\235\377\241\243\240\377\242\243\254\377\241\243\240" + "\377\260\256\262\377\255\257\254\377\\^[\377\203\0\0\0\0\7`]a\377\365" + "\367\364\377\310\314\334\377\254\247\322\377\311\316\321\377\262\264" + "\261\377\306\300\277\377\202\314\321\323\377\24\323\316\314\377\314\321" + "\323\377\323\316\314\377\322\317\324\377\320\322\317\377\315\317\314" + "\377\277\277\311\377\236\236\301\377\210\210\266\377]_\227\377IJ\207" + "\377\206\202\253\377\261\261\273\377\260\256\262\377\223\225\222\377" + "\236\240\235\377\247\251\245\377\262\264\261\377\270\265\272\377`]a\377" + "\203\0\0\0\0\35fge\377\375\367\366\377\346\350\345\377\304\310\327\377" + "\211\215\265\377\265\271\311\377\317\315\321\377\327\325\331\377\324" + "\331\333\377\337\331\330\377\336\333\340\377\330\333\327\377\324\321" + "\326\377\313\313\325\377\277\277\311\377\235\231\266\377fh\240\377QQ" + "\217\377gl\236\377\265\266\300\377\272\274\271\377\270\265\272\377\260" + "\256\262\377\260\253\251\377\226\227\241\377\260\253\251\377\275\277" + "\274\377\302\304\301\377dec\377\203\0\0\0\0\27`]a\377\357\362\356\377" + "\344\346\343\377\306\300\277\377\304\301\305\377\272\272\321\377\265" + "\265\314\377\307\307\322\377\315\315\327\377\310\314\334\377\304\310" + "\327\377\300\277\327\377\265\265\314\377\236\236\301\377\210\210\266" + "\377tx\236\377\222\222\264\377\253\247\270\377\242\241\270\377\265\267" + "\263\377\277\271\270\377\255\257\254\377\253\260\262\377\202\260\253" + "\251\377\1\243\250\252\377\202\304\301\305\377\1`]a\377\203\0\0\0\0\10" + "hig\377\355\353\357\377\311\316\321\377\334\336\332\377\337\341\335\377" + "\344\336\335\377\331\327\333\377\310\314\334\377\202\313\313\325\377" + "\1\311\305\327\377\202\302\277\320\377\20\306\302\324\377\313\313\325" + "\377\317\315\321\377\306\314\316\377\302\304\301\377\261\261\273\377" + "\251\252\264\377\277\271\270\377\260\253\251\377\263\270\272\377\275" + "\277\274\377\306\300\277\377\270\272\267\377\260\262\256\377\275\277" + "\274\377jli\377\203\0\0\0\0\30`]a\377\323\326\322\377\337\341\335\377" + "\355\353\357\377\352\345\343\377\341\346\351\377\337\341\335\377\344" + "\336\335\377\334\336\332\377\332\337\342\377\334\336\332\377\330\335" + "\340\377\331\327\333\377\323\326\322\377\322\317\324\377\315\317\314" + "\377\315\307\306\377\304\301\305\377\272\274\271\377\270\265\272\377" + "\263\270\272\377\304\301\305\377\306\300\277\377\304\301\305\377\202" + "\306\314\316\377\3\317\315\321\377\253\260\262\377`]a\377\203\0\0\0\0" + "\7TUS\377\223\225\222\377\216\220\215\377\210\206\212\377\203\205\202" + "\377\210\206\212\377\203\210\213\377\202\210\206\212\377\6\214\212\216" + "\377\210\206\212\377\207\211\206\377\214\212\216\377\207\211\206\377" + "\210\206\212\377\202\207\211\206\377\4\210\206\212\377\203\205\202\377" + "\210\206\212\377\203\205\202\377\202\203\210\213\377\6\214\212\216\377" + "\216\220\215\377\220\222\217\377\223\225\222\377\220\222\217\377QRP\377" + "\203\0\0\0\0\2\242\243\254\377TUS\377\232KLJ\377\1hig\377\302\0\0\0\0"}; + + diff --git a/src/icons/stock_sylpheed_32.png b/src/icons/stock_sylpheed_32.png new file mode 100644 index 0000000..8300438 Binary files /dev/null and b/src/icons/stock_sylpheed_32.png differ diff --git a/src/icons/stock_sylpheed_newmail.h b/src/icons/stock_sylpheed_newmail.h new file mode 100644 index 0000000..5c7585e --- /dev/null +++ b/src/icons/stock_sylpheed_newmail.h @@ -0,0 +1,246 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_sylpheed_newmail) +#endif +#ifdef __GNUC__ +static const guint8 stock_sylpheed_newmail[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_sylpheed_newmail[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (4606) */ + "\0\0\22\26" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (192) */ + "\0\0\0\300" + /* width (48) */ + "\0\0\0""0" + /* height (48) */ + "\0\0\0""0" + /* pixel_data: */ + "\377\0\0\0\0\377\0\0\0\0\350\0\0\0\0\15\5\5\273\10\5\5\2675\5\5\261]" + "\5\5\254y\5\5\247\207\4\4\243\211\4\4\242\202\4\4\242u\4\4\242b\4\4\243" + "K\4\4\2453\4\4\246\32\5\5\247\4\205\0\0\0\0\3\4\4\234\1\4\4\225\40\4" + "\4\223\36\231\0\0\0\0\27\5\5\276\"\5\5\266s\5\5\253\255\5\5\246\263\5" + "\5\244\263\5\5\240\263\5\5\234\263\4\4\231\263\4\4\225\263\4\4\222\263" + "\4\4\216\263\4\4\213\263\4\4\207\263\4\4\203\263\4\4\200\262\4\4\203" + "\242\4\4\205\222\3\3\205\211\3\3\202\212\3\3{\224\3\3o\250\3\3\206c\4" + "\4\230\4\227\0\0\0\0\27\5\5\302\12\5\5\272n\5\5\260\263\5\5\255\263\5" + "\5\254\237\5\5\254\216\5\5\252\205\5\5\247\207\5\5\244\215\4\4\237\230" + "\4\4\230\244\4\4\221\261\4\4\215\263\4\4\211\263\4\4\206\263\4\4\203" + "\263\4\4\177\263\4\4{\263\3\4x\263\3\3x\251\3\3\202\210\4\4\216T\4\4" + "\230\27\230\0\0\0\0\5\5\5\302\22\5\5\270\226\5\5\266\217\5\5\272@\5\5" + "\273\13\207\0\0\0\0\11\5\5\257\1\4\4\254\13\4\4\251\27\4\4\245\40\4\4" + "\242%\4\4\241%\4\4\240\35\4\4\240\17\4\4\240\1\232\0\0\0\0\4\5\5\303" + "\6\5\5\272\221\5\5\274Z\5\5\276\2\254\0\0\0\0\2\5\5\276V\5\5\273h\255" + "\0\0\0\0\3\5\5\303\1\5\5\270\223\5\5\277\11\244\0\0\0\0\2\0\0\0\1\0\0" + "\0\2\207\0\0\0\3\2\4\4\256!\4\4\267g\230\0\0\0\3\2\0\0\0\2\0\0\0\1\212" + "\0\0\0\0\1\0\0\0L\211\0\0\0\377\2\1\1*\377\0\0!\377\230\0\0\0\377\3\0" + "\0\0""8\0\0\0\12\0\0\0\2\210\0\0\0\0\7\0\0\0\1\0\0\0\377\325\325\325" + "\377\374\374\374\377\375\375\375\377\374\374\374\377\374\374\375\377" + "\203\375\375\375\377\13\374\374\374\377\276\276\352\377\336\336\360\377" + "\363\363\363\377\356\356\356\377\353\353\353\377\352\352\352\377\354" + "\354\354\377\357\357\357\377\362\362\362\377\364\364\364\377\204\365" + "\365\365\377\3\364\364\364\377\363\363\363\377\363\364\363\377\202\363" + "\363\363\377\202\364\364\364\377\2\365\365\365\377\366\366\366\377\202" + "\367\367\367\377\5\302\302\302\377\0\0\0\377\0\0\0\40\0\0\0\12\0\0\0" + "\1\207\0\0\0\0\35\0\0\0\2\0\0\0\377\374\373\374\377\370\370\370\377\370" + "\371\370\377\366\367\366\377\365\364\364\377\364\364\365\377\364\363" + "\364\377\362\362\362\377\361\361\361\377\306\305\345\377\302\304\340" + "\377\344\344\344\377\341\341\341\377\336\336\336\377\334\334\334\377" + "\337\337\337\377\340\340\340\377\340\341\341\377\264\264\327\377\206" + "\206\307\377~~\303\377nn\273\377QQ\251\377UT\251\377tt\270\377\251\250" + "\315\377\331\332\333\377\202\333\333\333\377\12\335\337\335\377\341\341" + "\341\377\342\342\342\377\330\330\331\377\342\342\342\377\235\235\235" + "\377\0\0\0\377\0\0\0""7\0\0\0\24\0\0\0\2\207\0\0\0\0)\0\0\0\3\0\0\0\377" + "\340\341\340\377\376\375\376\377\370\367\367\377\367\366\367\377\364" + "\364\364\377\363\364\364\377\363\363\363\377\361\361\361\377\357\356" + "\357\377\346\346\352\377\233\233\326\377\343\343\343\377\337\337\337" + "\377\336\337\336\377\336\336\336\377\336\336\335\377\324\324\335\377" + "\221\221\314\377\262\260\324\377\333\333\336\377\337\337\337\377\334" + "\334\334\377\326\326\331\377\242\243\311\377SS\244\377BA\226\377_^\250" + "\377\300\276\317\377\325\327\326\377\327\327\333\377\334\334\333\377" + "\340\340\340\377\333\333\335\377\337\337\337\377\233\234\233\377\0\0" + "\0\377\0\0\0@\0\0\0\30\0\0\0\3\207\0\0\0\0)\0\0\0\3\0\0\0\377\303\303" + "\303\377\377\377\377\377\374\373\374\377\365\366\365\377\364\364\364" + "\377\362\362\362\377\362\361\361\377\357\357\357\377\356\356\356\377" + "\353\353\353\377\240\240\326\377\317\317\336\377\337\336\336\377\335" + "\336\336\377\335\334\335\377\335\336\335\377\266\266\326\377\335\334" + "\336\377\340\342\340\377\336\336\334\377\333\333\333\377\331\327\331" + "\377\325\325\325\377\324\324\324\377\311\310\317\377ih\256\377@\77\220" + "\377II\226\377\262\262\307\377\316\320\320\377\325\325\325\377\325\327" + "\323\377\343\343\343\377\216\216\216\377\211\212\211\377\0\0\0\377\0" + "\0\0B\0\0\0\31\0\0\0\3\207\0\0\0\0)\0\0\0\3\0\0\0\377\350\350\350\377" + "\272\272\272\377\377\377\377\377\370\370\370\377\363\363\363\377\361" + "\362\361\377\360\360\361\377\357\357\357\377\354\354\355\377\351\351" + "\351\377\340\340\345\377\225\225\320\377\330\327\335\377\335\335\334" + "\377\334\334\333\377\336\334\336\377\337\336\337\377\335\336\335\377" + "\335\333\333\377\334\332\332\377\327\327\330\377\326\325\324\377\323" + "\323\323\377\321\317\321\377\315\315\316\377\304\305\311\377TS\236\377" + "<<\213\377LL\225\377\273\273\303\377\312\312\312\377\326\325\325\377" + "\272\272\272\377\244\244\244\377\247\247\247\377\0\0\0\377\0\0\0B\0\0" + "\0\31\0\0\0\3\207\0\0\0\0)\0\0\0\3\0\0\0\377\377\377\377\377\343\343" + "\341\377\265\265\265\377\377\377\377\377\366\366\366\377\362\362\362" + "\377\360\360\360\377\356\356\355\377\354\354\354\377\351\351\351\377" + "\345\345\345\377\333\334\337\377\246\246\321\377\312\312\327\377\332" + "\332\332\377\332\332\334\377\333\333\334\377\334\334\333\377\331\331" + "\331\377\322\323\326\377\270\272\317\377\323\323\323\377\320\321\321" + "\377\316\316\316\377\311\314\314\377\311\307\311\377\224\224\271\377" + ";;\207\377:;\204\377pp\246\377\306\306\307\377\253\253\253\377\217\220" + "\217\377\305\305\304\377\246\246\246\377\0\0\0\377\0\0\0B\0\0\0\31\0" + "\0\0\3\207\0\0\0\0)\0\0\0\3\0\0\0\377\377\377\377\377\370\370\370\377" + "\342\342\342\377\272\272\272\377\377\377\377\377\364\364\364\377\357" + "\357\356\377\354\354\354\377\352\352\351\377\350\347\347\377\345\345" + "\344\377\340\341\340\377\336\336\336\377\277\277\324\377\260\260\320" + "\377\251\251\316\377\234\235\312\377\222\222\306\377\217\217\304\377" + "\251\247\312\377\322\322\321\377\321\323\321\377\320\320\320\377\315" + "\317\316\377\316\316\316\377\311\310\311\377\271\270\300\377;:\206\377" + "88\201\377>>\203\377\237\237\245\377\204\205\204\377\256\260\256\377" + "\305\305\306\377\235\235\235\377\0\0\0\377\0\0\0B\0\0\0\31\0\0\0\3\207" + "\0\0\0\0)\0\0\0\3\0\0\0\377\377\377\377\377\370\370\370\377\370\370\367" + "\377\343\343\343\377\272\272\272\377\377\377\377\377\362\362\362\377" + "\354\354\354\377\350\350\350\377\347\346\346\377\343\343\343\377\337" + "\337\337\377\334\334\335\377\332\332\332\377\332\331\331\377\327\326" + "\326\377\326\326\327\377\321\323\326\377\326\324\326\377\323\322\322" + "\377\323\323\320\377\321\321\321\377\316\321\321\377\317\317\316\377" + "\315\315\314\377\313\314\311\377\307\307\305\377==\206\377;;\203\377" + "00u\377dd\177\377\242\240\240\377\265\266\265\377\300\277\277\377\232" + "\232\232\377\0\0\0\377\0\0\0B\0\0\0\31\0\0\0\3\207\0\0\0\0\16\0\0\0\3" + "\0\0\0\377\377\377\377\377\367\370\370\377\367\367\367\377\365\367\365" + "\377\343\343\343\377\272\272\272\377\377\377\377\377\356\356\356\377" + "\347\350\347\377\343\346\343\377\342\342\342\377\337\337\337\377\202" + "\333\333\333\377\31\330\330\327\377\326\326\326\377\324\326\324\377\325" + "\323\323\377\322\320\322\377\322\321\322\377\317\317\317\377\320\316" + "\316\377\317\317\317\377\316\316\320\377\320\320\317\377\314\317\317" + "\377\310\310\310\377>>\206\37722x\377''k\377ss\225\377\247\247\251\377" + "\256\256\256\377\271\271\271\377\227\230\227\377\0\0\0\377\0\0\0B\0\0" + "\0\31\0\0\0\3\207\0\0\0\0\3\0\0\0\3\0\0\0\377\377\377\377\377\202\367" + "\367\367\377%\365\365\365\377\363\365\365\377\335\333\335\377\264\264" + "\264\377\375\375\375\377\353\353\353\377\342\342\342\377\336\337\337" + "\377\335\334\335\377\331\332\331\377\327\330\331\377\325\326\327\377" + "\326\325\325\377\322\322\322\377\316\320\320\377\316\320\317\377\314" + "\315\315\377\312\314\314\377\315\312\312\377\315\314\315\377\316\314" + "\316\377\316\316\316\377\316\317\317\377\304\304\320\37744y\377&&j\377" + "//q\377}}\235\377\241\240\241\377\246\246\246\377\261\257\257\377\222" + "\223\223\377\0\0\0\377\0\0\0D\0\0\0\34\0\0\0\5\0\0\0\1\206\0\0\0\0\22" + "\0\0\0\3\0\0\0\377\377\377\377\377\367\367\370\377\365\367\365\377\364" + "\364\364\377\362\362\362\377\343\341\353\377\254\254\316\377\201\201" + "\253\377\302\302\347\377\307\307\334\377\327\327\330\377\330\330\330" + "\377\327\327\327\377\326\326\326\377\323\323\323\377\322\322\322\377" + "\226\0\0\0\377\3\0\0\0\23\0\0\0\6\0\0\0\1\205\0\0\0\0\22\0\0\0\3\0\0" + "\0\377\377\377\377\377\367\367\367\377\364\364\364\377\325\323\352\377" + "zz\311\377II\257\377HG\255\377@@\244\37744\227\377JJ\253\377LL\251\377" + "uu\270\377\254\253\307\377\316\315\316\377\315\315\316\377\311\313\311" + "\377\226\0\0\0\377\4\0\0\0-\0\0\0\22\0\0\0\4\0\0\0\1\204\0\0\0\0\20\0" + "\0\0\3\0\0\0\377\377\377\377\377\366\366\364\377\270\270\341\377NN\262" + "\377II\255\377\\\\\266\377\212\212\310\377\231\231\313\377\206\206\271" + "\377]]\237\377ff\267\377AA\236\377==\231\377[[\250\377\202\0\0\0\377" + "\202\325((\377\202\365\316\315\377\202\365\317\316\377\202\365\316\315" + "\377\202\364\316\313\377\202\364\315\313\377\206\365\316\315\377\202" + "\364\315\313\377\202\234\35\35\377\202\0\0\0\377\2\0\0\0\22\0\0\0\6\204" + "\0\0\0\0\20\0\0\0\3\0\0\0\377\376\376\376\377\342\342\356\377PP\261\377" + "II\254\377\225\225\316\377\334\334\336\377\332\332\332\377\323\323\323" + "\377\317\317\321\377\254\253\253\377\234\234\234\377\307\307\316\377" + "\250\250\314\377vv\264\377\202\0\0\0\377\202\325((\377\202\365\316\315" + "\377\202\365\317\316\377\202\365\316\315\377\202\364\316\313\377\202" + "\364\315\313\377\206\365\316\315\377\202\364\315\313\377\202\234\35\35" + "\377\202\0\0\0\377\2\0\0\0+\0\0\0\20\204\0\0\0\0\20\0\0\0\3\0\0\0\377" + "\376\376\376\377\271\271\336\377JI\252\377\213\213\312\377\337\337\337" + "\377\330\331\331\377\320\321\320\377\313\313\313\377\271\271\271\377" + "\216\220\220\377\302\302\302\377\254\254\254\377\252\252\252\377\272" + "\272\272\377\202\0\0\0\377\202\365\316\315\377\202\325((\377\202\364" + "\316\313\377\202\364\315\311\377\202\365\317\316\377\202\363\310\302" + "\377\202\364\316\313\377\202\363\310\304\377\202\364\312\307\377\202" + "\325((\377\202\345\203|\377\202\0\0\0\377\2\0\0\0""9\0\0\0\26\204\0\0" + "\0\0\20\0\0\0\3\0\0\0\377\376\376\376\377\316\316\346\377HH\247\377\330" + "\330\337\377\333\332\332\377\322\322\322\377\312\312\312\377\271\271" + "\271\377\232\232\232\377\322\322\322\377\307\307\305\377\277\300\277" + "\377\260\260\260\377\251\251\251\377\202\0\0\0\377\202\365\316\315\377" + "\202\325((\377\202\364\316\313\377\202\364\315\311\377\202\365\317\316" + "\377\202\363\310\302\377\202\364\316\313\377\202\363\310\304\377\202" + "\364\312\307\377\202\325((\377\202\345\203|\377\202\0\0\0\377\2\0\0\0" + ";\0\0\0\27\204\0\0\0\0\20\0\0\0\3\0\0\0\377\375\375\375\377\356\356\356" + "\377{{\301\377\315\315\333\377\327\327\327\377\316\320\320\377\273\273" + "\273\377\233\233\233\377\324\324\324\377\311\312\312\377\315\315\315" + "\377\315\313\313\377\313\313\313\377\307\310\307\377\202\0\0\0\377\202" + "\364\315\313\377\202\357\266\256\377\202\325((\377\202\364\315\311\377" + "\202\363\314\306\377\202\363\312\302\377\202\363\313\306\377\202\363" + "\313\304\377\202\325((\377\202\357\267\260\377\202\340oc\377\202\0\0" + "\0\377\2\0\0\0;\0\0\0\27\204\0\0\0\0\20\0\0\0\3\0\0\0\377\375\375\375" + "\377\356\356\356\377\336\334\343\377\215\215\305\377\323\323\326\377" + "\300\302\300\377\236\236\236\377\327\327\327\377\316\317\316\377\322" + "\322\322\377\323\323\323\377\323\323\325\377\323\323\323\377\320\320" + "\320\377\202\0\0\0\377\202\364\315\313\377\202\357\266\256\377\202\325" + "((\377\202\364\315\311\377\202\363\314\306\377\202\363\312\302\377\202" + "\363\313\306\377\202\363\313\304\377\202\325((\377\202\357\267\260\377" + "\202\340oc\377\202\0\0\0\377\2\0\0\0;\0\0\0\27\204\0\0\0\0\20\0\0\0\3" + "\0\0\0\377\375\375\375\377\356\354\354\377\350\350\350\377\324\324\334" + "\377\201\201\267\377\214\214\242\377\334\334\334\377\322\322\322\377" + "\325\325\325\377\327\327\327\377\333\331\331\377\333\333\333\377\333" + "\332\333\377\327\327\327\377\202\0\0\0\377\202\365\316\316\377\202\364" + "\315\313\377\202\364\315\311\377\202\325((\377\202\364\316\313\377\202" + "\363\314\306\377\202\364\315\311\377\202\325((\377\202\360\277\267\377" + "\204\357\267\260\377\202\0\0\0\377\2\0\0\0;\0\0\0\27\204\0\0\0\0\20\0" + "\0\0\3\0\0\0\377\375\375\375\377\355\355\355\377\350\351\351\377\323" + "\323\323\377\252\252\252\377\271\271\325\377\231\231\303\377\253\252" + "\312\377\315\315\326\377\335\335\335\377\335\337\335\377\337\337\337" + "\377\336\336\336\377\333\331\333\377\202\0\0\0\377\202\365\316\316\377" + "\202\364\315\313\377\202\364\315\311\377\202\325((\377\202\364\316\313" + "\377\202\363\314\306\377\202\364\315\311\377\202\325((\377\202\360\277" + "\267\377\204\357\267\260\377\202\0\0\0\377\2\0\0\0;\0\0\0\27\204\0\0" + "\0\0\6\0\0\0\3\0\0\0\377\376\376\376\377\356\356\356\377\332\332\332" + "\377\261\261\261\377\202\334\334\334\377\10\333\332\333\377\325\325\332" + "\377\270\266\320\377\256\256\315\377\256\256\316\377\252\253\314\377" + "\244\244\313\377\226\226\301\377\202\0\0\0\377\202\363\307\302\377\202" + "\365\316\315\377\202\360\300\265\377\204\325((\377\202\363\314\304\377" + "\204\325((\377\202\363\312\302\377\202\362\306\277\377\202\343~p\377" + "\202\0\0\0\377\2\0\0\0;\0\0\0\27\204\0\0\0\0\14\0\0\0\3\0\0\0\377\376" + "\376\376\377\335\335\335\377\265\265\265\377\364\366\364\377\345\343" + "\343\377\340\340\340\377\337\337\337\377\337\335\335\377\336\336\336" + "\377\337\335\335\377\202\337\337\337\377\2\336\336\340\377\333\332\332" + "\377\202\0\0\0\377\202\363\307\302\377\202\365\316\315\377\202\360\300" + "\265\377\204\325((\377\202\363\314\304\377\204\325((\377\202\363\312" + "\302\377\202\362\306\277\377\202\343~p\377\202\0\0\0\377\2\0\0\0;\0\0" + "\0\27\204\0\0\0\0\13\0\0\0\3\0\0\0\377\377\377\377\377\254\256\254\377" + "\373\373\373\377\353\353\353\377\351\351\351\377\345\345\345\377\342" + "\342\342\377\340\342\340\377\340\336\340\377\202\336\336\336\377\3\334" + "\336\336\377\335\335\335\377\330\330\331\377\202\0\0\0\377\202\365\316" + "\315\377\202\363\310\302\377\202\325((\377\202\364\315\307\377\202\363" + "\313\302\377\202\325((\377\202\362\312\301\377\202\361\306\272\377\202" + "\325((\377\202\350\227\211\377\202\336bU\377\202\0\0\0\377\2\0\0\0;\0" + "\0\0\27\204\0\0\0\0\15\0\0\0\3\0\0\0\377\273\273\273\377\266\265\265" + "\377\262\262\262\377\251\251\251\377\250\250\250\377\252\252\252\377" + "\250\250\250\377\251\250\251\377\251\251\252\377\251\252\252\377\252" + "\252\252\377\202\253\253\253\377\1\250\250\250\377\202\0\0\0\377\202" + "\365\316\315\377\202\363\310\302\377\202\325((\377\202\364\315\307\377" + "\202\363\313\302\377\202\325((\377\202\362\312\301\377\202\361\306\272" + "\377\202\325((\377\202\350\227\211\377\202\336bU\377\202\0\0\0\377\2" + "\0\0\0;\0\0\0\27\204\0\0\0\0\2\0\0\0\2\0\0\0C\220\0\0\0\377\202\365\316" + "\315\377\202\325((\377\202\363\312\304\377\202\363\314\306\377\202\363" + "\313\304\377\202\360\303\267\377\204\362\307\277\377\202\342zi\377\202" + "\246\37\37\377\202\356\264\255\377\202\0\0\0\377\2\0\0\0;\0\0\0\27\204" + "\0\0\0\0\6\0\0\0\1\0\0\0\12\0\0\0!\0\0\0""7\0\0\0@\0\0\0A\211\0\0\0B" + "\1\0\0\0D\202\0\0\0\377\202\365\316\315\377\202\325((\377\202\363\312" + "\304\377\202\363\314\306\377\202\363\313\304\377\202\360\303\267\377" + "\204\362\307\277\377\202\342zi\377\202\246\37\37\377\202\356\264\255" + "\377\202\0\0\0\377\2\0\0\0;\0\0\0\27\205\0\0\0\0\4\0\0\0\2\0\0\0\12\0" + "\0\0\24\0\0\0\30\212\0\0\0\31\1\0\0\0\34\202\0\0\0\377\202\325((\377" + "\202\337h\\\377\202\336eY\377\202\332SE\377\202\343\177n\377\202\341" + "yh\377\202\350\234\215\377\202\345\214~\377\202\337jZ\377\202\350\226" + "\213\377\202w\26\26\377\202\0\0\0\377\2\0\0\0;\0\0\0\27\206\0\0\0\0\2" + "\0\0\0\1\0\0\0\2\213\0\0\0\3\1\0\0\0\6\202\0\0\0\377\202\325((\377\202" + "\337h\\\377\202\336eY\377\202\332SE\377\202\343\177n\377\202\341yh\377" + "\202\350\234\215\377\202\345\214~\377\202\337jZ\377\202\350\226\213\377" + "\202w\26\26\377\202\0\0\0\377\2\0\0\0""9\0\0\0\26\223\0\0\0\0\3\0\0\0" + "\2\0\0\0\20\0\0\0+\226\0\0\0\377\4\0\0\0L\0\0\0\77\0\0\0+\0\0\0\20\223" + "\0\0\0\0\3\0\0\0\1\0\0\0\6\0\0\0\22\226\0\0\0\377\4\0\0\0\77\0\0\0%\0" + "\0\0\22\0\0\0\6\224\0\0\0\0\5\0\0\0\1\0\0\0\4\0\0\0\22\0\0\0+\0\0\0""9" + "\222\0\0\0;\5\0\0\0""9\0\0\0+\0\0\0\22\0\0\0\4\0\0\0\1\225\0\0\0\0\3" + "\0\0\0\1\0\0\0\6\0\0\0\20\202\0\0\0\26\221\0\0\0\27\5\0\0\0\26\0\0\0" + "\20\0\0\0\6\0\0\0\1\0\0\0\0"}; + + diff --git a/src/icons/stock_sylpheed_newmail.png b/src/icons/stock_sylpheed_newmail.png new file mode 100644 index 0000000..58f3b4c Binary files /dev/null and b/src/icons/stock_sylpheed_newmail.png differ diff --git a/src/icons/stock_sylpheed_newmail_16.h b/src/icons/stock_sylpheed_newmail_16.h new file mode 100644 index 0000000..ad1ba4f --- /dev/null +++ b/src/icons/stock_sylpheed_newmail_16.h @@ -0,0 +1,68 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (stock_sylpheed_newmail_16) +#endif +#ifdef __GNUC__ +static const guint8 stock_sylpheed_newmail_16[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 stock_sylpheed_newmail_16[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (812) */ + "\0\0\3D" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (64) */ + "\0\0\0@" + /* width (16) */ + "\0\0\0\20" + /* height (16) */ + "\0\0\0\20" + /* pixel_data: */ + "\227\0\0\0\0\10\265\265\346\377\214\214\325\377\203\203\316\377\220\220" + "\320\377\246\246\326\377\275\275\336\377\307\307\343\377\302\302\336" + "\377\206\0\0\0\0\11\317\317\362\377\241\241\343\377\301\301\352\377\312" + "\312\354\377\301\301\347\377\265\265\336\377\254\254\330\377\255\255" + "\325\377\277\277\336\377\207\0\0\0\0\1\310\310\360\377\212\0\0\0\0\2" + "\310\310\310\377\225\225\225\377\202\227\227\227\377\2\204\204\233\377" + "\214\214\225\377\202\224\224\224\377\206\225\225\225\377(\230\230\230" + "\377\0\0\0\0\223\223\223\377\367\367\367\377\367\366\367\377\365\365" + "\365\377\331\331\352\377\334\334\344\377\340\340\340\377\324\324\337" + "\377\277\276\332\377\261\261\321\377\213\213\300\377\267\267\317\377" + "\337\340\340\377\342\342\343\377\235\235\235\377\0\0\0\0\221\221\221" + "\377\345\345\345\377\365\365\365\377\360\360\360\377\344\344\351\377" + "\306\306\333\377\333\333\334\377\327\327\334\377\332\332\333\377\324" + "\324\325\377\305\305\314\377^]\236\377\236\237\271\377\311\311\310\377" + "\177\177\177\377\0\0\0\0\227\227\227\377\357\357\357\377\342\342\342" + "\377\360\360\360\377\302\300\300\377\26\7\7\377\202\31\25\25\377\203" + "\30\25\24\377\20\31\25\24\377\31\25\25\377\30\25\24\377\21\6\6\377\7" + "\1\1+\227\227\227\377\370\371\371\377\352\353\353\377\326\326\334\377" + "\21\6\6\377\336YX\377\360\265\264\377\365\316\315\377\365\316\314\377" + "\364\315\312\377\364\315\313\377\202\365\315\314\377\15\357\264\262\377" + "\265DC\377\15\4\4\377\227\227\227\377\356\356\364\377\213\213\313\377" + "sr\273\377\23\20\20\377\361\273\271\377\340da\377\356\253\251\377\364" + "\315\312\377\364\314\307\377\202\363\313\306\377\35\355\250\245\377\340" + "ca\377\342wo\377\22\12\11\377\227\227\227\377\243\243\324\377\254\254" + "\317\377\321\321\321\377\23\20\20\377\364\313\311\377\355\254\247\377" + "\343tr\377\354\244\241\377\363\314\306\377\363\313\306\377\354\243\237" + "\377\342pl\377\353\244\236\377\347\222\211\377\22\13\12\377\227\227\227" + "\377\314\314\342\377\307\307\323\377\272\272\272\377\23\20\17\377\364" + "\313\311\377\364\314\311\377\351\226\221\377\333HG\377\202\353\240\234" + "\377'\333HG\377\351\224\217\377\361\300\271\377\352\240\226\377\22\14" + "\13\377\227\227\227\377\355\355\355\377\266\266\304\377\276\276\316\377" + "\23\20\17\377\364\313\310\377\357\263\257\377\342ql\377\347\213\206\377" + "\344{w\377\344{v\377\346\210\201\377\342rn\377\351\233\221\377\342wi" + "\377\21\10\7\377\227\227\227\377\333\334\333\377\343\343\343\377\337" + "\337\337\377\23\20\20\377\361\274\273\377\341ge\377\355\251\244\377\363" + "\314\305\377\356\262\252\377\355\260\250\377\362\307\276\377\344\201" + "v\377\302JD\377\342\216\206\377\22\14\13\377\256\256\256\377ppp\377\202" + "iii\377\14\21\6\6\377\334LJ\377\336bX\377\341uj\377\342yk\377\345\213" + "|\377\347\227\211\377\351\235\220\377\342zk\377\334zp\377\232A>\377\13" + "\4\4\377\204\0\0\0\0\3\12\2\2+\25\5\5\377\26\12\11\377\202\26\12\10\377" + "\7\27\14\13\377\27\16\14\377\27\17\15\377\26\14\12\377\27\16\15\377\16" + "\4\4\377\5\1\1+"}; + + diff --git a/src/icons/stock_sylpheed_newmail_16.png b/src/icons/stock_sylpheed_newmail_16.png new file mode 100644 index 0000000..bbbc21b Binary files /dev/null and b/src/icons/stock_sylpheed_newmail_16.png differ diff --git a/src/icons/sylpheed-logo.h b/src/icons/sylpheed-logo.h new file mode 100644 index 0000000..e2eedd1 --- /dev/null +++ b/src/icons/sylpheed-logo.h @@ -0,0 +1,515 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (sylpheed_logo) +#endif +#ifdef __GNUC__ +static const guint8 sylpheed_logo[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 sylpheed_logo[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (16181) */ + "\0\0\77M" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (532) */ + "\0\0\2\24" + /* width (133) */ + "\0\0\0\205" + /* height (46) */ + "\0\0\0." + /* pixel_data: */ + "\220\0\0\0\0\13Pt\253\32Pt\253WPt\253\205Pt\253\233Pt\253\236Pt\253\224" + "Pt\253\177Pt\253`Pt\253>Os\252\32Os\252\1\205\0\0\0\0\3Mp\246\2Mo\244" + ".Mo\245\4\356\0\0\0\0\4\0\0\0\1""0Ef\5Mp\245[Or\251\306\211Pt\253\377" + "\17Os\252\377Nq\247\367Ln\243\334Ik\240\307Ik\236\272Jk\236\274Jl\237" + "\315Ik\237\335A^\214Z\0\0\0\17\0\0\0\14\0\0\0\11\0\0\0\6\0\0\0\3\0\0" + "\0\1\220\0\0\0\0\2""0L\222\0\320\276\322\0\276\0\0\0\0\2\240N\347\0\350" + "\373s\0\205\333\333\333\0\204\0\0\0\1\202\333\333\333\0\213\0\0\0\0\36" + "\0\0\0\1Jl\237-Os\252\315Pt\253\377Os\252\350Nq\246\311Ln\242\276Km\241" + "\300Km\240\311Ln\243\327Mp\245\347Or\250\366Pt\253\377Pt\252\377Os\251" + "\377Or\250\377Nq\247\377Mp\246\377Km\241\351Gg\230\276=Y\203{\26\40/" + "1\0\0\0\40\0\0\0\33\0\0\0\25\0\0\0\17\0\0\0\12\0\0\0\5\0\0\0\2\0\0\0" + "\1\214\0\0\0\0\205\0\0\0\1\220\0\0\0\0\1\0\0\0\1\202\0\0\0\2\202\0\0" + "\0\3\2\0\0\0\2\0\0\0\1\247\0\0\0\0\204\333\333\333\0\11\0\0\0\1\36,A" + "\5.Ba2\0\0\0\3\0\0\0\4\0\0\0\3\0\0\0\2\0\0\0\1\333\333\333\0\212\0\0" + "\0\0\37\0\0\0\1Mp\245\77Pt\253\352Mo\244\2138Qw2\0\0\0\32\0\0\0#\0\0" + "\0.\0\0\0""8\0\0\0A\0\0\0I\0\0\0P\0\0\0U\5\7\13\\\20\27#f\26!0l\30#4" + "k\25\37/d\7\12\20S\0\0\0G\0\0\0B\0\0\0<\0\0\0""6\0\0\0/\0\0\0'\0\0\0" + "\36\0\0\0\25\0\0\0\15\0\0\0\7\0\0\0\3\0\0\0\1\212\0\0\0\0\3\0\0\0\1""0" + "Ef\6Cb\221@\203\0\0\0\4\3\0\0\0\3\0\0\0\2\0\0\0\1\214\0\0\0\0\13\0\0" + "\0\1\0\0\0\2;V\177(7Pw\305\40/E\16\0\0\0\10\0\0\0\7\0\0\0\6\0\0\0\4\0" + "\0\0\2\0\0\0\1\245\0\0\0\0\203\333\333\333\0\4\0\0\0\1)\\o\31%7\362\27!0\377%6P\225\0\0\0\35\0\0\0!\0\0\0\40\0\0\0\32" + "\0\0\0\22\0\0\0\13\0\0\0\5\210\0\0\0\0\21\0\0\0\1""9Rz\7Or\251\273\0" + "\0\0\14\0\0\0\25\0\0\0\36\0\0\0(\0\0\0""0\0\0\0""5\0\0\0""9\0\0\0<\0" + "\0\0\77\0\0\0B\0\0\0D\0\0\0G\0\0\0I\0\0\0K\202\0\0\0M\16\0\0\0L\0\0\0" + "K\0\0\0H\0\0\0C\0\0\0=\0\0\0""6\0\0\0-\0\0\0$\0\0\0\33\0\0\0\22\0\0\0" + "\13\0\0\0\5\0\0\0\2\0\0\0\1\207\0\0\0\0\15\0\0\0\1\0\0\0\3Ca\220x@]\211" + "\377@]\212\252\0\0\0\37\0\0\0$\0\0\0#\0\0\0\35\0\0\0\24\0\0\0\14\0\0" + "\0\6\0\0\0\2\211\0\0\0\0\6\0\0\0\2\0\0\0\5:U~\2004Ko\3776Ou\275\0\0\0" + ")\202\0\0\0/\6\0\0\0(\0\0\0\35\0\0\0\22\0\0\0\11\0\0\0\4\0\0\0\1\244" + "\0\0\0\0\15\333\333\333\0\0\0\0\2\35*\77\16\30$5\367\27!0\375\30#4>\0" + "\0\0-\0\0\0""1\0\0\0.\0\0\0%\0\0\0\31\0\0\0\16\0\0\0\7\210\0\0\0\0\10" + "\0\0\0\1Lo\2430Ln\243q\0\0\0\17\0\0\0\30\0\0\0!\0\0\0(\0\0\0,\202\0\0" + "\0-\202\0\0\0,\6\0\0\0-\0\0\0.\0\0\0""0\0\0\0""2\0\0\0""3\0\0\0""4\202" + "\0\0\0""5\14\0\0\0""4\0\0\0""2\0\0\0.\0\0\0*\0\0\0%\0\0\0\37\0\0\0\30" + "\0\0\0\22\0\0\0\14\0\0\0\7\0\0\0\3\0\0\0\1\210\0\0\0\0\16\0\0\0\2\0\0" + "\0\6B`\216\240@]\211\377/CdS\0\0\0/\0\0\0""4\0\0\0""1\0\0\0(\0\0\0\33" + "\0\0\0\20\0\0\0\7\0\0\0\3\0\0\0\1\207\0\0\0\0\17\0\0\0\1\0\0\0\3\30\"" + "3\13""6Os\3374Ko\377(;W^\0\0\0:\0\0\0A\0\0\0\77\0\0\0""4\0\0\0%\0\0\0" + "\26\0\0\0\13\0\0\0\4\0\0\0\1\244\0\0\0\0\15\0\0\0\1\0\0\0\4+>\\O\27!" + "2\377\"1I\265\0\0\0""1\0\0\0=\0\0\0A\0\0\0:\0\0\0-\0\0\0\35\0\0\0\20" + "\0\0\0\7\210\0\0\0\0\13\0\0\0\2Lo\243HEd\224=\0\0\0\22\0\0\0\32\0\0\0" + "\"\0\0\0&\0\0\0%\0\0\0#\0\0\0\37\0\0\0\34\202\0\0\0\33\4\0\0\0\34\0\0" + "\0\35\0\0\0\37\0\0\0\40\203\0\0\0!\13\0\0\0\40\0\0\0\36\0\0\0\33\0\0" + "\0\30\0\0\0\25\0\0\0\21\0\0\0\15\0\0\0\11\0\0\0\6\0\0\0\3\0\0\0\1\210" + "\0\0\0\0\17\0\0\0\1\0\0\0\3""3Im\30@]\212\367@]\211\322\1\2\3""0\0\0" + "\0<\0\0\0B\0\0\0=\0\0\0/\0\0\0\37\0\0\0\21\0\0\0\10\0\0\0\3\0\0\0\1\207" + "\0\0\0\0\17\0\0\0\1\0\0\0\4:T}`4Lp\3775Nt\317\1\1\2:\0\0\0I\0\0\0O\0" + "\0\0J\0\0\0;\0\0\0(\0\0\0\27\0\0\0\13\0\0\0\4\0\0\0\1\244\0\0\0\0\15" + "\0\0\0\1\0\0\0\5%7P\243\27\"2\377\35+\77Z\0\0\0=\0\0\0I\0\0\0K\0\0\0" + "A\0\0\0""0\0\0\0\36\0\0\0\20\0\0\0\7\210\0\0\0\0\36\0\0\0\2Jl\240\77" + "Db\221=\0\0\0\22\0\0\0\32\0\0\0\37\0\0\0\40\0\0\0\35\0\0\0\30\3\5\10" + "\25Z\2057Hi\233kNq\247\326" + "Ps\252\377Lm\241\3000Gi;\0\0\0\32\0\0\0\27\0\0\0\24\0\0\0\17\0\0\0\13" + "\0\0\0\10\0\0\0\5\0\0\0\3\0\0\0\2\0\0\0\1\213\0\0\0\0\16\0\0\0\2'9T\13" + "A_\215\345A]\212\353\11\16\25""3\0\0\0\77\0\0\0L\0\0\0M\0\0\0B\0\0\0" + "0\0\0\0\36\0\0\0\17\0\0\0\7\0\0\0\2\207\0\0\0\0\7\0\0\0\1\0\0\0\3;V~" + "P5Lq\3775Ns\341\3\5\7""9\0\0\0K\202\0\0\0W\7\0\0\0K\0\0\0""7\0\0\0\"" + "\0\0\0\22\0\0\0\10\0\0\0\3\0\0\0\1\243\0\0\0\0\16\0\0\0\1\0\0\0\3)=[" + "k\30#2\377#3Kn\0\0\0""8\0\0\0I\0\0\0Q\0\0\0L\0\0\0=\0\0\0)\0\0\0\30\0" + "\0\0\13\0\0\0\4\210\0\0\0\0\11\0\0\0\2\0\0\0\5Ln\243\201\24\36,\27\0" + "\0\0\27\0\0\0\31\0\0\0\27""2Ik.Ee\225N\202\0\0\0\13\20\0\0\0\15\0\0\0" + "\20\0\0\0\24\13\20\27\35Gg\230\217Or\250\377Mp\245\3645MrX\0\0\0\"\0" + "\0\0\35\0\0\0\26\0\0\0\17\0\0\0\11\0\0\0\5\0\0\0\2\0\0\0\1\213\0\0\0" + "\0\17\0\0\0\1\0\0\0\3Ba\217eA^\213\377=X\203\221\0\0\0""3\0\0\0D\0\0" + "\0M\0\0\0K\0\0\0=\0\0\0+\0\0\0\31\0\0\0\14\0\0\0\5\0\0\0\1\207\0\0\0" + "\0\17\0\0\0\1\0\0\0\4""9Rz\3064Lp\3772Il\203\0\0\0>\0\0\0P\0\0\0X\0\0" + "\0T\0\0\0D\0\0\0/\0\0\0\34\0\0\0\16\0\0\0\6\0\0\0\2\244\0\0\0\0\16\0" + "\0\0\1\16\25\37\7\36-C\327\34*=\344\4\6\12,\0\0\0<\0\0\0J\0\0\0N\0\0" + "\0E\0\0\0""5\0\0\0\"\0\0\0\22\0\0\0\10\0\0\0\3\210\0\0\0\0\33\0\0\0\2" + "\0\0\0\5""8Ry\37Ik\236w\0\0\0\24\0\0\0\26\0\0\0\25\10\14\22\23\0\0\0" + "\15\0\0\0\13\0\0\0\14\5\7\12\20\0\0\0\24\0\0\0\31\0\0\0\36\0\0\0$Cb\220" + "\226Nq\246\377Ln\243\361\40/EJ\0\0\0,\0\0\0#\0\0\0\31\0\0\0\17\0\0\0" + "\10\0\0\0\4\0\0\0\1\213\0\0\0\0\17\0\0\0\1\30#4\6B`\215\330@]\211\372" + "\34*>;\0\0\0""9\0\0\0G\0\0\0L\0\0\0E\0\0\0""6\0\0\0#\0\0\0\24\0\0\0\11" + "\0\0\0\3\0\0\0\1\206\0\0\0\0\20\0\0\0\1\0\0\0\2;V\200@5Mq\3774Ms\360" + "\14\22\32:\0\0\0E\0\0\0T\0\0\0W\0\0\0M\0\0\0;\0\0\0'\0\0\0\26\0\0\0\13" + "\0\0\0\4\0\0\0\1\244\0\0\0\0\16\0\0\0\2,\77^S\30#4\377%7Q\202\0\0\0/" + "\0\0\0@\0\0\0J\0\0\0I\0\0\0=\0\0\0+\0\0\0\32\0\0\0\15\0\0\0\6\0\0\0\2" + "\210\0\0\0\0\33\0\0\0\1\0\0\0\4\0\0\0\10\0\0\0" + "5\0\0\0'\0\0\0\32\0\0\0\17\0\0\0\7\0\0\0\4\203\0\0\0\2\202\0\0\0\1\203" + "\0\0\0\0\202\0\0\0\1\7\0\0\0\2\0\0\0\3Ba\220VA_\213\377@]\211\251\0\0" + "\0.\0\0\0\77\202\0\0\0J\6\0\0\0\77\0\0\0.\0\0\0\35\0\0\0\17\0\0\0\7\0" + "\0\0\3\210\0\0\0\2\17\0\0\0\4:T{\2675Lq\3775Ms\227\0\0\0:\0\0\0L\0\0" + "\0V\0\0\0T\0\0\0F\0\0\0""3\0\0\0\40\0\0\0\22\0\0\0\11\0\0\0\4\0\0\0\2" + "\204\0\0\0\0\1\0\0\0\1\202\0\0\0\2\203\0\0\0\3\2\0\0\0\2\0\0\0\1\205" + "\0\0\0\0\1\0\0\0\1\202\0\0\0\2\203\0\0\0\3\2\0\0\0\2\0\0\0\1\211\0\0" + "\0\0\20\0\0\0\1\0\0\0\2\10\14\22\6\40""1G\313\31%8\364\17\26!1\0\0\0" + "6\0\0\0C\0\0\0I\0\0\0C\0\0\0""4\0\0\0#\0\0\0\24\0\0\0\11\0\0\0\4\0\0" + "\0\1\210\0\0\0\0\22\0\0\0\1\0\0\0\3\0\0\0\7\0\0\0\13\30#4\27B`\216NC" + "a\217WGg\230dIk\235tKl\240\204Gf\227b\25\37.\32\0\0\0\27\0\0\0\32\0\0" + "\0\36\0\0\0$\0\0\0-Ee\225\272\202Mo\244\377\10\33';h\0\0\0G\0\0\0""8" + "\0\0\0'\0\0\0\30\31%6\25Ca\217=#2J\15\202\0\0\0\6\34\0\0\0\5\0\0\0\4" + "\0\0\0\3\0\0\0\2\0\0\0\3\0\0\0\4\0\0\0\5\0\0\0\6\10\13\21\11C`\216\316" + "A^\213\377*=[K\0\0\0""7\0\0\0F\0\0\0L\0\0\0G\0\0\0:\0\0\0(\0\0\0\31\0" + "\0\0\16\0\0\0\7\0\0\0\5\0\0\0\4\0\0\0\5\0\0\0\6\0\0\0\7\0\0\0\10\0\0" + "\0\7\202\0\0\0\6\34""8Ry55Mr\3774Lq\373\30#3C\0\0\0C\0\0\0S\0\0\0X\0" + "\1\1Q\35*\77b\17\26!9\0\0\0\36\0\0\0\22\0\0\0\13\0\0\0\6\0\0\0\3\0\0" + "\0\2\0\0\0\1""8Qx\13:S|D7Qxa7OuO%6P\23\0\0\0\10\0\0\0\11\0\0\0\10\0\0" + "\0\6\0\0\0\4\0\0\0\2\202\0\0\0\1\14""4Mq\13""5MsD3Jma2ImO#3K\23\0\0\0" + "\10\0\0\0\11\0\0\0\10\0\0\0\6\0\0\0\4\0\0\0\2\0\0\0\1\204\0\0\0\0\202" + "\0\0\0\1\10\0\0\0\2\0\0\0\3\0\0\0\5+\77^M\31#5\377%6P\234\0\0\0,\0\0" + "\0<\202\0\0\0F\7\0\0\0<\0\0\0,\0\0\0\33\0\0\0\16\0\0\0\6\0\0\0\2\275" + "\275\275\0\210\0\0\0\0}\0\0\0\1\0\0\0\3\0\0\0\6\0\0\0\11\0\0\0\15\0\0" + "\0\20\0\0\0\22\4\6\11\22\34)=\31\4\6\11\22\0\0\0\22\0\0\0\24\0\0\0\26" + "\0\0\0\27\0\0\0\31\0\0\0\36\0\0\0&Ba\217\246Mo\244\377Lo\243\3771Hj\234" + "\0\0\0X\0\0\0J\0\0\0""7-BaKHh\232\322Hi\233\377C`\217b\0\0\0\17\0\0\0" + "\16\0\0\0\13\0\0\0\10%6O\14Ed\224\342Dc\222\3667Pv%\0\0\0\13\0\0\0\15" + "=Y\204QA_\213\377@]\212\301\0\0\0/\0\0\0\77\0\0\0K\0\0\0L\22\33(U,A_" + "m\0\0\0%\0\0\0\30\0\0\0\17:U~B>Z\205\247;V\200\345:T{\374;U~\3304MrG" + "\0\0\0\22\0\0\0\21\0\0\0\16\0\0\0\15:T}\2525Mr\3777Ou\254\0\0\0:\0\0" + "\0M\7\12\20`(;W\2363Il\3431Gi\3772Jl\345\0\0\0!\0\0\0\30\0\0\0\21\0\0" + "\0\14\0\0\0\7""1Hj\25""9RzS7Qxi7Ovi3Lo\233*=\\\373+\77^\3560FgX\0\0\0" + "\23\0\0\0\22\0\0\0\17\0\0\0\13\0\0\0\7.Dd\25""4MqS3Jni2Ili/De\233#4L" + "\373%5O\356-A`X\0\0\0\23\0\0\0\22\0\0\0\17\0\0\0\12\0\0\0\6\0\0\0\3\0" + "\0\0\2\0\0\0\1\0\0\0\2-Ba\17.CdX+\77\\\210,A_o,A_U&8S&\"1I\310\30#4\375" + "\30#4A\0\0\0""5\0\0\0C\0\0\0H\0\0\0C\0\0\0""5\0\0\0$\0\0\0\25\0\0\0\12" + "\0\0\0\4\0\0\0\1\304\304\304\0\207\0\0\0\0\10\0\0\0\1\0\0\0\2\0\0\0\3" + "\0\0\0\6\0\0\0\11\0\0\0\14\0\0\0\17\0\0\0\20\202\0\0\0\21\2\0\0\0\22" + "\0\0\0\23\204\0\0\0\24d\0\0\0\27\0\0\0\36Dd\224\250Mo\244\377Ln\243\377" + "5Mr\256\0\0\0e\1\2\3[6Ou\224Cb\221\277Ij\234\377Gg\232\354\21\31%#\0" + "\0\0\34\0\0\0\32\0\0\0\25\0\0\0\20Ca\220nEc\223\377Dc\223\267\0\0\0\23" + "\0\0\0\27\0\0\0\30A_\214\306A_\213\3770Fgb\0\0\0""9\0\0\0G\16\24\36Z" + "-Cc\220=Y\203\373=Y\202\344\0\0\0%\25\36-'=Y\204\250:V\200\363=X\202" + "\301\0\0\0.\0\0\0\35\0\0\0\17\0\0\0\7\0\0\0\2\274\274\274" + "\0\311\311\311\0\205\0\0\0\0\12\0\0\0\1\0\0\0\2\0\0\0\4\0\0\0\5\0\0\0" + "\7\0\0\0\11\0\0\0\13\0\0\0\15\0\0\0\17\0\0\0\20\202\0\0\0\21\202\0\0" + "\0\22A\0\0\0\23\0\0\0\22\0\0\0\20\0\0\0\17\0\0\0\20\0\0\0\27Hi\233\277" + "Lo\243\377Ln\242\3770Fg\246\0\0\0o\21\31&x\15\23\35_\77[\206\253Ij\234" + "\377>Z\205\221\0\0\0,\0\0\0-\0\0\0)\0\0\0\"\14\22\32\36Ec\224\343Dc\222" + "\3777PwU\0\0\0!\0\0\0&.CdVB_\214\377@^\212\327\1\1\2""7\0\0\0C%6O|\14" + "\21\32\\4Lp\245>Z\205\377;V\177\274\26!0=Z\0\0\0<\0\0\0G\0" + "\0\0K\0\0\0F\0\0\0""7\0\0\0&\0\0\0\26\0\0\0\13\0\0\0\4\0\0\0\1\303\303" + "\303\0\316\316\316\0\203\0\0\0\0\11\0\0\0\1\0\0\0\2Km\2416Nq\246~Mq\246" + "\236Mp\245\236Kl\240\200A_\214J\12\17\27\26\203\0\0\0\23\202\0\0\0\22" + "\202\0\0\0\21\20\0\0\0\20\0\0\0\16\0\0\0\14\0\0\0\13\0\0\0\14\4\5\10" + "\23Lm\241\353Ln\242\377Kn\242\377\34)<\210\0\0\0v\0\0\0q\21\30$oHi\232" + "\371Gg\230\364\20\30$H\202\0\0\0>=\0\0\0""9\0\0\0.;V\177xEd\223\377C" + "b\221\323\1\2\3)\0\0\0""2\0\0\0""7>Z\206\301A_\213\3771Gj\200\0\0\0B" + "\0\0\0L\0\0\0R\0\0\0Q;V\200\323=Y\203\377,Bav9Sz\253\26\37/H\0\0\0""3" + "\0\0\0""1\0\0\0/\1\1\2""39Sz\3519Ry\377,A`\224\0\0\0O\0\0\0K\23\34)R" + "5Ns\3725Ms\3775Lq\301(:V\211\0\1\1\\\0\0\0d\0\0\0c\34)=\1772Hj\3772H" + "k\341\0\1\1I\0\0\0H\0\0\0D'9Tu1Hj\272\0\0\0!\0\0\0\30\0\0\0\24\0\0\0" + "\23\0\0\0\24\0\0\0\30\0\0\0\40.Dd\245)Z\204\377:V\177\316" + "\30$5a\0\0\0O\0\0\0S\0\0\0P\0\0\0H\0\0\0@5Mr\2539Sy\3772Jm\266\0\0\0" + "_\0\0\0g\11\15\23n5Ms\3666Ns\377$5O\202\0\0\0U\0\0\0`\0\0\0g\0\0\0f\24" + "\35,u2Hk\3762Hj\354\4\6\11U\0\0\0X\0\0\0\\,@_\266/De\343\0\0\0@\0\0\0" + "3\0\0\0*\0\0\0#\0\0\0\36+>\\N0Fh\267/Ef\274'9Uh\0\1\2=\0\0\0K);X\264" + ")=Y\344\0\0\0P\0\0\0D\0\0\0""7\0\0\0+\0\0\0\"'8SP*>]\267*=Z\274$5Nh\0" + "\1\2<\0\0\0I\0\0\0Q\1\2\2Q$6Q\312\36-C\373\33(]\2576Ns\3774Lr\332\0\1\1X\0\0\0]\0\0\0e\0\0\0g\0\0\0a/Fg\303" + "2Hj\377*=[\231\0\0\0R\0\0\0Z\0\0\1_/Df\361.Cc\276\0\0\0D\0\0\0:\10\14" + "\23""8+>]r0Fh\2220Eh{\34)=0\0\0\0\40\0\0\0*\0\0\0""9\0\1\1J)Z\205\376;W\201\337\3" + "\5\10H\0\0\0Q\202\0\0\0[4\0\0\0R\2\2\4E9S{\3118S{\370\"2Kh\0\0\0L\0\0" + "\0[\5\10\14i5Mt\3626Ns\377\40/E\210\0\0\0]\0\0\0b\0\0\0e\0\0\0b\30\"" + "3u2Ik\3762Ij\360\7\13\21Q\0\0\0R\0\0\0[\20\27\"p/Dd\377-A`\264\0\0\0" + "G\10\14\22H\35+\77^\12\17\27""9\0\0\0'\0\0\0\40\0\0\0\34\0\0\0\37\0\0" + "\0'\0\0\0""4\23\34*W(9T\377(:W\266\0\0\0V\6\11\16W\31$6g\11\15\23@\0" + "\0\0+\0\0\0!\0\0\0\35\0\0\0\37\0\0\0'\0\0\0""3\0\0\0>\7\12\17M\"1I\360" + "\36,B\376\22\32&X\203\0\0\0A5\0\0\0=\0\0\0""6\25\37.G\"2J\315\32&8\377" + "\34*\77\354\5\7\13D\0\0\0M\0\0\0W\0\0\0V\0\0\0K\0\0\0""8\0\0\0$\0\0\0" + "\24\0\0\0\11\0\0\0\3\0\0\0\1\306\306\306\0\320\320\320\0\326\326\326" + "\0\332\332\332\0\0\0\0\1\0\0\0\3@]\211)Mp\246\273\0\0\0\40\0\0\0""0\0" + "\0\0>\0\0\0H\0\0\0J\0\0\0I\0\0\0E\0\0\0B\0\0\0\77\0\0\0=\0\0\0;\0\0\0" + "7\0\0\0""1;V\200uCb\220\210\0\0\0\35\0\0\0\27""6Nt8Jm\240\341Ln\242\377" + "Hj\236\336\36,AS\0\0\0R\0\0\0g\0\0\0t;W\200\317Hi\233\3778Ry\274\203" + "\0\0\0_o\0\1\1Z3Jn\230Dc\223\374Dc\223\3778Qx\227\0\0\0G\0\0\0U\10\14" + "\22f@^\212\363@^\212\374\21\31%l\0\0\0[\0\0\0\\\0\0\0Y\0\0\0Q5Mq\240" + ">Z\204\377-Bb|\0\0\0G\0\0\0S\0\0\0Z\0\0\0W\0\1\1L5Ns\2519S{\366*=[h\0" + "\0\0:\0\0\0H\0\0\0V*\0\0\0""8\0\0\0""4\0\0\0""2\202\0\0\0""1\37\0\0" + "\0""0\0\0\0.\0\0\0+7QwaA_\214\200B_\215yKn\242\364Ln\243\377Ff\226\257" + "\20\30$@\0\0\0G\0\0\0Z\0\0\0i\23\34*\202Hi\233\376Gh\230\374\22\33'q" + "\0\0\0_\0\0\0`\3\4\6b7Ov\2577Qw\244Ed\223\377Bb\220\362\14\21\32N\0\0" + "\0Q\0\0\0]0Gj\254B_\214\3778Rx\303\0\0\0[\202\0\0\0\\a\0\0\0W\3\5\7O" + "=Y\203\351=X\202\340\2\3\4@\0\0\0H\0\0\0S\0\0\0W\2\3\5R5Ns\255:T}\342" + "%5OU\0\0\0""0\0\0\0""9\0\0\0F\3\4\6T6Ou\3526Nt\377#3L\212\0\0\0\\\0\0" + "\0`\0\0\0a\0\0\0[!0G~2Ik\3772Hk\367\15\23\35O\0\0\0L\0\0\0V\0\0\0[\13" + "\21\31d.Cc\377.Bc\366\13\20\30V\0\0\0K\0\0\0G\0\0\0=\0\0\0/\0\0\0$\0" + "\0\0\35\0\0\0\34\0\0\0\40\0\0\0&\22\32&>'9T\377'9T\365\12\17\26X\0\0" + "\0Q\0\0\0L\0\0\0@\0\0\0""1\0\0\0$\0\0\0\35\0\0\0\34\0\0\0\37\0\0\0$\0" + "\0\0)&8S\311\37-C\377%7Q\263\0\0\0H\0\0\0P\0\0\0R\0\0\0L\30\"3c%6O\260" + "#3Kn\32&9\377\33(;\364\11\16\25N\0\0\0T\1\1\2`\0\0\0_\0\0\0S\0\0\0>\0" + "\0\0(\0\0\0\26\0\0\0\13\0\0\0\4\0\0\0\1\304\304\304\0\317\317\317\0\325" + "\325\325\0\331\331\331\0\333\333\333\0\0\0\0\1\0\0\0\4\0\0\0\12\0\0\0" + "\24A^\213q:U~v\1\2\2:\0\0\0<\0\0\0""8\0\0\0""0\0\0\0)\0\0\0$\202\0\0" + "\0\"\177\0\0\0#\0\0\0$\0\0\0%\7\13\20);V\200oKl\241\343Mo\244\377Gh\232" + "\3070Fga\0\0\0""7\0\0\0C\0\0\0Q\0\0\0^\0\0\0e7Pu\265Hi\232\377[\207\307Ed\222\3778Qx\240" + "\0\0\0O\0\0\0[\2\4\6f@]\211\361B_\214\377\33':{\32%8z,A`\240\3\4\6`\0" + "\0\0U+@^\204>Z\204\3772Ik\211\0\0\0@\0\0\0K\0\0\0S\27!1j8Rx\3155Ms\246" + "\16\24\36:\0\0\0,\0\0\0""0\0\0\0""9\0\0\0C)=Z\2136Ot\3776Mr\340\0\1\1" + "V\0\0\0[\0\0\0_\0\0\0]\0\1\1T3Jn\3332Ik\3771Gj\256\17\25\40T*=Z\241\5" + "\7\13_\0\0\0Z\0\0\0S0Eg\327.Bb\377-Cb\272\0\1\1P\0\0\0M\0\0\0C\0\0\0" + "4\0\0\0'\20\30#()[\207]Hh\232\240Ln\243\353" + "Lm\241\357Dc\223\245>Z\204\213\31$6I\0\0\0=\0\0\0E\0\0\0N\0\0\0V\202" + "\0\0\0ZJ=Y\204\277Hi\232\377>[\205\303&8T\2056Nt\261[t\5\10\14\35\202\0\0\0\27\33\0\0\0\31#3K\352\37-C" + "\377\36-C\373$4M\273#2K\323\35+@\371#2K\300\4\7\12L\0\0\0:\37-C\345\32" + "&8\377\35*>\354\37,B\247\1\1\2h\0\0\0i\0\0\0]\0\0\0G\0\0\0/\0\0\0\33" + "\0\0\0\15\0\0\0\5\0\0\0\1\302\302\302\0\316\316\316\0\325\325\325\0\331" + "\331\331\0\202\333\333\333\0C\0\0\0\0\0\0\0\2\0\0\0\6\0\0\0\14\0\0\0" + "\25\0\0\0\35\0\0\0#\24\36,.:U}Z@]\210aCa\216]Fe\225aHh\232jHj\236{Hj" + "\234\204Ee\225q\77[\207Y/De@\5\10\14(\0\0\0.\0\0\0""61Gjz\1\1\2G\0\0" + "\0M\0\0\0Q\0\0\0R\0\0\0P\0\0\0J\30$5[>Y\205\260Ba\217\327\77\\\207\307" + ");X\217\0\1\1a\0\0\0\\[\206\351!0H\202\0\0\0^\22\32't-Bb\2413Ko\253>Z\204" + "\377;V\201\3271Hk\2210Fg\227!0Gz\2\3\5S\0\0\0I\0\0\0=\0\0\0""2\0\0\0" + ",\0\0\0+\0\0\0.\0\0\0""2\34(;P7Pw\3346Ot\310\3\5\10F\0\0\0M\202\0\0\0" + "U>\0\0\0M!1Il3Jn\3534Jn\326&8Sz\0\0\0O\0\0\0[\0\0\0]\0\0\0U\0\0\0H\0" + "\0\0>!1Hl.De\331,A_\377,@]\377+@^\370-Ba\274\34)=T\0\0\0&\0\0\0\35\0" + "\0\0\30\0\0\0\26\0\0\0\31\0\0\0!#2Ka*=Y\330%6P\377%5N\377$5O\370(;X\274" + "\32'9T\0\0\0&\0\0\0\35\0\0\0\30\0\0\0\25\0\0\0\27*=Z|\36-B\374\36,A\377" + "\35*\77\376!1I\333\31$6\224\0\0\0]\0\0\0N\0\0\0B\40.E\337\36,B\345\27" + "\"3{\0\0\0a\0\0\0j\0\0\0g\0\0\0W\0\0\0@\0\0\0(\0\0\0\26\0\0\0\12\0\0" + "\0\4\0\0\0\1\310\310\310\0\321\321\321\0\327\327\327\0\332\332\332\0" + "\202\333\333\333\0\14\0\0\0\0\0\0\0\1\0\0\0\4\0\0\0\10\0\0\0\16\0\0\0" + "\24\0\0\0\31\0\0\0\33\0\0\0\31\0\0\0\26\0\0\0\23\0\0\0\20\202\0\0\0\16" + "\11\0\0\0\20\0\0\0\23\0\0\0\27\0\0\0\36\0\0\0&\0\0\0/\0\0\0""9\12\17" + "\26I\32&8`\202\0\0\0KA\0\0\0G\0\0\0@\0\0\0""8\0\0\0""3\0\0\0""4\0\0\0" + "=\0\0\0K\0\0\0W\0\0\0[\27!1oCb\221\375Ba\220\367\16\24\36b\0\0\0^\0\0" + "\0f\0\0\0g\3\5\7c%6P\205\6\11\16X\0\0\0U\0\0\0[\0\0\0^\0\0\0Z9S|\307" + "\0\0" + "\0""1\0\0\0(\0\0\0\"\0\0\0\40\0\0\0\37\202\0\0\0\36\20\0\0\0\40\0\0\0" + "'\0\0\0""1\0\0\0<\0\0\0B\0\0\0\77\0\0\0""5\0\0\0+\0\0\0'\0\0\0-\0\0\0" + ";\0\0\0I\0\0\0P\0\0\0M\0\0\0A\0\0\0""3\202\0\0\0+1\0\0\0""5\0\0\0B\0" + "\0\0K\0\0\0N\0\0\0H\0\0\0>\0\0\0""1\0\0\0&\0\0\0\35\0\0\0\26\0\0\0\24" + "\0\0\0\27\0\0\0!\0\0\0""0\0\0\0@\0\0\0K\0\0\0N\0\0\0H\0\0\0>\0\0\0""1" + "\0\0\0&\0\0\0\35\0\0\0\27\0\0\0\25\0\0\0\33\0\0\0(\0\0\0<\0\0\0Q\0\0" + "\0`\0\0\0d\0\0\0]\0\0\0R\0\0\0G\0\0\0B\0\0\0D\0\0\0M\0\0\0U\0\0\0X\0" + "\0\0P\0\0\0@\0\0\0,\0\0\0\32\0\0\0\15\0\0\0\6\0\0\0\2\303\303\303\0\317" + "\317\317\0\326\326\326\0\332\332\332\0\203\333\333\333\0\202\0\0\0\0" + "\7\0\0\0\1\0\0\0\2\0\0\0\4\0\0\0\7\0\0\0\12\0\0\0\14\0\0\0\15\203\0\0" + "\0\16\202\0\0\0\17\30\0\0\0\21\0\0\0\24\0\0\0\27\0\0\0\34\0\0\0!\0\0" + "\0'\0\0\0,\0\0\0""0\0\0\0""2\0\0\0""0\0\0\0,\0\0\0%\0\0\0\36\0\0\0\30" + "\0\0\0\27\0\0\0\34\0\0\0'\0\0\0""4\0\0\0\77\35*>bCb\222\375C`\221\367" + "\17\26\40`\0\0\0[\202\0\0\0bJ\0\0\0Y\0\0\0J\0\0\0\77\0\0\0:\0\0\0>\0" + "\0\0D\0\0\0G8Qx\265=X\202\371\20\30#S\0\0\0J\0\0\0P\0\0\0S\0\0\0O\0\0" + "\0D\0\0\0""6\0\0\0)\0\0\0\40\0\0\0\32\0\0\0\27\0\0\0\25\0\0\0\24\0\0" + "\0\23\0\0\0\26\0\0\0\35\0\0\0&\0\0\0/\0\0\0""2\0\0\0/\0\0\0'\0\0\0\37" + "\0\0\0\36\0\0\0$\0\0\0""0\0\0\0;\0\0\0@\0\0\0<\0\0\0""2\0\0\0&\0\0\0" + "\36\0\0\0\37\0\0\0'\0\0\0""3\0\0\0=\0\0\0B\0\0\0A\0\0\0:\0\0\0""0\0\0" + "\0%\0\0\0\34\0\0\0\25\0\0\0\21\0\0\0\22\0\0\0\31\0\0\0$\0\0\0""2\0\0" + "\0=\0\0\0B\0\0\0A\0\0\0:\0\0\0""0\0\0\0%\0\0\0\34\0\0\0\25\0\0\0\23\0" + "\0\0\26\0\0\0!\0\0\0""1\0\0\0C\0\0\0P\0\0\0U\0\0\0Q\0\0\0H\0\0\0>\202" + "\0\0\0""9\1\0\0\0>\202\0\0\0D\13\0\0\0>\0\0\0""0\0\0\0!\0\0\0\23\0\0" + "\0\11\0\0\0\4\0\0\0\1\310\310\310\0\322\322\322\0\327\327\327\0\332\332" + "\332\0\203\333\333\333\0\203\0\0\0\0\7\0\0\0\1\0\0\0\2\0\0\0\3\0\0\0" + "\5\0\0\0\7\0\0\0\10\0\0\0\12\202\0\0\0\13\11\0\0\0\14\0\0\0\15\0\0\0" + "\16\0\0\0\20\0\0\0\23\0\0\0\26\0\0\0\32\0\0\0\35\0\0\0\37\202\0\0\0!" + "&\0\0\0\37\0\0\0\33\0\0\0\27\0\0\0\23\0\0\0\20\0\0\0\21\3\4\7\27\26!" + "1*\40/E@\26\37/C\77\\\207\274Dc\222\3778Ry\253\0\0\0S\0\0\0]\0\0\0a\0" + "\0\0[\0\0\0N\0\0\0=\0\0\0""0\0\0\0+\0\0\0-\0\0\0""2\32'9N=Y\203\3739" + "Rz\260\0\0\0A\0\0\0I\0\0\0P\0\0\0O\0\0\0G\0\0\0""9\0\0\0+\0\0\0\36\0" + "\0\0\26\0\0\0\21\0\0\0\16\0\0\0\14\202\0\0\0\13\7\0\0\0\15\0\0\0\23\0" + "\0\0\32\0\0\0\40\0\0\0\"\0\0\0\37\0\0\0\31\202\0\0\0\24\14\0\0\0\31\0" + "\0\0\"\0\0\0*\0\0\0-\0\0\0)\0\0\0!\0\0\0\31\0\0\0\23\0\0\0\24\0\0\0\32" + "\0\0\0#\0\0\0,\202\0\0\0""2\5\0\0\0/\0\0\0(\0\0\0\37\0\0\0\27\0\0\0\20" + "\202\0\0\0\14\25\0\0\0\20\0\0\0\30\0\0\0\"\0\0\0,\0\0\0""1\0\0\0""2\0" + "\0\0/\0\0\0(\0\0\0\37\0\0\0\27\0\0\0\21\0\0\0\16\0\0\0\20\0\0\0\27\0" + "\0\0#\0\0\0""0\0\0\0;\0\0\0\77\0\0\0=\0\0\0""6\0\0\0.\202\0\0\0)\1\0" + "\0\0,\202\0\0\0/\12\0\0\0*\0\0\0\40\0\0\0\26\0\0\0\14\0\0\0\6\0\0\0\2" + "\276\276\276\0\314\314\314\0\324\324\324\0\330\330\330\0\204\333\333" + "\333\0\205\0\0\0\0\6\0\0\0\1\0\0\0\2\0\0\0\3\0\0\0\5\0\0\0\6\0\0\0\7" + "\202\0\0\0\10\7\0\0\0\11\0\0\0\12\0\0\0\14\0\0\0\15\0\0\0\17\0\0\0\20" + "\0\0\0\22\203\0\0\0\23&\0\0\0\21\0\0\0\20\31$6\25Ba\217]Ff\230\241Ee" + "\225\310Ed\225\276Ba\217\236=Y\203\177@\\\211\244Dc\222\377Db\221\377" + "+>\\\177\0\0\0U\0\0\0]\0\0\0^\0\0\0T\0\0\0B\0\0\0""0\0\0\0#\0\0\0\35" + "\0\0\0\36\0\0\0\"=X\202\251>Y\203\377\40.ET\0\0\0\77\0\0\0H\0\0\0L\0" + "\0\0H\0\0\0=\0\0\0-\0\0\0\37\0\0\0\24\0\0\0\16\0\0\0\12\0\0\0\10\0\0" + "\0\6\202\0\0\0\5\34\0\0\0\7\0\0\0\13\0\0\0\17\0\0\0\23\0\0\0\24\0\0\0" + "\22\0\0\0\16\0\0\0\13\0\0\0\14\0\0\0\17\0\0\0\24\0\0\0\31\0\0\0\33\0" + "\0\0\30\0\0\0\23\0\0\0\16\0\0\0\12\0\0\0\13\0\0\0\16\0\0\0\24\0\0\0\33" + "\0\0\0\37\0\0\0!\0\0\0\37\0\0\0\33\0\0\0\25\0\0\0\20\0\0\0\13\202\0\0" + "\0\7\13\0\0\0\11\0\0\0\15\0\0\0\24\0\0\0\33\0\0\0\37\0\0\0!\0\0\0\37" + "\0\0\0\33\0\0\0\25\0\0\0\20\0\0\0\13\202\0\0\0\11\13\0\0\0\16\0\0\0\25" + "\0\0\0\36\0\0\0$\0\0\0(\0\0\0&\0\0\0\"\0\0\0\35\0\0\0\32\0\0\0\31\0\0" + "\0\32\202\0\0\0\34\12\0\0\0\30\0\0\0\23\0\0\0\14\0\0\0\7\0\0\0\3\0\0" + "\0\1\304\304\304\0\317\317\317\0\325\325\325\0\331\331\331\0\204\333" + "\333\333\0\207\0\0\0\0\1\0\0\0\1\202\0\0\0\2\1\0\0\0\3\202\0\0\0\4\2" + "\0\0\0\5\0\0\0\6\202\0\0\0\7\1\0\0\0\10\206\0\0\0\11\23Ec\223]Fg\230" + "\351Ff\230\356B`\216n\24\35,\35\0\0\0\33\0\0\0\37\0\0\0$@]\211\255Dc" + "\221\377;U~\250\"2Js$4M\205\0\0\0_\0\0\0Z\0\0\0L\0\0\0""8\0\0\0%\0\0" + "\0\30\202\0\0\0\22\5.Cc8>Z\204\374=Y\203\276\0\0\0""2\0\0\0\77\202\0" + "\0\0H\10\0\0\0@\0\0\0""2\0\0\0\"\0\0\0\25\0\0\0\15\0\0\0\7\0\0\0\5\0" + "\0\0\3\203\0\0\0\2\13\0\0\0\3\0\0\0\5\0\0\0\10\0\0\0\11\0\0\0\12\0\0" + "\0\11\0\0\0\7\0\0\0\5\0\0\0\6\0\0\0\10\0\0\0\12\202\0\0\0\15\3\0\0\0" + "\14\0\0\0\11\0\0\0\6\202\0\0\0\5\4\0\0\0\7\0\0\0\12\0\0\0\16\0\0\0\21" + "\202\0\0\0\22\4\0\0\0\17\0\0\0\14\0\0\0\11\0\0\0\5\202\0\0\0\3\5\0\0" + "\0\4\0\0\0\6\0\0\0\12\0\0\0\16\0\0\0\21\202\0\0\0\22\4\0\0\0\17\0\0\0" + "\14\0\0\0\11\0\0\0\6\202\0\0\0\4\4\0\0\0\7\0\0\0\13\0\0\0\17\0\0\0\23" + "\202\0\0\0\25\5\0\0\0\22\0\0\0\17\0\0\0\15\0\0\0\14\0\0\0\15\202\0\0" + "\0\16\12\0\0\0\14\0\0\0\11\0\0\0\6\0\0\0\3\0\0\0\1\276\276\276\0\312" + "\312\312\0\323\323\323\0\327\327\327\0\332\332\332\0\204\333\333\333" + "\0\211\0\0\0\0\203\0\0\0\1\203\0\0\0\2\206\0\0\0\3\26\0\0\0\4\0\0\0\5" + "Gg\231`Gh\230\377Fg\230\3520Fg9\0\0\0\35\0\0\0#\0\0\0&\0\0\0)-A`YDb\222" + "\375A^\214\333\12\17\26M\0\0\0S\0\0\0]\0\0\0^\0\0\0U\0\0\0C\0\0\0.\0" + "\0\0\34\0\0\0\20\202\0\0\0\13\15@]\210\257=Y\203\3770Ef\\\0\0\0""5\0" + "\0\0A\0\0\0G\0\0\0C\0\0\0""7\0\0\0'\0\0\0\30\0\0\0\15\0\0\0\7\0\0\0\3" + "\202\0\0\0\1\203\0\0\0\0\3\0\0\0\1\0\0\0\2\0\0\0\3\202\0\0\0\4\1\0\0" + "\0\3\203\0\0\0\2\2\0\0\0\3\0\0\0\4\203\0\0\0\5\3\0\0\0\3\0\0\0\2\0\0" + "\0\1\202\0\0\0\2\3\0\0\0\4\0\0\0\6\0\0\0\7\202\0\0\0\10\4\0\0\0\7\0\0" + "\0\6\0\0\0\4\0\0\0\2\203\0\0\0\1\4\0\0\0\2\0\0\0\4\0\0\0\6\0\0\0\7\202" + "\0\0\0\10\3\0\0\0\7\0\0\0\6\0\0\0\4\203\0\0\0\2\4\0\0\0\3\0\0\0\4\0\0" + "\0\6\0\0\0\10\202\0\0\0\11\2\0\0\0\10\0\0\0\6\203\0\0\0\5\1\0\0\0\6\202" + "\0\0\0\5\10\0\0\0\3\0\0\0\2\0\0\0\1\277\277\277\0\310\310\310\0\320\320" + "\320\0\325\325\325\0\331\331\331\0\205\333\333\333\0\223\0\0\0\0\203" + "\0\0\0\1$\23\33(\4Fg\231\346Gg\230\377>\\\207k\0\0\0#\0\0\0,\0\0\0""2" + "\0\0\0""4\27\"2EB`\217\346A`\215\344\31$6V\0\0\0L\0\0\0X\0\0\0^\0\0\0" + "Z\0\0\0M\0\0\0""9\0\0\0%\0\0\0\25\0\0\0\13\0\0\0\7" + "Z\205\323\1\2\3+\0\0\0:\0\0\0E\0\0\0G\0\0\0>\0\0\0/\0\0\0\37\0\0\0\21" + "\0\0\0\10\0\0\0\3\0\0\0\1\207\0\0\0\0\204\0\0\0\1\203\0\0\0\0\206\0\0" + "\0\1\204\0\0\0\0\1\0\0\0\1\202\0\0\0\2\202\0\0\0\3\202\0\0\0\2\1\0\0" + "\0\1\205\0\0\0\0\1\0\0\0\1\202\0\0\0\2\202\0\0\0\3\202\0\0\0\2\1\0\0" + "\0\1\203\0\0\0\0\202\0\0\0\1\1\0\0\0\2\203\0\0\0\3\202\0\0\0\2\207\0" + "\0\0\1\7\302\302\302\0\306\306\306\0\312\312\312\0\320\320\320\0\324" + "\324\324\0\330\330\330\0\332\332\332\0\205\333\333\333\0\225\0\0\0\0" + "$\0\0\0\1=Z\205\27Gf\230\377Gf\226\377\26\40//\0\0\0""1\0\0\0<\0\0\0" + "A\35*>ZA`\216\337\77]\212\314\25\37.N\0\0\0E\0\0\0O\0\0\0X\0\0\0Y\0\0" + "\0Q\0\0\0B\0\0\0.\0\0\0\34\0\0\0\17\0\0\0\10\0\0\0\6\77]\211\276=Y\203" + "\3777Pvp\0\0\0""2\0\0\0A\0\0\0H\0\0\0E\0\0\0""8\0\0\0(\0\0\0\30\0\0\0" + "\14\0\0\0\5\0\0\0\2\277\0\0\0\0\202\316\316\316\0\5\320\320\320\0\323" + "\323\323\0\325\325\325\0\330\330\330\0\332\332\332\0\206\333\333\333" + "\0\225\0\0\0\0$\0\0\0\2\22\33(\10Ef\227\305Ff\226\3778Qxy\25\37.N#4L" + "o9Sz\254A^\213\3271Hk\205\2\3\5>\0\0\0>\0\0\0E\0\0\0M\0\0\0R\0\0\0O\0" + "\0\0D\0\0\0""4\0\0\0#\0\0\0\24\0\0\0\13\0\0\0\6@]\211G=Y\203\377=Y\203" + "\347\11\15\24-\0\0\0""9\0\0\0F\0\0\0I\0\0\0A\0\0\0""2\0\0\0!\0\0\0\22" + "\0\0\0\10\0\0\0\3\0\0\0\1\277\0\0\0\0\1\325\325\325\0\202\326\326\326" + "\0\3\330\330\330\0\331\331\331\0\332\332\332\0\207\333\333\333\0\225" + "\0\0\0\0\15\0\0\0\2\0\0\0\7\13\20\30\24""5MrX8Qx\2045Nt\226.Cc\214\33" + "':i\0\0\0H\0\0\0@\0\0\0;\0\0\0<\0\0\0A\202\0\0\0E\24\0\0\0\77\0\0\0""3" + "\0\0\0%\0\0\0\27\0\0\0\15\0\0\0\7\0\0\0\5@_\214t\77[\207\2467Qvi\0\0" + "\0.\0\0\0>\0\0\0G\0\0\0F\0\0\0;\0\0\0*\0\0\0\32\0\0\0\15\0\0\0\6\0\0" + "\0\2\300\0\0\0\0\204\332\332\332\0\211\333\333\333\0\225\0\0\0\0\6\0" + "\0\0\2\0\0\0\7\0\0\0\21\0\0\0\37\0\0\0""1\0\0\0A\202\0\0\0J\14\0\0\0" + "C\0\0\0;\0\0\0""7\0\0\0""6\0\0\0""8\0\0\0""9\0\0\0""6\0\0\0.\0\0\0#\0" + "\0\0\30\0\0\0\16\0\0\0\10\202\0\0\0\4\15\0\0\0\10\0\0\0\21\0\0\0\37\0" + "\0\0""0\0\0\0>\0\0\0D\0\0\0\77\0\0\0""2\0\0\0\"\0\0\0\23\0\0\0\11\0\0" + "\0\4\0\0\0\1\300\0\0\0\0\215\333\333\333\0\225\0\0\0\0\6\0\0\0\2\0\0" + "\0\6\0\0\0\17\0\0\0\33\0\0\0+\0\0\0""8\202\0\0\0@\32\0\0\0:\0\0\0""4" + "\0\0\0/\0\0\0.\0\0\0-\0\0\0+\0\0\0&\0\0\0\36\0\0\0\26\0\0\0\16\0\0\0" + "\7\0\0\0\4\0\0\0\2\0\0\0\4\0\0\0\10\0\0\0\21\0\0\0\36\0\0\0,\0\0\0""7" + "\0\0\0:\0\0\0""4\0\0\0'\0\0\0\31\0\0\0\15\0\0\0\6\0\0\0\2\301\0\0\0\0" + "\215\333\333\333\0\225\0\0\0\0\6\0\0\0\1\0\0\0\5\0\0\0\13\0\0\0\24\0" + "\0\0\40\0\0\0*\202\0\0\0""1\13\0\0\0-\0\0\0)\0\0\0%\0\0\0#\0\0\0\40\0" + "\0\0\35\0\0\0\30\0\0\0\22\0\0\0\14\0\0\0\7\0\0\0\3\202\0\0\0\1\15\0\0" + "\0\3\0\0\0\7\0\0\0\17\0\0\0\31\0\0\0$\0\0\0,\0\0\0-\0\0\0&\0\0\0\34\0" + "\0\0\21\0\0\0\11\0\0\0\3\0\0\0\1\301\0\0\0\0\215\333\333\333\0\225\0" + "\0\0\0\32\0\0\0\1\0\0\0\3\0\0\0\7\0\0\0\15\0\0\0\24\0\0\0\34\0\0\0\40" + "\0\0\0!\0\0\0\37\0\0\0\34\0\0\0\31\0\0\0\27\0\0\0\24\0\0\0\21\0\0\0\15" + "\0\0\0\11\0\0\0\5\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\5\0\0" + "\0\13\0\0\0\22\0\0\0\31\202\0\0\0\36\5\0\0\0\31\0\0\0\22\0\0\0\12\0\0" + "\0\5\0\0\0\2\302\0\0\0\0\215\333\333\333\0\226\0\0\0\0\21\0\0\0\1\0\0" + "\0\3\0\0\0\7\0\0\0\13\0\0\0\17\0\0\0\22\0\0\0\23\0\0\0\22\0\0\0\20\0" + "\0\0\17\0\0\0\15\0\0\0\12\0\0\0\10\0\0\0\6\0\0\0\4\0\0\0\2\0\0\0\1\203" + "\0\0\0\0\14\0\0\0\1\0\0\0\3\0\0\0\7\0\0\0\13\0\0\0\17\0\0\0\22\0\0\0" + "\21\0\0\0\16\0\0\0\12\0\0\0\5\0\0\0\2\0\0\0\1\302\0\0\0\0\215\333\333" + "\333\0\227\0\0\0\0\4\0\0\0\1\0\0\0\3\0\0\0\5\0\0\0\7\203\0\0\0\11\7\0" + "\0\0\10\0\0\0\7\0\0\0\6\0\0\0\4\0\0\0\3\0\0\0\2\0\0\0\1\206\0\0\0\0\12" + "\0\0\0\1\0\0\0\3\0\0\0\5\0\0\0\7\0\0\0\11\0\0\0\10\0\0\0\6\0\0\0\4\0" + "\0\0\2\0\0\0\1\303\0\0\0\0\215\333\333\333\0\230\0\0\0\0\1\0\0\0\1\202" + "\0\0\0\2\205\0\0\0\3\1\0\0\0\2\202\0\0\0\1\211\0\0\0\0\2\0\0\0\1\0\0" + "\0\2\203\0\0\0\3\1\0\0\0\2\202\0\0\0\1\304\0\0\0\0\215\333\333\333\0"}; + + diff --git a/src/icons/sylpheed-logo.png b/src/icons/sylpheed-logo.png new file mode 100644 index 0000000..1423da5 Binary files /dev/null and b/src/icons/sylpheed-logo.png differ diff --git a/src/icons/sylpheed.ico b/src/icons/sylpheed.ico new file mode 100644 index 0000000..f7cd1f8 Binary files /dev/null and b/src/icons/sylpheed.ico differ diff --git a/src/icons/unread.xpm b/src/icons/unread.xpm new file mode 100644 index 0000000..b3c31e4 --- /dev/null +++ b/src/icons/unread.xpm @@ -0,0 +1,50 @@ +/* XPM */ +static char * unread_xpm[] = { +"13 10 37 1", +" c None", +". c #000000", +"+ c #3D81C1", +"@ c #D4E3F0", +"# c #D5E3F1", +"$ c #2A5984", +"% c #D1E0EF", +"& c #D1E1EF", +"* c #D2E2F0", +"= c #94B9DC", +"- c #C0D5EA", +"; c #D4E2F0", +"> c #C2D5EA", +", c #80AAD6", +"' c #346FA6", +") c #C9D9ED", +"! c #CFDEEF", +"~ c #C9DBED", +"{ c #3673AC", +"] c #3B7EBD", +"^ c #D2E1F0", +"/ c #8EB0DA", +"( c #A6C1E2", +"_ c #77A0D3", +": c #CCDCEE", +"< c #D1DFEF", +"[ c #8CAFDA", +"} c #295883", +"| c #BFD4E9", +"1 c #7AA9D4", +"2 c #6B9CCF", +"3 c #91B4DB", +"4 c #ABC4E3", +"5 c #9DBCDF", +"6 c #7DA6D5", +"7 c #A4C0E0", +"8 c #1E4060", +" ........... ", +".+@##@@##@@$.", +".@+@@#%@&*+=.", +".@-+#@*;*+>,.", +".#@@+#@#')>>.", +".!#~+{@]+^!/.", +".#%+@;+^!+(_.", +".@+&@;: +#include +#include +#include +#include +#include + +#include "mainwindow.h" +#include "prefs_common.h" +#include "procmime.h" +#include "imageview.h" +#include "utils.h" + +static gboolean get_resized_size(gint w, + gint h, + gint aw, + gint ah, + gint *sw, + gint *sh); + +static gint button_press_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer data); +static void size_allocate_cb (GtkWidget *widget, + GtkAllocation *allocation, + gpointer data); + +ImageView *imageview_create(void) +{ + ImageView *imageview; + GtkWidget *scrolledwin; + + debug_print(_("Creating image view...\n")); + imageview = g_new0(ImageView, 1); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_widget_set_size_request + (scrolledwin, prefs_common.mainview_width, -1); + + g_signal_connect(G_OBJECT(scrolledwin), "button_press_event", + G_CALLBACK(button_press_cb), imageview); + g_signal_connect(G_OBJECT(scrolledwin), "size_allocate", + G_CALLBACK(size_allocate_cb), imageview); + + gtk_widget_show_all(scrolledwin); + + imageview->scrolledwin = scrolledwin; + imageview->image = NULL; + imageview->image_data = NULL; + imageview->resize = FALSE; + imageview->resizing = FALSE; + + return imageview; +} + +void imageview_init(ImageView *imageview) +{ +} + +void imageview_show_image(ImageView *imageview, MimeInfo *mimeinfo, + const gchar *file, gboolean resize) +{ + GdkPixbuf *pixbuf; + GdkPixbuf *rotated; + GError *error = NULL; + + g_return_if_fail(imageview != NULL); + g_return_if_fail(imageview->scrolledwin != NULL); + g_return_if_fail(imageview->scrolledwin->parent != NULL); + + if (file) { + imageview_clear(imageview); + pixbuf = gdk_pixbuf_new_from_file(file, &error); + imageview->image_data = pixbuf; + } else { + pixbuf = (GdkPixbuf *)imageview->image_data; + } + + if (error != NULL) { + g_warning("%s\n", error->message); + g_error_free(error); + } + + if (!pixbuf) { + g_warning(_("Can't load the image.")); + return; + } + + imageview->resize = resize; + + pixbuf = rotated = imageview_get_rotated_pixbuf(pixbuf); + + if (resize) { + pixbuf = imageview_get_resized_pixbuf + (pixbuf, imageview->scrolledwin->parent, 8); + } else + g_object_ref(pixbuf); + + g_object_unref(rotated); + + if (!imageview->image) { + imageview->image = gtk_image_new_from_pixbuf(pixbuf); + + gtk_scrolled_window_add_with_viewport + (GTK_SCROLLED_WINDOW(imageview->scrolledwin), + imageview->image); + } else + gtk_image_set_from_pixbuf(GTK_IMAGE(imageview->image), pixbuf); + + g_object_unref(pixbuf); + + gtk_widget_show(imageview->image); +} + +void imageview_clear(ImageView *imageview) +{ + GtkAdjustment *hadj, *vadj; + + if (imageview->image) + gtk_image_set_from_pixmap(GTK_IMAGE(imageview->image), + NULL, NULL); + hadj = gtk_scrolled_window_get_hadjustment + (GTK_SCROLLED_WINDOW(imageview->scrolledwin)); + gtk_adjustment_set_value(hadj, 0.0); + vadj = gtk_scrolled_window_get_vadjustment + (GTK_SCROLLED_WINDOW(imageview->scrolledwin)); + gtk_adjustment_set_value(vadj, 0.0); + + if (imageview->image_data) { + g_object_unref(imageview->image_data); + imageview->image_data = NULL; + } +} + +void imageview_destroy(ImageView *imageview) +{ + imageview_clear(imageview); + g_free(imageview); +} + +GdkPixbuf *imageview_get_resized_pixbuf(GdkPixbuf *pixbuf, GtkWidget *parent, + gint margin) +{ + gint avail_width; + gint avail_height; + gint new_width; + gint new_height; + + avail_width = parent->allocation.width; + avail_height = parent->allocation.height; + if (avail_width > margin) avail_width -= margin; + if (avail_height > margin) avail_height -= margin; + + if (get_resized_size(gdk_pixbuf_get_width(pixbuf), + gdk_pixbuf_get_height(pixbuf), + avail_width, avail_height, + &new_width, &new_height)) + return gdk_pixbuf_scale_simple + (pixbuf, new_width, new_height, GDK_INTERP_BILINEAR); + + g_object_ref(pixbuf); + return pixbuf; +} + +static gboolean get_resized_size(gint w, gint h, gint aw, gint ah, + gint *sw, gint *sh) +{ + gfloat wratio = 1.0; + gfloat hratio = 1.0; + gfloat ratio = 1.0; + + if (w <= aw && h <= ah) { + *sw = w; + *sh = h; + return FALSE; + } + + if (w > aw) + wratio = (gfloat)aw / (gfloat)w; + if (h > ah) + hratio = (gfloat)ah / (gfloat)h; + + ratio = (wratio > hratio) ? hratio : wratio; + + *sw = (gint)(w * ratio); + *sh = (gint)(h * ratio); + + /* restrict minimum size */ + if (*sw < 16 || *sh < 16) { + wratio = 16.0 / (gfloat)w; + hratio = 16.0 / (gfloat)h; + ratio = (wratio > hratio) ? wratio : hratio; + if (ratio >= 1.0) { + *sw = w; + *sh = h; + } else { + *sw = (gint)(w * ratio); + *sh = (gint)(h * ratio); + } + } + + return TRUE; +} + +GdkPixbuf *imageview_get_rotated_pixbuf(GdkPixbuf *pixbuf) +{ +#if GTK_CHECK_VERSION(2, 12, 0) + return gdk_pixbuf_apply_embedded_orientation(pixbuf); +#else + const gchar *orientation_str; + gint orientation; + GdkPixbuf *rotated; + GdkPixbuf *new_pixbuf; + + orientation_str = gdk_pixbuf_get_option(pixbuf, "orientation"); + if (!orientation_str) + return g_object_ref(pixbuf); + + orientation = strtol(orientation_str, NULL, 10); + + switch (orientation) { + case 1: + new_pixbuf = g_object_ref(pixbuf); + break; + case 2: + new_pixbuf = gdk_pixbuf_flip(pixbuf, TRUE); + break; + case 3: + new_pixbuf = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_UPSIDEDOWN); + break; + case 4: + new_pixbuf = gdk_pixbuf_flip(pixbuf, FALSE); + break; + case 5: + rotated = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE); + new_pixbuf = gdk_pixbuf_flip(rotated, TRUE); + g_object_unref(rotated); + break; + case 6: + new_pixbuf = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE); + break; + case 7: + rotated = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE); + new_pixbuf = gdk_pixbuf_flip(rotated, FALSE); + g_object_unref(rotated); + break; + case 8: + new_pixbuf = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE); + break; + default: + new_pixbuf = g_object_ref(pixbuf); + break; + } + + return new_pixbuf; +#endif +} + +static gint button_press_cb(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + ImageView *imageview = (ImageView *)data; + + if (event->button == 1 && imageview->image) { + imageview_show_image(imageview, NULL, NULL, !imageview->resize); + return TRUE; + } + return FALSE; +} + +static void size_allocate_cb(GtkWidget *widget,GtkAllocation *allocation, + gpointer data) +{ + ImageView *imageview = (ImageView *)data; + + if (imageview->resize) { + if (imageview->resizing) { + imageview->resizing = FALSE; + return; + } + if (!imageview->scrolledwin->parent) + return; + if (!imageview->image_data) + return; + imageview_show_image(imageview, NULL, NULL, TRUE); + imageview->resizing = TRUE; + } +} diff --git a/src/imageview.h b/src/imageview.h new file mode 100644 index 0000000..48686b7 --- /dev/null +++ b/src/imageview.h @@ -0,0 +1,59 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2013 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __IMAGEVIEW_H__ +#define __IMAGEVIEW_H__ + +#include +#include +#include + +typedef struct _ImageView ImageView; + +#include "messageview.h" +#include "procmime.h" + +struct _ImageView +{ + GtkWidget *scrolledwin; + GtkWidget *image; + + gpointer image_data; + gboolean resize; + gboolean resizing; + + MessageView *messageview; +}; + +ImageView *imageview_create (void); +void imageview_init (ImageView *imageview); +void imageview_show_image (ImageView *imageview, + MimeInfo *mimeinfo, + const gchar *file, + gboolean resize); +void imageview_clear (ImageView *imageview); +void imageview_destroy (ImageView *imageview); + +GdkPixbuf *imageview_get_resized_pixbuf (GdkPixbuf *pixbuf, + GtkWidget *parent, + gint margin); + +GdkPixbuf *imageview_get_rotated_pixbuf (GdkPixbuf *pixbuf); + +#endif /* __IMAGEVIEW_H__ */ diff --git a/src/import.c b/src/import.c new file mode 100644 index 0000000..a59f11f --- /dev/null +++ b/src/import.c @@ -0,0 +1,785 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * DBX file conversion engine is based on OutlookExpress-To by Tietew. + * OutlookExpress-To - OE5/6 Multi Converter + * Copyright (C) 2002 by Tietew + * http://www.tietew.net/ + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "main.h" +#include "inc.h" +#include "mbox.h" +#include "folderview.h" +#include "menu.h" +#include "filesel.h" +#include "foldersel.h" +#include "gtkutils.h" +#include "manage_window.h" +#include "folder.h" +#include "procmsg.h" +#include "procheader.h" +#include "progressdialog.h" +#include "alertpanel.h" + +enum +{ + IMPORT_MBOX, + IMPORT_EML_FOLDER, + IMPORT_DBX +}; + +static GtkWidget *window; +static GtkWidget *format_optmenu; +static GtkWidget *desc_label; +static GtkWidget *file_label; +static GtkWidget *file_entry; +static GtkWidget *dest_entry; +static GtkWidget *file_button; +static GtkWidget *dest_button; +static GtkWidget *ok_button; +static GtkWidget *cancel_button; +static gboolean import_finished; +static gboolean import_ack; +static ProgressDialog *progress; +static gboolean import_progress_cancelled; + +static void import_create (void); +static gint import_do (void); +static gint import_eml_folder (FolderItem *dest, + const gchar *path); +static gint import_dbx (FolderItem *dest, + const gchar *file); + +static void import_format_menu_cb (GtkWidget *widget, + gpointer data); + +static void import_ok_cb (GtkWidget *widget, + gpointer data); +static void import_cancel_cb (GtkWidget *widget, + gpointer data); +static void import_filesel_cb (GtkWidget *widget, + gpointer data); +static void import_destsel_cb (GtkWidget *widget, + gpointer data); +static gint delete_event (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); + +static void import_progress_cancel_cb (GtkWidget *widget, + gpointer data); +static gint import_progress_delete_event(GtkWidget *widget, + GdkEventAny *event, + gpointer data); + + +static gboolean import_mbox_func(Folder *folder, FolderItem *item, guint count, guint total, gpointer data) +{ + gchar str[64]; + static GTimeVal tv_prev = {0, 0}; + GTimeVal tv_cur; + + g_get_current_time(&tv_cur); + g_snprintf(str, sizeof(str), "%u", count); + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress->progressbar), str); + + if (tv_prev.tv_sec == 0 || + (tv_cur.tv_sec - tv_prev.tv_sec) * G_USEC_PER_SEC + + tv_cur.tv_usec - tv_prev.tv_usec > 100 * 1000) { + gtk_progress_bar_pulse(GTK_PROGRESS_BAR(progress->progressbar)); + ui_update(); + tv_prev = tv_cur; + } + + if (import_progress_cancelled) + return FALSE; + else + return TRUE; +} + +gint import_mail(FolderItem *default_dest) +{ + gint ok = 0; + gchar *dest_id = NULL; + + import_create(); + + if (default_dest && default_dest->path) + dest_id = folder_item_get_identifier(default_dest); + + if (dest_id) { + gtk_entry_set_text(GTK_ENTRY(dest_entry), dest_id); + g_free(dest_id); + } + gtk_widget_grab_focus(file_entry); + + manage_window_set_transient(GTK_WINDOW(window)); + + import_finished = FALSE; + import_ack = FALSE; + import_progress_cancelled = FALSE; + + inc_lock(); + + while (!import_finished) + gtk_main_iteration(); + + if (import_ack) + ok = import_do(); + + gtk_widget_destroy(window); + window = NULL; + format_optmenu = NULL; + desc_label = file_label = NULL; + file_entry = dest_entry = NULL; + file_button = dest_button = ok_button = cancel_button = NULL; + + inc_unlock(); + + return ok; +} + +static gint import_do(void) +{ + gint ok = 0; + const gchar *utf8filename, *destdir; + FolderItem *dest; + gchar *filename; + gchar *msg; + gint type; + + type = menu_get_option_menu_active_index + (GTK_OPTION_MENU(format_optmenu)); + + utf8filename = gtk_entry_get_text(GTK_ENTRY(file_entry)); + destdir = gtk_entry_get_text(GTK_ENTRY(dest_entry)); + if (!utf8filename || !*utf8filename) + return -1; + + filename = g_filename_from_utf8(utf8filename, -1, NULL, NULL, NULL); + if (!filename) { + g_warning("failed to convert character set."); + filename = g_strdup(utf8filename); + } + if (!g_file_test(filename, G_FILE_TEST_EXISTS)) { + alertpanel_error(_("The source file does not exist.")); + g_free(filename); + return -1; + } + + if (!destdir || !*destdir) { + dest = folder_find_item_from_path(INBOX_DIR); + } else + dest = folder_find_item_from_identifier(destdir); + + if (!dest) { + alertpanel_error(_("Can't find the destination folder.")); + g_free(filename); + return -1; + } + + msg = g_strdup_printf(_("Importing %s ..."), g_basename(utf8filename)); + progress = progress_dialog_simple_create(); + gtk_window_set_title(GTK_WINDOW(progress->window), _("Importing")); + progress_dialog_set_label(progress, msg); + g_free(msg); + gtk_window_set_modal(GTK_WINDOW(progress->window), TRUE); + manage_window_set_transient(GTK_WINDOW(progress->window)); + g_signal_connect(G_OBJECT(progress->cancel_btn), "clicked", + G_CALLBACK(import_progress_cancel_cb), NULL); + g_signal_connect(G_OBJECT(progress->window), "delete_event", + G_CALLBACK(import_progress_delete_event), NULL); + gtk_widget_show(progress->window); + ui_update(); + + if (type == IMPORT_MBOX) { + folder_set_ui_func2(dest->folder, import_mbox_func, NULL); + ok = proc_mbox(dest, filename, NULL); + folder_set_ui_func2(dest->folder, NULL, NULL); + } else if (type == IMPORT_EML_FOLDER) { + ok = import_eml_folder(dest, filename); + } else if (type == IMPORT_DBX) { + ok = import_dbx(dest, filename); + } + + progress_dialog_set_label(progress, _("Scanning folder...")); + ui_update(); + folder_item_scan(dest); + folderview_update_item(dest, TRUE); + + progress_dialog_destroy(progress); + progress = NULL; + + g_free(filename); + + if (ok == -1) + alertpanel_error(_("Error occurred on import.")); + + return ok; +} + +static gint import_eml_folder(FolderItem *dest, const gchar *path) +{ + GDir *dir; + const gchar *dir_name, *p; + gchar *file; + MsgInfo *msginfo; + MsgFlags flags = {MSG_NEW|MSG_UNREAD, MSG_RECEIVED}; + gint count = 0; + gint ok = 0; + + g_return_val_if_fail(dest != NULL, -1); + g_return_val_if_fail(path != NULL, -1); + + if ((dir = g_dir_open(path, 0, NULL)) == NULL) { + g_warning("failed to open directory: %s", path); + return -1; + } + + while ((dir_name = g_dir_read_name(dir)) != NULL) { + if (((p = strrchr(dir_name, '.')) && + !g_ascii_strcasecmp(p + 1, "eml")) || + to_number(dir_name) > 0) { + file = g_strconcat(path, G_DIR_SEPARATOR_S, dir_name, + NULL); + if (!g_file_test(file, G_FILE_TEST_IS_REGULAR)) { + g_free(file); + continue; + } + + msginfo = procheader_parse_file(file, flags, FALSE); + if (!msginfo) { + g_warning("import_eml_folder(): procheader_parse_file() failed."); + g_free(file); + continue; + } + msginfo->file_path = file; + file = NULL; + count++; + if (import_mbox_func(dest->folder, dest, count, 0, NULL) == FALSE) { + ok = -2; + break; + } + ok = folder_item_add_msg_msginfo(dest, msginfo, FALSE); + procmsg_msginfo_free(msginfo); + if (ok < 0) { + g_warning("import_eml_folder(): folder_item_add_msg_msginfo() failed."); + break; + } + } + } + + g_dir_close(dir); + + return ok; +} + +static gint32 read_dword(FILE *fp, off_t offset) +{ + gint32 dw; + + if (fseek(fp, offset, SEEK_SET) < 0) { + perror("read_dword: fseek"); + return 0; + } + + if (fread(&dw, sizeof(dw), 1, fp) != 1) + return 0; + + dw = GINT32_FROM_LE(dw); + + return dw; +} + +static void get_dbx_index(FILE *fp, gint32 table_pos, GArray *array) +{ + gint32 another_pos, data_pos; + gint32 num_index, num_elems; + gint i; + + debug_print("get_dbx_index(%08x)\n", table_pos); + + another_pos = read_dword(fp, table_pos + 0x08); + num_elems = read_dword(fp, table_pos + 0x11) & 0x00ffffff; + num_index = read_dword(fp, table_pos + 0x14); + debug_print("table_pos: %08x another_pos: %08x num_elems: %08x num_index: %08x\n", table_pos, another_pos, num_elems, num_index); + if (another_pos > 0 && num_index > 0) + get_dbx_index(fp, another_pos, array); + + table_pos += 0x18; + for (i = 0; i < num_elems; i++) { + data_pos = read_dword(fp, table_pos); + if (data_pos == 0) { + g_warning("get_dbx_index: null data_pos at %08x", + table_pos); + break; + } + g_array_append_val(array, data_pos); + another_pos = read_dword(fp, table_pos + 0x04); + num_index = read_dword(fp, table_pos + 0x08); + debug_print("data_pos: %08x another_pos: %08x num_index: %08x\n", data_pos, another_pos, num_index); + table_pos += 0x0c; + if (another_pos > 0 && num_index > 0) + get_dbx_index(fp, another_pos, array); + } + + debug_print("get_dbx_index end\n"); +} + +static gint get_dbx_data(FILE *fp, gint32 data_pos, FolderItem *dest) +{ + gchar *tmp; + FILE *outfp; + gint32 mail_flag, news_flag; + gint32 data_ptr, data_len, next_ptr; + MsgFlags flags = {MSG_NEW|MSG_UNREAD, MSG_RECEIVED}; + gint ok = 0; + + debug_print("get_dbx_data(%08x)\n", data_pos); + + mail_flag = read_dword(fp, data_pos + 0x18); + news_flag = read_dword(fp, data_pos + 0x1c); + if ((news_flag & 0x0f) == 4) + data_ptr = news_flag; + else if ((mail_flag & 0x0f) == 4) + data_ptr = mail_flag; + else + return 0; + + if ((data_ptr & 0xff) >= 0x80) + data_ptr >>= 8; + else { + guchar b1 = (guchar)(data_ptr >>= 8); + guchar b2 = (guchar)read_dword(fp, data_pos + 0x0a); + data_ptr = read_dword(fp, data_pos + 0x0c + b2 * 4 + b1); + } + if (data_ptr == 0) { + g_warning("get_dbx_data(%08x): could not get data_ptr.", data_pos); + return 0; + } + + tmp = get_tmp_file(); + if ((outfp = g_fopen(tmp, "wb")) == NULL) { + FILE_OP_ERROR(tmp, "fopen"); + ok = -1; + goto finish; + } + + while (data_ptr) { + data_len = read_dword(fp, data_ptr + 0x08); + next_ptr = read_dword(fp, data_ptr + 0x0c); + if (append_file_part(fp, data_ptr + 0x10, data_len, outfp) < 0) { + fclose(outfp); + g_unlink(tmp); + ok = -1; + goto finish; + } + data_ptr = next_ptr; + } + + if (fclose(outfp) == EOF) { + FILE_OP_ERROR(tmp, "fclose"); + g_unlink(tmp); + ok = -1; + goto finish; + } + + if (folder_item_add_msg(dest, tmp, &flags, TRUE) < 0) { + g_warning("get_dbx_data: folder_item_add_msg() failed."); + g_unlink(tmp); + ok = -1; + } + +finish: + g_free(tmp); + return ok; +} + +static gint import_dbx(FolderItem *dest, const gchar *file) +{ + FILE *fp; + gint32 dw; + gint32 table_pos; + guint count = 0; + GArray *array; + gint i; + + g_return_val_if_fail(dest != NULL, -1); + g_return_val_if_fail(file != NULL, -1); + + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + return -1; + } + + if ((dw = read_dword(fp, 0xc4)) == 0) { + fclose(fp); + return -1; + } + + array = g_array_sized_new(FALSE, FALSE, sizeof(gint32), 1024); + + table_pos = read_dword(fp, 0xe4); + if (table_pos > 0) + get_dbx_index(fp, table_pos, array); + + for (i = 0; i < array->len; i++) { + if (import_mbox_func(dest->folder, dest, count + 1, 0, NULL) == FALSE) + break; + if (get_dbx_data(fp, g_array_index(array, gint32, i), dest) < 0) + break; + count++; + } + + debug_print("import_dbx: %u imported\n", count); + + g_array_free(array, TRUE); + fclose(fp); + + return 0; +} + +gint import_dbx_folders(FolderItem *dest, const gchar *path) +{ + GDir *dir; + const gchar *dir_name, *p; + gchar *orig_name, *folder_name, *file; + gchar *msg; + FolderItem *sub_folder; + gint count; + gint ok = 0; + + g_return_val_if_fail(dest != NULL, -1); + g_return_val_if_fail(dest->folder != NULL, -1); + g_return_val_if_fail(path != NULL, -1); + + if ((dir = g_dir_open(path, 0, NULL)) == NULL) { + g_warning("failed to open directory: %s", path); + return -1; + } + + import_progress_cancelled = FALSE; + + progress = progress_dialog_simple_create(); + gtk_window_set_title(GTK_WINDOW(progress->window), _("Importing")); + progress_dialog_set_label(progress, _("Importing Outlook Express folders")); + gtk_window_set_modal(GTK_WINDOW(progress->window), TRUE); + manage_window_set_transient(GTK_WINDOW(progress->window)); + g_signal_connect(G_OBJECT(progress->cancel_btn), "clicked", + G_CALLBACK(import_progress_cancel_cb), NULL); + g_signal_connect(G_OBJECT(progress->window), "delete_event", + G_CALLBACK(import_progress_delete_event), NULL); + gtk_widget_show(progress->window); + ui_update(); + + while ((dir_name = g_dir_read_name(dir)) != NULL) { + if ((p = strrchr(dir_name, '.')) && + !g_ascii_strcasecmp(p + 1, "dbx")) { + file = g_strconcat(path, G_DIR_SEPARATOR_S, dir_name, + NULL); + orig_name = g_strndup(dir_name, p - dir_name); + if (!g_file_test(file, G_FILE_TEST_IS_REGULAR) || + !g_ascii_strcasecmp(orig_name, "Folders") || + !g_ascii_strcasecmp(orig_name, "Offline") || + !g_ascii_strcasecmp(orig_name, "Pop3uidl")) { + g_free(orig_name); + g_free(file); + continue; + } + + folder_name = g_strdup(orig_name); + count = 1; + while (folder_find_child_item_by_name(dest, folder_name)) { + g_free(folder_name); + folder_name = g_strdup_printf("%s (%d)", orig_name, count++); + } + debug_print("orig_name: %s , folder_name: %s\n", orig_name, folder_name); + + sub_folder = dest->folder->klass->create_folder(dest->folder, dest, folder_name); + if (!sub_folder) { + alertpanel_error(_("Cannot create the folder '%s'."), folder_name); + ok = -1; + break; + } + folderview_append_item(folderview_get(), NULL, sub_folder, TRUE); + folder_write_list(); + msg = g_strdup_printf(_("Importing %s ..."), orig_name); + progress_dialog_set_label(progress, msg); + g_free(msg); + import_dbx(sub_folder, file); + + progress_dialog_set_label(progress, _("Scanning folder...")); + ui_update(); + folder_item_scan(sub_folder); + folderview_update_item(sub_folder, TRUE); + + g_free(folder_name); + g_free(orig_name); + g_free(file); + if (import_progress_cancelled) { + ok = -2; + break; + } + } + } + + g_dir_close(dir); + + progress_dialog_destroy(progress); + progress = NULL; + + folder_write_list(); + + return ok; +} + +static void import_create(void) +{ + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *menu; + GtkWidget *menuitem; + GtkWidget *format_label; + GtkWidget *dest_label; + GtkWidget *confirm_area; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), _("Import")); + gtk_container_set_border_width(GTK_CONTAINER(window), 5); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, FALSE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(delete_event), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + + vbox = gtk_vbox_new(FALSE, 4); + gtk_container_add(GTK_CONTAINER(window), vbox); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 4); + + desc_label = gtk_label_new + (_("Specify source file and destination folder.")); + gtk_box_pack_start(GTK_BOX(hbox), desc_label, FALSE, FALSE, 0); + + table = gtk_table_new(2, 3, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(table), 8); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + gtk_widget_set_size_request(table, 420 * gtkut_get_dpi_multiplier(), -1); + + format_label = gtk_label_new(_("File format:")); + gtk_table_attach(GTK_TABLE(table), format_label, 0, 1, 0, 1, + GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(format_label), 1, 0.5); + + file_label = gtk_label_new(_("Source:")); + gtk_table_attach(GTK_TABLE(table), file_label, 0, 1, 1, 2, + GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(file_label), 1, 0.5); + + dest_label = gtk_label_new(_("Destination folder:")); + gtk_table_attach(GTK_TABLE(table), dest_label, 0, 1, 2, 3, + GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(dest_label), 1, 0.5); + + format_optmenu = gtk_option_menu_new(); + gtk_table_attach(GTK_TABLE(table), format_optmenu, 1, 2, 0, 1, + GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + menu = gtk_menu_new(); + MENUITEM_ADD(menu, menuitem, _("UNIX mbox"), IMPORT_MBOX); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(import_format_menu_cb), NULL); + MENUITEM_ADD(menu, menuitem, _("eml (folder)"), IMPORT_EML_FOLDER); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(import_format_menu_cb), NULL); + MENUITEM_ADD(menu, menuitem, _("Outlook Express (dbx)"), IMPORT_DBX); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(import_format_menu_cb), NULL); + + gtk_option_menu_set_menu(GTK_OPTION_MENU(format_optmenu), menu); + + file_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), file_entry, 1, 2, 1, 2, + GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + dest_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), dest_entry, 1, 2, 2, 3, + GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + file_button = gtk_button_new_with_label(_(" Select... ")); + gtk_table_attach(GTK_TABLE(table), file_button, 2, 3, 1, 2, + 0, 0, 0, 0); + g_signal_connect(G_OBJECT(file_button), "clicked", + G_CALLBACK(import_filesel_cb), NULL); + + dest_button = gtk_button_new_with_label(_(" Select... ")); + gtk_table_attach(GTK_TABLE(table), dest_button, 2, 3, 2, 3, + 0, 0, 0, 0); + g_signal_connect(G_OBJECT(dest_button), "clicked", + G_CALLBACK(import_destsel_cb), NULL); + + gtkut_stock_button_set_create(&confirm_area, + &ok_button, GTK_STOCK_OK, + &cancel_button, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_button); + + g_signal_connect(G_OBJECT(ok_button), "clicked", + G_CALLBACK(import_ok_cb), NULL); + g_signal_connect(G_OBJECT(cancel_button), "clicked", + G_CALLBACK(import_cancel_cb), NULL); + + gtk_widget_show_all(window); +} + +static void import_format_menu_cb(GtkWidget *widget, gpointer data) +{ + gint type; + + type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), + MENU_VAL_ID)); + if (type == IMPORT_EML_FOLDER) { + gtk_label_set_text(GTK_LABEL(desc_label), + _("Specify source folder including eml files and destination folder.")); + } else { + gtk_label_set_text(GTK_LABEL(desc_label), + _("Specify source file and destination folder.")); + } +} + +static void import_ok_cb(GtkWidget *widget, gpointer data) +{ + import_finished = TRUE; + import_ack = TRUE; +} + +static void import_cancel_cb(GtkWidget *widget, gpointer data) +{ + import_finished = TRUE; + import_ack = FALSE; +} + +static void import_filesel_cb(GtkWidget *widget, gpointer data) +{ + gchar *filename; + gchar *utf8_filename; + gint type; + + type = menu_get_option_menu_active_index + (GTK_OPTION_MENU(format_optmenu)); + + if (type == IMPORT_EML_FOLDER) + filename = filesel_select_file + (_("Select importing folder"), NULL, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); + else + filename = filesel_select_file(_("Select importing file"), NULL, + GTK_FILE_CHOOSER_ACTION_OPEN); + if (!filename) return; + + utf8_filename = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); + if (!utf8_filename) { + g_warning("import_filesel_cb(): failed to convert characer set."); + utf8_filename = g_strdup(filename); + } + gtk_entry_set_text(GTK_ENTRY(file_entry), utf8_filename); + g_free(utf8_filename); + + g_free(filename); +} + +static void import_destsel_cb(GtkWidget *widget, gpointer data) +{ + FolderItem *dest; + gchar *dest_id; + + dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL); + if (dest && dest->path) { + dest_id = folder_item_get_identifier(dest); + if (dest_id) { + gtk_entry_set_text(GTK_ENTRY(dest_entry), dest_id); + g_free(dest_id); + } + } +} + +static gint delete_event(GtkWidget *widget, GdkEventAny *event, gpointer data) +{ + import_cancel_cb(NULL, NULL); + return TRUE; +} + +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + if (event && event->keyval == GDK_Escape) + import_cancel_cb(NULL, NULL); + return FALSE; +} + +static void import_progress_cancel_cb(GtkWidget *widget, gpointer data) +{ + debug_print("import cancelled\n"); + import_progress_cancelled = TRUE; +} + +static gint import_progress_delete_event(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + import_progress_cancel_cb(NULL, NULL); + return TRUE; +} diff --git a/src/import.h b/src/import.h new file mode 100644 index 0000000..c9dd4ef --- /dev/null +++ b/src/import.h @@ -0,0 +1,31 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2010 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __IMPORT_H__ +#define __IMPORT_H__ + +#include + +#include "folder.h" + +gint import_mail(FolderItem *default_dest); + +gint import_dbx_folders(FolderItem *dest, const gchar *path); + +#endif /* __IMPORT_H__ */ diff --git a/src/importcsv.c b/src/importcsv.c new file mode 100644 index 0000000..0562495 --- /dev/null +++ b/src/importcsv.c @@ -0,0 +1,1008 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2017 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "addrbook.h" +#include "addressbook.h" +#include "addressitem.h" +#include "filesel.h" +#include "gtkutils.h" +#include "stock_pixmap.h" +#include "prefs_common.h" +#include "manage_window.h" +#include "mgutils.h" +#include "importcsv.h" +#include "codeconv.h" +#include "utils.h" + +#define IMPORTCSV_GUESS_NAME "CSV Import" + +#define PAGE_FILE_INFO 0 +#define PAGE_ATTRIBUTES 1 +#define PAGE_FINISH 2 + +#define IMPORTCSV_WIDTH 480 +#define IMPORTCSV_HEIGHT 320 + +#define FIELDS_N_COLS 3 +#define FIELDS_COL_WIDTH_SELECT 10 +#define FIELDS_COL_WIDTH_FIELD 140 +#define FIELDS_COL_WIDTH_ATTRIB 140 + +typedef enum { + FIELD_COL_SELECT = 0, + FIELD_COL_FIELD = 1, + FIELD_COL_ATTRIB = 2 +} ImpCSV_FieldColPos; + +static struct _ImpCSVDlg { + GtkWidget *window; + GtkWidget *notebook; + GtkWidget *file_entry; + GtkWidget *name_entry; + GtkWidget *comma_radiobtn; + GtkWidget *tab_radiobtn; + GtkWidget *clist_field; + GtkWidget *check_select; + GtkWidget *labelBook; + GtkWidget *labelFile; + GtkWidget *labelRecords; + GtkWidget *btnPrev; + GtkWidget *btnNext; + GtkWidget *btnCancel; + GtkWidget *statusbar; + gint status_cid; + gint rowCount; + gchar *nameBook; + gchar *fileName; + gchar delimiter; + gboolean cancelled; +} impcsv_dlg; + +typedef enum { + ATTR_FIRST_NAME, + ATTR_LAST_NAME, + ATTR_DISPLAY_NAME, + ATTR_NICK_NAME, + ATTR_EMAIL_ADDRESS, + ATTR_REMARKS, + ATTR_ALIAS, + + N_CSV_ATTRIB +} ImpCSVAttribIndex; + +static struct _ImpCSVAttrib { + gchar *name; + gchar *value; + gint col; + gboolean enabled; +} imp_csv_attrib[] = { + {N_("First Name"), NULL, 0, TRUE}, + {N_("Last Name"), NULL, 1, TRUE}, + {N_("Display Name"), NULL, 2, TRUE}, + {N_("Nick Name"), NULL, 3, TRUE}, + {N_("E-Mail Address"), NULL, 4, TRUE}, + {N_("Remarks"), NULL, 5, TRUE}, + {N_("Alias"), NULL, 6, TRUE} +}; + +static AddressBookFile *_importedBook_; +static AddressIndex *_imp_addressIndex_; +static gint importCount = 0; +static gint result; + +static GdkPixmap *markxpm; +static GdkBitmap *markxpmmask; + +static void imp_csv_status_show( gchar *msg ) { + if( impcsv_dlg.statusbar != NULL ) { + gtk_statusbar_pop( GTK_STATUSBAR(impcsv_dlg.statusbar), impcsv_dlg.status_cid ); + if( msg ) { + gtk_statusbar_push( GTK_STATUSBAR(impcsv_dlg.statusbar), impcsv_dlg.status_cid, msg ); + } + } +} + +static void imp_csv_message( void ) { + gchar *sMsg = NULL; + gint pageNum; + + pageNum = gtk_notebook_get_current_page( GTK_NOTEBOOK(impcsv_dlg.notebook) ); + if( pageNum == PAGE_FILE_INFO ) { + sMsg = _( "Please specify address book name and file to import." ); + } + else if( pageNum == PAGE_ATTRIBUTES ) { + sMsg = _( "Select and reorder CSV field names to import." ); + } + else if( pageNum == PAGE_FINISH ) { + sMsg = _( "File imported." ); + } + imp_csv_status_show( sMsg ); +} + +static gchar *imp_csv_guess_file( AddressBookFile *abf ) { + gchar *newFile = NULL; + GList *fileList = NULL; + gint fileNum = 1; + fileList = addrbook_get_bookfile_list( abf ); + if( fileList ) { + fileNum = 1 + abf->maxValue; + } + newFile = addrbook_gen_new_file_name( fileNum ); + g_list_free( fileList ); + fileList = NULL; + return newFile; +} + +static gboolean imp_csv_load_fields( gchar *sFile ) { + GtkCList *clist = GTK_CLIST(impcsv_dlg.clist_field); + FILE *fp; + gchar buf[BUFFSIZE]; + CharSet enc; + + g_return_val_if_fail(sFile != NULL, FALSE); + + impcsv_dlg.rowCount = 0; + gtk_clist_clear( clist ); + + enc = conv_check_file_encoding(sFile); + + if ((fp = g_fopen(sFile, "rb")) == NULL) { + return FALSE; + } + + if (fgets(buf, sizeof(buf), fp) != NULL) { + gchar *str; + gchar **strv; + gchar *text[ FIELDS_N_COLS ]; + gint i; + guint fields_len; + guint data_len = 0; + guint len; + gint row; + + strretchomp(buf); + if (enc == C_UTF_8) + str = g_strdup(buf); + else + str = conv_localetodisp(buf, NULL); + strv = strsplit_csv(str, impcsv_dlg.delimiter, 0); + fields_len = sizeof(imp_csv_attrib) / sizeof(imp_csv_attrib[0]); + while (strv[data_len]) + ++data_len; + len = MAX(fields_len, data_len); + + gtk_clist_freeze(clist); + + for (i = 0; i < len; i++) { + text[ FIELD_COL_SELECT ] = ""; + if (i < data_len) + text[ FIELD_COL_FIELD ] = strv[i]; + else + text[ FIELD_COL_FIELD ] = ""; + if (i < fields_len) + text[ FIELD_COL_ATTRIB ] = + gettext(imp_csv_attrib[i].name); + else + text[ FIELD_COL_ATTRIB ] = ""; + row = gtk_clist_append(clist, text); + if (i < fields_len) { + imp_csv_attrib[i].value = NULL; + imp_csv_attrib[i].col = row; + gtk_clist_set_row_data + (clist, row, &imp_csv_attrib[i]); + if (imp_csv_attrib[i].enabled) + gtk_clist_set_pixmap(clist, row, FIELD_COL_SELECT, markxpm, markxpmmask); + } + } + + gtk_clist_thaw(clist); + gtk_widget_queue_resize(GTK_WIDGET(clist)); + + g_strfreev(strv); + g_free(str); + } + + fclose(fp); + + return TRUE; +} + +static void imp_csv_field_list_selected( GtkCList *clist, gint row, gint column, GdkEvent *event ) { + if (event && event->type == GDK_2BUTTON_PRESS) + return; + + if (column == FIELD_COL_SELECT) { + struct _ImpCSVAttrib *attr; + attr = gtk_clist_get_row_data( clist, row ); + if (attr) { + attr->enabled ^= TRUE; + if (attr->enabled) + gtk_clist_set_pixmap(clist, row, FIELD_COL_SELECT, markxpm, markxpmmask); + else + gtk_clist_set_text(clist, row, FIELD_COL_SELECT, ""); + } + } +} + +static void imp_csv_field_list_up( GtkWidget *button, gpointer data ) { + GtkCList *clist = GTK_CLIST(impcsv_dlg.clist_field); + gchar *text; + gint row; + struct _ImpCSVAttrib *src_attr; + struct _ImpCSVAttrib *dest_attr; + gchar *src_text; + gchar *dest_text; + + if (!clist->selection) return; + + row = GPOINTER_TO_INT( clist->selection->data ); + if ( row > 0 && row < clist->rows ) { + gtk_clist_freeze( clist ); + + src_attr = gtk_clist_get_row_data( clist, row ); + dest_attr = gtk_clist_get_row_data( clist, row - 1 ); + + gtk_clist_row_move( clist, row, row - 1 ); + + gtk_clist_get_text( clist, row, FIELD_COL_FIELD, &text ); + src_text = g_strdup( text ); + gtk_clist_get_text( clist, row - 1, FIELD_COL_FIELD, &text ); + dest_text = g_strdup( text ); + gtk_clist_set_text( clist, row - 1, FIELD_COL_FIELD, src_text ); + gtk_clist_set_text( clist, row, FIELD_COL_FIELD, dest_text ); + g_free( dest_text ); + g_free( src_text ); + + if ( src_attr ) + src_attr->col = row - 1; + if ( dest_attr ) + dest_attr->col = row; + + gtk_clist_thaw( clist ); + + if (gtk_clist_row_is_visible(clist, row - 1) + != GTK_VISIBILITY_FULL) + gtk_clist_moveto(clist, row - 1, 0, 0.5, 0); + } +} + +static void imp_csv_field_list_down( GtkWidget *button, gpointer data ) { + GtkCList *clist = GTK_CLIST(impcsv_dlg.clist_field); + gchar *text; + gint row; + struct _ImpCSVAttrib *src_attr; + struct _ImpCSVAttrib *dest_attr; + gchar *src_text; + gchar *dest_text; + + if (!clist->selection) return; + + row = GPOINTER_TO_INT( clist->selection->data ); + if ( row >= 0 && row < clist->rows - 1 ) { + gtk_clist_freeze( clist ); + + src_attr = gtk_clist_get_row_data( clist, row ); + dest_attr = gtk_clist_get_row_data( clist, row + 1 ); + + gtk_clist_row_move( clist, row, row + 1 ); + + gtk_clist_get_text( clist, row, FIELD_COL_FIELD, &text ); + src_text = g_strdup( text ); + gtk_clist_get_text( clist, row + 1, FIELD_COL_FIELD, &text ); + dest_text = g_strdup( text ); + gtk_clist_set_text( clist, row + 1, FIELD_COL_FIELD, src_text ); + gtk_clist_set_text( clist, row, FIELD_COL_FIELD, dest_text ); + g_free( dest_text ); + g_free( src_text ); + + if ( src_attr ) + src_attr->col = row + 1; + if ( dest_attr ) + dest_attr->col = row; + + gtk_clist_thaw( clist ); + + if (gtk_clist_row_is_visible(clist, row + 1) + != GTK_VISIBILITY_FULL) + gtk_clist_moveto(clist, row + 1, 0, 0.5, 0); + } +} + +static gint imp_csv_import_data( gchar *csvFile, AddressCache *cache ) { + FILE *fp; + gchar buf[BUFFSIZE]; + gint i; + gchar **strv; + CharSet enc; + guint fields_len; + gchar *firstName = NULL; + gchar *lastName = NULL; + gchar *fullName = NULL; + gchar *nickName = NULL; + gchar *address = NULL; + gchar *remarks = NULL; + gchar *alias = NULL; + ItemPerson *person; + ItemEMail *email; + gint count = 0; + + g_return_val_if_fail( csvFile != NULL, MGU_BAD_ARGS ); + + addrcache_clear( cache ); + cache->dataRead = FALSE; + + enc = conv_check_file_encoding(csvFile); + + if ((fp = g_fopen(csvFile, "rb")) == NULL) { + return MGU_OPEN_FILE; + } + + fields_len = sizeof(imp_csv_attrib) / sizeof(imp_csv_attrib[0]); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + gchar *str; + guint col, cols = 0; + + strretchomp(buf); + if (enc == C_UTF_8) + str = g_strdup(buf); + else + str = conv_localetodisp(buf, NULL); + strv = strsplit_csv(str, impcsv_dlg.delimiter, 0); + while (strv[cols]) + ++cols; + + for (i = 0; i < fields_len; i++) { + if (!imp_csv_attrib[i].enabled) + continue; + col = imp_csv_attrib[i].col; + if (col >= cols || !*strv[col]) + continue; + imp_csv_attrib[i].value = strv[col]; + } + + firstName = imp_csv_attrib[ATTR_FIRST_NAME].value; + lastName = imp_csv_attrib[ATTR_LAST_NAME].value; + fullName = imp_csv_attrib[ATTR_DISPLAY_NAME].value; + nickName = imp_csv_attrib[ATTR_NICK_NAME].value; + address = imp_csv_attrib[ATTR_EMAIL_ADDRESS].value; + remarks = imp_csv_attrib[ATTR_REMARKS].value; + alias = imp_csv_attrib[ATTR_ALIAS].value; + + if (!fullName && !firstName && !lastName && address) + fullName = address; + + person = addritem_create_item_person(); + addritem_person_set_common_name( person, fullName ); + addritem_person_set_first_name( person, firstName ); + addritem_person_set_last_name( person, lastName ); + addritem_person_set_nick_name( person, nickName ); + addrcache_id_person( cache, person ); + addrcache_add_person( cache, person ); + + if (address) { + email = addritem_create_item_email(); + addritem_email_set_address( email, address ); + addritem_email_set_remarks( email, remarks ); + addritem_email_set_alias( email, alias ); + addrcache_id_email( cache, email ); + addrcache_person_add_email( cache, person, email ); + } + + for (i = 0; i < fields_len; i++) + imp_csv_attrib[i].value = NULL; + g_strfreev(strv); + g_free(str); + + count++; + } + + fclose(fp); + + cache->modified = FALSE; + cache->dataRead = TRUE; + importCount = count; + + return MGU_SUCCESS; +} + +/* +* Move off fields page. +* return: TRUE if OK to move off page. +*/ +static gboolean imp_csv_field_move() { + gboolean retVal = FALSE; + gchar *newFile; + AddressBookFile *abf = NULL; + + if( _importedBook_ ) { + addrbook_free_book( _importedBook_ ); + } + + abf = addrbook_create_book(); + addrbook_set_path( abf, _imp_addressIndex_->filePath ); + addrbook_set_name( abf, impcsv_dlg.nameBook ); + newFile = imp_csv_guess_file( abf ); + addrbook_set_file( abf, newFile ); + g_free( newFile ); + + /* Import data into file */ + if( imp_csv_import_data( impcsv_dlg.fileName, abf->addressCache ) == MGU_SUCCESS ) { + addrbook_save_data( abf ); + abf->dirtyFlag = TRUE; + _importedBook_ = abf; + retVal = TRUE; + } + else { + addrbook_free_book( abf ); + } + + return retVal; +} + +/* +* Move off fields page. +* return: TRUE if OK to move off page. +*/ +static gboolean imp_csv_file_move() { + gboolean retVal = FALSE; + gchar *sName; + gchar *sFile; + gchar *sMsg = NULL; + gboolean errFlag = FALSE; + + sFile = gtk_editable_get_chars( GTK_EDITABLE(impcsv_dlg.file_entry), 0, -1 ); + g_strchug( sFile ); g_strchomp( sFile ); + + sName = gtk_editable_get_chars( GTK_EDITABLE(impcsv_dlg.name_entry), 0, -1 ); + g_strchug( sName ); g_strchomp( sName ); + + g_free( impcsv_dlg.nameBook ); + g_free( impcsv_dlg.fileName ); + impcsv_dlg.nameBook = sName; + impcsv_dlg.fileName = sFile; + + gtk_entry_set_text( GTK_ENTRY(impcsv_dlg.file_entry), sFile ); + gtk_entry_set_text( GTK_ENTRY(impcsv_dlg.name_entry), sName ); + + if (gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(impcsv_dlg.comma_radiobtn))) { + impcsv_dlg.delimiter = ','; + } else { + impcsv_dlg.delimiter = '\t'; + } + + if( *sFile == '\0'|| strlen( sFile ) < 1 ) { + sMsg = _( "Please select a file." ); + gtk_widget_grab_focus(impcsv_dlg.file_entry); + errFlag = TRUE; + } + + if( *sName == '\0'|| strlen( sName ) < 1 ) { + if( ! errFlag ) sMsg = _( "Address book name must be supplied." ); + gtk_widget_grab_focus(impcsv_dlg.name_entry); + errFlag = TRUE; + } + + if( ! errFlag ) { + gchar *sFSFile; + sFSFile = conv_filename_from_utf8( sFile ); + if ( ! imp_csv_load_fields( sFSFile ) ) { + sMsg = _( "Error reading CSV fields." ); + } else { + retVal = TRUE; + } + g_free( sFSFile ); + } + imp_csv_status_show( sMsg ); + + return retVal; +} + +/* + * Display finish page. + */ +static void imp_csv_finish_show() { + gchar *sMsg; + gchar *name; + + name = gtk_editable_get_chars( GTK_EDITABLE(impcsv_dlg.name_entry), 0, -1 ); + gtk_label_set_text( GTK_LABEL(impcsv_dlg.labelBook), name ); + g_free( name ); + gtk_label_set_text( GTK_LABEL(impcsv_dlg.labelFile), impcsv_dlg.fileName ); + gtk_label_set_text( GTK_LABEL(impcsv_dlg.labelRecords), itos( importCount ) ); + gtk_widget_set_sensitive( impcsv_dlg.btnPrev, FALSE ); + gtk_widget_set_sensitive( impcsv_dlg.btnNext, FALSE ); + if( result == MGU_SUCCESS ) { + sMsg = _( "CSV file imported successfully." ); + } + else { + sMsg = mgu_error2string( result ); + } + imp_csv_status_show( sMsg ); + gtk_button_set_label(GTK_BUTTON(impcsv_dlg.btnCancel), GTK_STOCK_CLOSE); + gtk_widget_grab_focus(impcsv_dlg.btnCancel); +} + +static void imp_csv_prev( GtkWidget *widget ) { + gint pageNum; + + pageNum = gtk_notebook_get_current_page( GTK_NOTEBOOK(impcsv_dlg.notebook) ); + if( pageNum == PAGE_ATTRIBUTES ) { + /* Goto file page stuff */ + gtk_notebook_set_current_page( + GTK_NOTEBOOK(impcsv_dlg.notebook), PAGE_FILE_INFO ); + gtk_widget_set_sensitive( impcsv_dlg.btnPrev, FALSE ); + } + imp_csv_message(); +} + +static void imp_csv_next( GtkWidget *widget ) { + gint pageNum; + + pageNum = gtk_notebook_get_current_page( GTK_NOTEBOOK(impcsv_dlg.notebook) ); + if( pageNum == PAGE_FILE_INFO ) { + /* Goto attributes stuff */ + if( imp_csv_file_move() ) { + gtk_notebook_set_current_page( + GTK_NOTEBOOK(impcsv_dlg.notebook), PAGE_ATTRIBUTES ); + imp_csv_message(); + gtk_widget_set_sensitive( impcsv_dlg.btnPrev, TRUE ); + } + else { + gtk_widget_set_sensitive( impcsv_dlg.btnPrev, FALSE ); + } + } + else if( pageNum == PAGE_ATTRIBUTES ) { + /* Goto finish stuff */ + if( imp_csv_field_move() ) { + gtk_notebook_set_current_page( + GTK_NOTEBOOK(impcsv_dlg.notebook), PAGE_FINISH ); + imp_csv_finish_show(); + } + } +} + +static void imp_csv_cancel( GtkWidget *widget, gpointer data ) { + gint pageNum; + + pageNum = gtk_notebook_get_current_page( GTK_NOTEBOOK(impcsv_dlg.notebook) ); + if( pageNum != PAGE_FINISH ) { + impcsv_dlg.cancelled = TRUE; + } + gtk_main_quit(); +} + +static void imp_csv_file_select( void ) { + gchar *sSelFile; + + sSelFile = filesel_select_file( _("Select CSV File"), NULL, + GTK_FILE_CHOOSER_ACTION_OPEN ); + if (sSelFile) { + gchar *sUTF8File; + sUTF8File = conv_filename_to_utf8( sSelFile ); + gtk_entry_set_text( GTK_ENTRY(impcsv_dlg.file_entry), sUTF8File ); + g_free( sUTF8File ); + g_free( sSelFile ); + } +} + +static gint imp_csv_delete_event( GtkWidget *widget, GdkEventAny *event, gpointer data ) { + imp_csv_cancel( widget, data ); + return TRUE; +} + +static gboolean imp_csv_key_pressed( GtkWidget *widget, GdkEventKey *event, gpointer data ) { + if (event && event->keyval == GDK_Escape) { + imp_csv_cancel( widget, data ); + } + return FALSE; +} + +static void imp_csv_page_file( gint pageNum, gchar *pageLbl ) { + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *label; + GtkWidget *file_entry; + GtkWidget *name_entry; + GtkWidget *file_btn; + GtkWidget *hbox; + GtkWidget *comma_radiobtn; + GtkWidget *tab_radiobtn; + gint top; + + vbox = gtk_vbox_new(FALSE, 4); + gtk_container_add( GTK_CONTAINER( impcsv_dlg.notebook ), vbox ); + gtk_container_set_border_width( GTK_CONTAINER (vbox), 4 ); + + label = gtk_label_new( pageLbl ); + gtk_widget_show( label ); + gtk_notebook_set_tab_label( + GTK_NOTEBOOK( impcsv_dlg.notebook ), + gtk_notebook_get_nth_page( + GTK_NOTEBOOK( impcsv_dlg.notebook ), pageNum ), + label ); + + table = gtk_table_new(2, 3, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 6 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + + /* First row */ + top = 0; + label = gtk_label_new(_("Address Book")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + name_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), name_entry, 1, 2, top, (top + 1), + GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* Second row */ + top = 1; + label = gtk_label_new(_("File Name")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + file_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), file_entry, 1, 2, top, (top + 1), + GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + file_btn = gtk_button_new_with_label(_(" ... ")); + gtk_table_attach(GTK_TABLE(table), file_btn, 2, 3, top, (top + 1), + GTK_FILL, 0, 3, 0); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER (hbox), 4 ); + + comma_radiobtn = gtk_radio_button_new_with_label + (NULL, _("Comma-separated")); + gtk_box_pack_start(GTK_BOX(hbox), comma_radiobtn, FALSE, FALSE, 0); + + tab_radiobtn = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON(comma_radiobtn), _("Tab-separated")); + gtk_box_pack_start(GTK_BOX(hbox), tab_radiobtn, FALSE, FALSE, 0); + + gtk_widget_show_all(vbox); + + /* Button handler */ + g_signal_connect(G_OBJECT(file_btn), "clicked", + G_CALLBACK(imp_csv_file_select), NULL); + + impcsv_dlg.file_entry = file_entry; + impcsv_dlg.name_entry = name_entry; + impcsv_dlg.comma_radiobtn = comma_radiobtn; + impcsv_dlg.tab_radiobtn = tab_radiobtn; +} + +static void imp_csv_page_fields( gint pageNum, gchar *pageLbl ) { + GtkWidget *vbox; + GtkWidget *hbox; + + GtkWidget *label; + GtkWidget *clist_swin; + GtkWidget *clist_field; + + GtkWidget *btn_vbox; + GtkWidget *btn_vbox1; + GtkWidget *up_btn; + GtkWidget *down_btn; + + gchar *titles[ FIELDS_N_COLS ]; + gint i; + + titles[ FIELD_COL_SELECT ] = _("S"); + titles[ FIELD_COL_FIELD ] = _("CSV Field"); + titles[ FIELD_COL_ATTRIB ] = _("Address Book Field"); + + vbox = gtk_vbox_new(FALSE, 4); + gtk_container_add( GTK_CONTAINER( impcsv_dlg.notebook ), vbox ); + gtk_container_set_border_width( GTK_CONTAINER (vbox), 4 ); + + label = gtk_label_new( pageLbl ); + gtk_widget_show( label ); + gtk_notebook_set_tab_label( + GTK_NOTEBOOK( impcsv_dlg.notebook ), + gtk_notebook_get_nth_page(GTK_NOTEBOOK( impcsv_dlg.notebook ), pageNum ), + label ); + + hbox = gtk_hbox_new( FALSE, 0 ); + gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, FALSE, 4 ); + + label = gtk_label_new + ( _("Reorder address book fields with the Up and Down button.") ); + gtk_box_pack_start( GTK_BOX( hbox ), label, FALSE, FALSE, 0 ); + gtk_label_set_justify( GTK_LABEL( label ), GTK_JUSTIFY_LEFT ); + gtk_label_set_line_wrap( GTK_LABEL( label ), TRUE ); + + hbox = gtk_hbox_new( FALSE, 4 ); + gtk_box_pack_start( GTK_BOX( vbox ), hbox, TRUE, TRUE, 0 ); + + clist_swin = gtk_scrolled_window_new( NULL, NULL ); + gtk_box_pack_start( GTK_BOX(hbox), clist_swin, TRUE, TRUE, 0 ); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + + clist_field = gtk_clist_new_with_titles( FIELDS_N_COLS, titles ); + gtk_container_add( GTK_CONTAINER(clist_swin), clist_field ); + gtk_clist_set_selection_mode( GTK_CLIST(clist_field), GTK_SELECTION_BROWSE ); + gtk_clist_set_column_width( + GTK_CLIST(clist_field), FIELD_COL_SELECT, FIELDS_COL_WIDTH_SELECT ); + gtk_clist_set_column_width( + GTK_CLIST(clist_field), FIELD_COL_FIELD, FIELDS_COL_WIDTH_FIELD ); + gtk_clist_set_column_width( + GTK_CLIST(clist_field), FIELD_COL_ATTRIB, FIELDS_COL_WIDTH_ATTRIB ); + gtkut_clist_set_redraw( GTK_CLIST(clist_field) ); + + for( i = 0; i < FIELDS_N_COLS; i++ ) + GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist_field)->column[i].button, GTK_CAN_FOCUS); + + btn_vbox = gtk_vbox_new( FALSE, 0 ); + gtk_box_pack_start( GTK_BOX( hbox ), btn_vbox, FALSE, FALSE, 0 ); + gtk_container_set_border_width( GTK_CONTAINER (btn_vbox), 4 ); + + btn_vbox1 = gtk_vbox_new( FALSE, 8 ); + gtk_box_pack_start( GTK_BOX( btn_vbox ), btn_vbox1, TRUE, FALSE, 0 ); + + up_btn = gtk_button_new_with_label( _("Up") ); + gtk_box_pack_start( GTK_BOX( btn_vbox1 ), up_btn, FALSE, FALSE, 0 ); + down_btn = gtk_button_new_with_label( _("Down") ); + gtk_box_pack_start( GTK_BOX( btn_vbox1 ), down_btn, FALSE, FALSE, 0 ); + + gtk_widget_show_all(vbox); + + g_signal_connect( G_OBJECT(clist_field), "select_row", + G_CALLBACK(imp_csv_field_list_selected), NULL ); + g_signal_connect( G_OBJECT(up_btn), "clicked", + G_CALLBACK(imp_csv_field_list_up), NULL ); + g_signal_connect( G_OBJECT(down_btn), "clicked", + G_CALLBACK(imp_csv_field_list_down), NULL ); + + impcsv_dlg.clist_field = clist_field; +} + +static void imp_csv_page_finish( gint pageNum, gchar *pageLbl ) { + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *label; + GtkWidget *labelBook; + GtkWidget *labelFile; + GtkWidget *labelRecs; + gint top; + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add( GTK_CONTAINER( impcsv_dlg.notebook ), vbox ); + gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH ); + + label = gtk_label_new( pageLbl ); + gtk_widget_show( label ); + gtk_notebook_set_tab_label( + GTK_NOTEBOOK( impcsv_dlg.notebook ), + gtk_notebook_get_nth_page( GTK_NOTEBOOK( impcsv_dlg.notebook ), pageNum ), label ); + + table = gtk_table_new(3, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 8 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8 ); + + /* First row */ + top = 0; + label = gtk_label_new(_("Address Book :")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + labelBook = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), labelBook, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(labelBook), 0, 0.5); + + /* Second row */ + top++; + label = gtk_label_new(_("File Name :")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + labelFile = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), labelFile, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(labelFile), 0, 0.5); + gtk_label_set_line_wrap(GTK_LABEL(labelFile), TRUE); + + /* Third row */ + top++; + label = gtk_label_new(_("Records :")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + labelRecs = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), labelRecs, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(labelRecs), 0, 0.5); + + impcsv_dlg.labelBook = labelBook; + impcsv_dlg.labelFile = labelFile; + impcsv_dlg.labelRecords = labelRecs; +} + +static void imp_csv_dialog_create() { + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *vnbox; + GtkWidget *notebook; + GtkWidget *hbbox; + GtkWidget *btnPrev; + GtkWidget *btnNext; + GtkWidget *btnCancel; + GtkWidget *hsbox; + GtkWidget *statusbar; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request(window, IMPORTCSV_WIDTH, IMPORTCSV_HEIGHT ); + gtk_window_set_title( GTK_WINDOW(window), _("Import CSV file into Address Book") ); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(imp_csv_delete_event), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(imp_csv_key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + + vbox = gtk_vbox_new(FALSE, 4); + gtk_widget_show(vbox); + gtk_container_add(GTK_CONTAINER(window), vbox); + + vnbox = gtk_vbox_new(FALSE, 4); + gtk_container_set_border_width(GTK_CONTAINER(vnbox), 4); + gtk_widget_show(vnbox); + gtk_box_pack_start(GTK_BOX(vbox), vnbox, TRUE, TRUE, 0); + + /* Notebook */ + notebook = gtk_notebook_new(); + gtk_notebook_set_show_tabs( GTK_NOTEBOOK(notebook), FALSE ); + gtk_widget_show(notebook); + gtk_box_pack_start(GTK_BOX(vnbox), notebook, TRUE, TRUE, 0); + gtk_container_set_border_width(GTK_CONTAINER(notebook), 6); + + /* Status line */ + hsbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, 0); + statusbar = gtk_statusbar_new(); + gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, 0); + + /* Button panel */ + if (prefs_common.comply_gnome_hig) { + gtkut_stock_button_set_create(&hbbox, &btnNext, _("Next"), + &btnPrev, _("Prev"), + &btnCancel, GTK_STOCK_CANCEL); + } else { + gtkut_stock_button_set_create(&hbbox, &btnPrev, _("Prev"), + &btnNext, _("Next"), + &btnCancel, GTK_STOCK_CANCEL); + } + gtk_box_pack_end(GTK_BOX(vnbox), hbbox, FALSE, FALSE, 0); + gtk_widget_grab_default(btnNext); + + /* Button handlers */ + g_signal_connect(G_OBJECT(btnPrev), "clicked", + G_CALLBACK(imp_csv_prev), NULL); + g_signal_connect(G_OBJECT(btnNext), "clicked", + G_CALLBACK(imp_csv_next), NULL); + g_signal_connect(G_OBJECT(btnCancel), "clicked", + G_CALLBACK(imp_csv_cancel), NULL); + + gtk_widget_show_all(vbox); + + impcsv_dlg.window = window; + impcsv_dlg.notebook = notebook; + impcsv_dlg.btnPrev = btnPrev; + impcsv_dlg.btnNext = btnNext; + impcsv_dlg.btnCancel = btnCancel; + impcsv_dlg.statusbar = statusbar; + impcsv_dlg.status_cid = gtk_statusbar_get_context_id( + GTK_STATUSBAR(statusbar), "Import CSV Dialog" ); + +} + +static void imp_csv_create() { + imp_csv_dialog_create(); + imp_csv_page_file( PAGE_FILE_INFO, _( "File Info" ) ); + imp_csv_page_fields( PAGE_ATTRIBUTES, _( "Fields" ) ); + imp_csv_page_finish( PAGE_FINISH, _( "Finish" ) ); + gtk_widget_show_all( impcsv_dlg.window ); +} + +AddressBookFile *addressbook_imp_csv( AddressIndex *addrIndex ) { + _importedBook_ = NULL; + _imp_addressIndex_ = addrIndex; + + if( ! impcsv_dlg.window ) + imp_csv_create(); + impcsv_dlg.cancelled = FALSE; + manage_window_set_transient(GTK_WINDOW(impcsv_dlg.window)); + gtk_widget_grab_default(impcsv_dlg.btnNext); + + gtk_entry_set_text( GTK_ENTRY(impcsv_dlg.name_entry), IMPORTCSV_GUESS_NAME ); + gtk_entry_set_text( GTK_ENTRY(impcsv_dlg.file_entry), "" ); + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(impcsv_dlg.comma_radiobtn), TRUE ); + gtk_clist_clear( GTK_CLIST(impcsv_dlg.clist_field) ); + gtk_notebook_set_current_page( GTK_NOTEBOOK(impcsv_dlg.notebook), PAGE_FILE_INFO ); + gtk_widget_set_sensitive( impcsv_dlg.btnPrev, FALSE ); + gtk_widget_set_sensitive( impcsv_dlg.btnNext, TRUE ); + gtk_button_set_label( GTK_BUTTON(impcsv_dlg.btnCancel), GTK_STOCK_CANCEL ); + stock_pixmap_gdk( impcsv_dlg.window, STOCK_PIXMAP_MARK, + &markxpm, &markxpmmask ); + imp_csv_message(); + gtk_widget_grab_focus(impcsv_dlg.file_entry); + + impcsv_dlg.rowCount = 0; + g_free( impcsv_dlg.nameBook ); + g_free( impcsv_dlg.fileName ); + impcsv_dlg.nameBook = NULL; + impcsv_dlg.fileName = NULL; + impcsv_dlg.delimiter = ','; + importCount = 0; + + gtk_widget_show(impcsv_dlg.window); + + gtk_main(); + gtk_widget_hide(impcsv_dlg.window); + _imp_addressIndex_ = NULL; + + g_free( impcsv_dlg.nameBook ); + g_free( impcsv_dlg.fileName ); + impcsv_dlg.nameBook = NULL; + impcsv_dlg.fileName = NULL; + + if( impcsv_dlg.cancelled == TRUE ) return NULL; + return _importedBook_; +} + +/* +* End of Source. +*/ + diff --git a/src/importcsv.h b/src/importcsv.h new file mode 100644 index 0000000..01047a5 --- /dev/null +++ b/src/importcsv.h @@ -0,0 +1,34 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2007 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Import CSV text data. + */ + +#ifndef __IMPORT_CSV_H__ +#define __IMPORT_CSV_H__ + +/* Function prototypes */ +AddressBookFile *addressbook_imp_csv( AddressIndex *addrIndex ); + +#endif /* __IMPORT_CSV_H__ */ + +/* +* End of Source. +*/ diff --git a/src/importldif.c b/src/importldif.c new file mode 100644 index 0000000..d8f52fd --- /dev/null +++ b/src/importldif.c @@ -0,0 +1,883 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2017 Hiroyuki Yamamoto + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "addrbook.h" +#include "addressbook.h" +#include "addressitem.h" +#include "filesel.h" +#include "gtkutils.h" +#include "stock_pixmap.h" +#include "prefs_common.h" +#include "manage_window.h" +#include "mgutils.h" +#include "ldif.h" +#include "codeconv.h" +#include "utils.h" + +#define IMPORTLDIF_GUESS_NAME "LDIF Import" + +#define PAGE_FILE_INFO 0 +#define PAGE_ATTRIBUTES 1 +#define PAGE_FINISH 2 + +#define IMPORTLDIF_WIDTH 480 +#define IMPORTLDIF_HEIGHT 300 + +#define FIELDS_N_COLS 3 +#define FIELDS_COL_WIDTH_SELECT 10 +#define FIELDS_COL_WIDTH_FIELD 140 +#define FIELDS_COL_WIDTH_ATTRIB 140 + +typedef enum { + FIELD_COL_SELECT = 0, + FIELD_COL_FIELD = 1, + FIELD_COL_ATTRIB = 2 +} ImpLdif_FieldColPos; + +static struct _ImpLdif_Dlg { + GtkWidget *window; + GtkWidget *notebook; + GtkWidget *file_entry; + GtkWidget *name_entry; + GtkWidget *clist_field; + GtkWidget *name_ldif; + GtkWidget *name_attrib; + GtkWidget *check_select; + GtkWidget *labelBook; + GtkWidget *labelFile; + GtkWidget *labelRecords; + GtkWidget *btnPrev; + GtkWidget *btnNext; + GtkWidget *btnCancel; + GtkWidget *statusbar; + gint status_cid; + gint rowIndSelect; + gint rowCount; + gchar *nameBook; + gchar *fileName; + gboolean cancelled; +} impldif_dlg; + +static AddressBookFile *_importedBook_; +static AddressIndex *_imp_addressIndex_; +static LdifFile *_ldifFile_ = NULL; + +static GdkPixmap *markxpm; +static GdkBitmap *markxpmmask; + +static void imp_ldif_status_show( gchar *msg ) { + if( impldif_dlg.statusbar != NULL ) { + gtk_statusbar_pop( GTK_STATUSBAR(impldif_dlg.statusbar), impldif_dlg.status_cid ); + if( msg ) { + gtk_statusbar_push( GTK_STATUSBAR(impldif_dlg.statusbar), impldif_dlg.status_cid, msg ); + } + } +} + +static void imp_ldif_message( void ) { + gchar *sMsg = NULL; + gint pageNum; + + pageNum = gtk_notebook_get_current_page( GTK_NOTEBOOK(impldif_dlg.notebook) ); + if( pageNum == PAGE_FILE_INFO ) { + sMsg = _( "Please specify address book name and file to import." ); + } + else if( pageNum == PAGE_ATTRIBUTES ) { + sMsg = _( "Select and rename LDIF field names to import." ); + } + else if( pageNum == PAGE_FINISH ) { + sMsg = _( "File imported." ); + } + imp_ldif_status_show( sMsg ); +} + +static gchar *imp_ldif_guess_file( AddressBookFile *abf ) { + gchar *newFile = NULL; + GList *fileList = NULL; + gint fileNum = 1; + fileList = addrbook_get_bookfile_list( abf ); + if( fileList ) { + fileNum = 1 + abf->maxValue; + } + newFile = addrbook_gen_new_file_name( fileNum ); + g_list_free( fileList ); + fileList = NULL; + return newFile; +} + +static void imp_ldif_update_row( GtkCList *clist ) { + Ldif_FieldRec *rec; + gchar *text[ FIELDS_N_COLS ]; + gint row; + + if( impldif_dlg.rowIndSelect < 0 ) return; + row = impldif_dlg.rowIndSelect; + + rec = gtk_clist_get_row_data( clist, row ); + text[ FIELD_COL_SELECT ] = ""; + text[ FIELD_COL_FIELD ] = rec->tagName; + text[ FIELD_COL_ATTRIB ] = rec->userName; + + gtk_clist_freeze( clist ); + gtk_clist_remove( clist, row ); + if( row == impldif_dlg.rowCount - 1 ) { + gtk_clist_append( clist, text ); + } + else { + gtk_clist_insert( clist, row, text ); + } + if( rec->selected ) + gtk_clist_set_pixmap( clist, row, FIELD_COL_SELECT, markxpm, markxpmmask ); + + gtk_clist_set_row_data( clist, row, rec ); + gtk_clist_thaw( clist ); +} + +static void imp_ldif_load_fields( LdifFile *ldf ) { + GtkCList *clist = GTK_CLIST(impldif_dlg.clist_field); + GList *node, *list; + gchar *text[ FIELDS_N_COLS ]; + + impldif_dlg.rowIndSelect = -1; + impldif_dlg.rowCount = 0; + if( ! ldf->accessFlag ) return; + gtk_clist_clear( clist ); + list = ldif_get_fieldlist( ldf ); + node = list; + while( node ) { + Ldif_FieldRec *rec = node->data; + gint row; + + if( ! rec->reserved ) { + text[ FIELD_COL_SELECT ] = ""; + text[ FIELD_COL_FIELD ] = rec->tagName; + text[ FIELD_COL_ATTRIB ] = rec->userName; + row = gtk_clist_append( clist, text ); + gtk_clist_set_row_data( clist, row, rec ); + if( rec->selected ) + gtk_clist_set_pixmap( clist, row, FIELD_COL_SELECT, markxpm, markxpmmask ); + impldif_dlg.rowCount++; + } + node = g_list_next( node ); + } + g_list_free( list ); + list = NULL; + ldif_set_accessed( ldf, FALSE ); +} + +static void imp_ldif_field_list_selected( GtkCList *clist, gint row, gint column, GdkEvent *event, gpointer data ) { + Ldif_FieldRec *rec = gtk_clist_get_row_data( clist, row ); + + impldif_dlg.rowIndSelect = row; + gtk_entry_set_text( GTK_ENTRY(impldif_dlg.name_attrib), "" ); + if( rec ) { + gtk_label_set_text( GTK_LABEL(impldif_dlg.name_ldif), rec->tagName ); + if( rec->userName ) + gtk_entry_set_text( GTK_ENTRY(impldif_dlg.name_attrib), rec->userName ); + gtk_toggle_button_set_active( + GTK_TOGGLE_BUTTON( impldif_dlg.check_select), + rec->selected ); + } + gtk_widget_grab_focus(impldif_dlg.name_attrib); +} + +static gboolean imp_ldif_field_list_toggle( GtkCList *clist, GdkEventButton *event, gpointer data ) { + if( ! event ) return FALSE; + if( impldif_dlg.rowIndSelect < 0 ) return FALSE; + if( event->button == 1 ) { + if( event->type == GDK_2BUTTON_PRESS ) { + Ldif_FieldRec *rec = gtk_clist_get_row_data( clist, impldif_dlg.rowIndSelect ); + if( rec ) { + rec->selected = ! rec->selected; + imp_ldif_update_row( clist ); + } + } + } + return FALSE; +} + +static void imp_ldif_modify_pressed( GtkWidget *widget, gpointer data ) { + GtkCList *clist = GTK_CLIST(impldif_dlg.clist_field); + Ldif_FieldRec *rec; + gint row; + + if( impldif_dlg.rowIndSelect < 0 ) return; + row = impldif_dlg.rowIndSelect; + rec = gtk_clist_get_row_data( clist, impldif_dlg.rowIndSelect ); + + g_free( rec->userName ); + rec->userName = gtk_editable_get_chars( GTK_EDITABLE(impldif_dlg.name_attrib), 0, -1 ); + rec->selected = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( impldif_dlg.check_select) ); + imp_ldif_update_row( clist ); + gtk_clist_select_row( clist, row, 0 ); + gtk_label_set_text( GTK_LABEL(impldif_dlg.name_ldif), "" ); + gtk_entry_set_text( GTK_ENTRY(impldif_dlg.name_attrib), "" ); + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( impldif_dlg.check_select), FALSE ); +} + +/* +* Move off fields page. +* return: TRUE if OK to move off page. +*/ +static gboolean imp_ldif_field_move() { + gboolean retVal = FALSE; + gchar *newFile; + AddressBookFile *abf = NULL; + + if( _importedBook_ ) { + addrbook_free_book( _importedBook_ ); + } + + abf = addrbook_create_book(); + addrbook_set_path( abf, _imp_addressIndex_->filePath ); + addrbook_set_name( abf, impldif_dlg.nameBook ); + newFile = imp_ldif_guess_file( abf ); + addrbook_set_file( abf, newFile ); + g_free( newFile ); + + /* Import data into file */ + if( ldif_import_data( _ldifFile_, abf->addressCache ) == MGU_SUCCESS ) { + addrbook_save_data( abf ); + abf->dirtyFlag = TRUE; + _importedBook_ = abf; + retVal = TRUE; + } + else { + addrbook_free_book( abf ); + } + + return retVal; +} + +/* +* Move off fields page. +* return: TRUE if OK to move off page. +*/ +static gboolean imp_ldif_file_move() { + gboolean retVal = FALSE; + gchar *sName; + gchar *sFile; + gchar *sMsg = NULL; + gboolean errFlag = FALSE; + + sFile = gtk_editable_get_chars( GTK_EDITABLE(impldif_dlg.file_entry), 0, -1 ); + g_strchug( sFile ); g_strchomp( sFile ); + + sName = gtk_editable_get_chars( GTK_EDITABLE(impldif_dlg.name_entry), 0, -1 ); + g_strchug( sName ); g_strchomp( sName ); + + g_free( impldif_dlg.nameBook ); + g_free( impldif_dlg.fileName ); + impldif_dlg.nameBook = sName; + impldif_dlg.fileName = sFile; + + gtk_entry_set_text( GTK_ENTRY(impldif_dlg.file_entry), sFile ); + gtk_entry_set_text( GTK_ENTRY(impldif_dlg.name_entry), sName ); + + if( *sFile == '\0'|| strlen( sFile ) < 1 ) { + sMsg = _( "Please select a file." ); + gtk_widget_grab_focus(impldif_dlg.file_entry); + errFlag = TRUE; + } + + if( *sName == '\0'|| strlen( sName ) < 1 ) { + if( ! errFlag ) sMsg = _( "Address book name must be supplied." ); + gtk_widget_grab_focus(impldif_dlg.name_entry); + errFlag = TRUE; + } + + if( ! errFlag ) { + gchar *sFSFile; + /* Read attribute list */ + sFSFile = conv_filename_from_utf8( sFile ); + ldif_set_file( _ldifFile_, sFSFile ); + g_free( sFSFile ); + if( ldif_read_tags( _ldifFile_ ) == MGU_SUCCESS ) { + /* Load fields */ + /* ldif_print_file( _ldifFile_, stdout ); */ + imp_ldif_load_fields( _ldifFile_ ); + retVal = TRUE; + } + else { + sMsg = _( "Error reading LDIF fields." ); + } + } + imp_ldif_status_show( sMsg ); + + return retVal; +} + +/* + * Display finish page. + */ +static void imp_ldif_finish_show() { + gchar *sMsg; + gchar *name; + + name = gtk_editable_get_chars( GTK_EDITABLE(impldif_dlg.name_entry), 0, -1 ); + gtk_label_set_text( GTK_LABEL(impldif_dlg.labelBook), name ); + g_free( name ); + gtk_label_set_text( GTK_LABEL(impldif_dlg.labelFile), _ldifFile_->path ); + gtk_label_set_text( GTK_LABEL(impldif_dlg.labelRecords), itos( _ldifFile_->importCount ) ); + gtk_widget_set_sensitive( impldif_dlg.btnPrev, FALSE ); + gtk_widget_set_sensitive( impldif_dlg.btnNext, FALSE ); + if( _ldifFile_->retVal == MGU_SUCCESS ) { + sMsg = _( "LDIF file imported successfully." ); + } + else { + sMsg = mgu_error2string( _ldifFile_->retVal ); + } + imp_ldif_status_show( sMsg ); + gtk_button_set_label(GTK_BUTTON(impldif_dlg.btnCancel), GTK_STOCK_CLOSE); + gtk_widget_grab_focus(impldif_dlg.btnCancel); +} + +static void imp_ldif_prev( GtkWidget *widget ) { + gint pageNum; + + pageNum = gtk_notebook_get_current_page( GTK_NOTEBOOK(impldif_dlg.notebook) ); + if( pageNum == PAGE_ATTRIBUTES ) { + /* Goto file page stuff */ + gtk_notebook_set_current_page( + GTK_NOTEBOOK(impldif_dlg.notebook), PAGE_FILE_INFO ); + gtk_widget_set_sensitive( impldif_dlg.btnPrev, FALSE ); + } + imp_ldif_message(); +} + +static void imp_ldif_next( GtkWidget *widget ) { + gint pageNum; + + pageNum = gtk_notebook_get_current_page( GTK_NOTEBOOK(impldif_dlg.notebook) ); + if( pageNum == PAGE_FILE_INFO ) { + /* Goto attributes stuff */ + if( imp_ldif_file_move() ) { + gtk_notebook_set_current_page( + GTK_NOTEBOOK(impldif_dlg.notebook), PAGE_ATTRIBUTES ); + imp_ldif_message(); + gtk_widget_set_sensitive( impldif_dlg.btnPrev, TRUE ); + } + else { + gtk_widget_set_sensitive( impldif_dlg.btnPrev, FALSE ); + } + } + else if( pageNum == PAGE_ATTRIBUTES ) { + /* Goto finish stuff */ + if( imp_ldif_field_move() ) { + gtk_notebook_set_current_page( + GTK_NOTEBOOK(impldif_dlg.notebook), PAGE_FINISH ); + imp_ldif_finish_show(); + } + } +} + +static void imp_ldif_cancel( GtkWidget *widget, gpointer data ) { + gint pageNum; + + pageNum = gtk_notebook_get_current_page( GTK_NOTEBOOK(impldif_dlg.notebook) ); + if( pageNum != PAGE_FINISH ) { + impldif_dlg.cancelled = TRUE; + } + gtk_main_quit(); +} + +static void imp_ldif_file_select( void ) { + gchar *sSelFile; + + sSelFile = filesel_select_file( _("Select LDIF File"), NULL, + GTK_FILE_CHOOSER_ACTION_OPEN ); + if ( sSelFile ) { + gchar *sUTF8File; + sUTF8File = conv_filename_to_utf8( sSelFile ); + gtk_entry_set_text( GTK_ENTRY(impldif_dlg.file_entry), sUTF8File ); + g_free( sUTF8File ); + g_free( sSelFile ); + } +} + +static gint imp_ldif_delete_event( GtkWidget *widget, GdkEventAny *event, gpointer data ) { + imp_ldif_cancel( widget, data ); + return TRUE; +} + +static gboolean imp_ldif_key_pressed( GtkWidget *widget, GdkEventKey *event, gpointer data ) { + if (event && event->keyval == GDK_Escape) { + imp_ldif_cancel( widget, data ); + } + return FALSE; +} + +static void imp_ldif_page_file( gint pageNum, gchar *pageLbl ) { + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *label; + GtkWidget *file_entry; + GtkWidget *name_entry; + GtkWidget *file_btn; + gint top; + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add( GTK_CONTAINER( impldif_dlg.notebook ), vbox ); + gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH ); + + label = gtk_label_new( pageLbl ); + gtk_widget_show( label ); + gtk_notebook_set_tab_label( + GTK_NOTEBOOK( impldif_dlg.notebook ), + gtk_notebook_get_nth_page( + GTK_NOTEBOOK( impldif_dlg.notebook ), pageNum ), + label ); + + table = gtk_table_new(2, 3, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 8 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8 ); + + /* First row */ + top = 0; + label = gtk_label_new(_("Address Book")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + name_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), name_entry, 1, 2, top, (top + 1), + GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* Second row */ + top = 1; + label = gtk_label_new(_("File Name")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + file_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), file_entry, 1, 2, top, (top + 1), + GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + file_btn = gtk_button_new_with_label( _(" ... ")); + gtk_table_attach(GTK_TABLE(table), file_btn, 2, 3, top, (top + 1), + GTK_FILL, 0, 3, 0); + + gtk_widget_show_all(vbox); + + /* Button handler */ + g_signal_connect(G_OBJECT(file_btn), "clicked", + G_CALLBACK(imp_ldif_file_select), NULL); + + impldif_dlg.file_entry = file_entry; + impldif_dlg.name_entry = name_entry; +} + +static void imp_ldif_page_fields( gint pageNum, gchar *pageLbl ) { + GtkWidget *vbox; + GtkWidget *vboxt; + GtkWidget *vboxb; + GtkWidget *buttonMod; + + GtkWidget *table; + GtkWidget *label; + GtkWidget *clist_swin; + GtkWidget *clist_field; + GtkWidget *name_ldif; + GtkWidget *name_attrib; + GtkWidget *check_select; + gint top; + + gchar *titles[ FIELDS_N_COLS ]; + gint i; + + titles[ FIELD_COL_SELECT ] = _("S"); + titles[ FIELD_COL_FIELD ] = _("LDIF Field"); + titles[ FIELD_COL_ATTRIB ] = _("Attribute Name"); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add( GTK_CONTAINER( impldif_dlg.notebook ), vbox ); + gtk_container_set_border_width( GTK_CONTAINER (vbox), 4 ); + + label = gtk_label_new( pageLbl ); + gtk_widget_show( label ); + gtk_notebook_set_tab_label( + GTK_NOTEBOOK( impldif_dlg.notebook ), + gtk_notebook_get_nth_page(GTK_NOTEBOOK( impldif_dlg.notebook ), pageNum ), + label ); + + /* Upper area - Field list */ + vboxt = gtk_vbox_new( FALSE, 4 ); + gtk_container_add( GTK_CONTAINER( vbox ), vboxt ); + + clist_swin = gtk_scrolled_window_new( NULL, NULL ); + gtk_container_add( GTK_CONTAINER(vboxt), clist_swin ); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + + clist_field = gtk_clist_new_with_titles( FIELDS_N_COLS, titles ); + gtk_container_add( GTK_CONTAINER(clist_swin), clist_field ); + gtk_clist_set_selection_mode( GTK_CLIST(clist_field), GTK_SELECTION_BROWSE ); + gtk_clist_set_column_width( + GTK_CLIST(clist_field), FIELD_COL_SELECT, FIELDS_COL_WIDTH_SELECT ); + gtk_clist_set_column_width( + GTK_CLIST(clist_field), FIELD_COL_FIELD, FIELDS_COL_WIDTH_FIELD ); + gtk_clist_set_column_width( + GTK_CLIST(clist_field), FIELD_COL_ATTRIB, FIELDS_COL_WIDTH_ATTRIB ); + gtkut_clist_set_redraw( GTK_CLIST(clist_field) ); + + for( i = 0; i < FIELDS_N_COLS; i++ ) + GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist_field)->column[i].button, GTK_CAN_FOCUS); + + /* Lower area - Edit area */ + vboxb = gtk_vbox_new( FALSE, 4 ); + gtk_box_pack_end(GTK_BOX(vbox), vboxb, FALSE, FALSE, 2); + + /* Data entry area */ + table = gtk_table_new( 3, 3, FALSE); + gtk_box_pack_start(GTK_BOX(vboxb), table, FALSE, FALSE, 0); + gtk_table_set_row_spacings(GTK_TABLE(table), 4); + gtk_table_set_col_spacings(GTK_TABLE(table), 4); + + /* First row */ + top = 0; + label = gtk_label_new(_("LDIF Field")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + name_ldif = gtk_label_new( "" ); + gtk_misc_set_alignment(GTK_MISC(name_ldif), 0.01, 0.5); + gtk_table_attach(GTK_TABLE(table), name_ldif, 1, 3, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* Second row */ + ++top; + label = gtk_label_new(_("Attribute")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + name_attrib = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), name_attrib, 1, 3, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + /* Next row */ + ++top; + label = gtk_label_new(_("Select")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + check_select = gtk_check_button_new(); + gtk_table_attach(GTK_TABLE(table), check_select, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); + + buttonMod = gtk_button_new_with_label( _("Modify")); + gtk_table_attach(GTK_TABLE(table), buttonMod, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0); + + gtk_widget_show_all(vbox); + + /* Event handlers */ + g_signal_connect( G_OBJECT(clist_field), "select_row", + G_CALLBACK(imp_ldif_field_list_selected), NULL ); + g_signal_connect( G_OBJECT(clist_field), "button_press_event", + G_CALLBACK(imp_ldif_field_list_toggle), NULL ); + g_signal_connect( G_OBJECT(buttonMod), "clicked", + G_CALLBACK(imp_ldif_modify_pressed), NULL ); + + impldif_dlg.clist_field = clist_field; + impldif_dlg.name_ldif = name_ldif; + impldif_dlg.name_attrib = name_attrib; + impldif_dlg.check_select = check_select; +} + +static void imp_ldif_page_finish( gint pageNum, gchar *pageLbl ) { + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *label; + GtkWidget *labelBook; + GtkWidget *labelFile; + GtkWidget *labelRecs; + gint top; + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add( GTK_CONTAINER( impldif_dlg.notebook ), vbox ); + gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH ); + + label = gtk_label_new( pageLbl ); + gtk_widget_show( label ); + gtk_notebook_set_tab_label( + GTK_NOTEBOOK( impldif_dlg.notebook ), + gtk_notebook_get_nth_page( GTK_NOTEBOOK( impldif_dlg.notebook ), pageNum ), label ); + + table = gtk_table_new(3, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_container_set_border_width( GTK_CONTAINER(table), 8 ); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8 ); + + /* First row */ + top = 0; + label = gtk_label_new(_("Address Book :")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + labelBook = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), labelBook, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(labelBook), 0, 0.5); + + /* Second row */ + top++; + label = gtk_label_new(_("File Name :")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + labelFile = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), labelFile, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(labelFile), 0, 0.5); + + /* Third row */ + top++; + label = gtk_label_new(_("Records :")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + labelRecs = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), labelRecs, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(labelRecs), 0, 0.5); + + impldif_dlg.labelBook = labelBook; + impldif_dlg.labelFile = labelFile; + impldif_dlg.labelRecords = labelRecs; +} + +static void imp_ldif_dialog_create() { + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *vnbox; + GtkWidget *notebook; + GtkWidget *hbbox; + GtkWidget *btnPrev; + GtkWidget *btnNext; + GtkWidget *btnCancel; + GtkWidget *hsbox; + GtkWidget *statusbar; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request(window, IMPORTLDIF_WIDTH, IMPORTLDIF_HEIGHT ); + gtk_window_set_title( GTK_WINDOW(window), _("Import LDIF file into Address Book") ); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(imp_ldif_delete_event), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(imp_ldif_key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + + vbox = gtk_vbox_new(FALSE, 4); + gtk_widget_show(vbox); + gtk_container_add(GTK_CONTAINER(window), vbox); + + vnbox = gtk_vbox_new(FALSE, 4); + gtk_container_set_border_width(GTK_CONTAINER(vnbox), 4); + gtk_widget_show(vnbox); + gtk_box_pack_start(GTK_BOX(vbox), vnbox, TRUE, TRUE, 0); + + /* Notebook */ + notebook = gtk_notebook_new(); + gtk_notebook_set_show_tabs( GTK_NOTEBOOK(notebook), FALSE ); + gtk_widget_show(notebook); + gtk_box_pack_start(GTK_BOX(vnbox), notebook, TRUE, TRUE, 0); + gtk_container_set_border_width(GTK_CONTAINER(notebook), 6); + + /* Status line */ + hsbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, 0); + statusbar = gtk_statusbar_new(); + gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, 0); + + /* Button panel */ + if (prefs_common.comply_gnome_hig) { + gtkut_stock_button_set_create(&hbbox, &btnNext, _("Next"), + &btnPrev, _("Prev"), + &btnCancel, GTK_STOCK_CANCEL); + } else { + gtkut_stock_button_set_create(&hbbox, &btnPrev, _("Prev"), + &btnNext, _("Next"), + &btnCancel, GTK_STOCK_CANCEL); + } + gtk_box_pack_end(GTK_BOX(vnbox), hbbox, FALSE, FALSE, 0); + gtk_widget_grab_default(btnNext); + + /* Button handlers */ + g_signal_connect(G_OBJECT(btnPrev), "clicked", + G_CALLBACK(imp_ldif_prev), NULL); + g_signal_connect(G_OBJECT(btnNext), "clicked", + G_CALLBACK(imp_ldif_next), NULL); + g_signal_connect(G_OBJECT(btnCancel), "clicked", + G_CALLBACK(imp_ldif_cancel), NULL); + + gtk_widget_show_all(vbox); + + impldif_dlg.window = window; + impldif_dlg.notebook = notebook; + impldif_dlg.btnPrev = btnPrev; + impldif_dlg.btnNext = btnNext; + impldif_dlg.btnCancel = btnCancel; + impldif_dlg.statusbar = statusbar; + impldif_dlg.status_cid = gtk_statusbar_get_context_id( + GTK_STATUSBAR(statusbar), "Import LDIF Dialog" ); + +} + +static void imp_ldif_create() { + imp_ldif_dialog_create(); + imp_ldif_page_file( PAGE_FILE_INFO, _( "File Info" ) ); + imp_ldif_page_fields( PAGE_ATTRIBUTES, _( "Attributes" ) ); + imp_ldif_page_finish( PAGE_FINISH, _( "Finish" ) ); + gtk_widget_show_all( impldif_dlg.window ); +} + +AddressBookFile *addressbook_imp_ldif( AddressIndex *addrIndex ) { + _importedBook_ = NULL; + _imp_addressIndex_ = addrIndex; + + if( ! impldif_dlg.window ) + imp_ldif_create(); + impldif_dlg.cancelled = FALSE; + manage_window_set_transient(GTK_WINDOW(impldif_dlg.window)); + gtk_widget_grab_default(impldif_dlg.btnNext); + + gtk_entry_set_text( GTK_ENTRY(impldif_dlg.name_entry), IMPORTLDIF_GUESS_NAME ); + gtk_entry_set_text( GTK_ENTRY(impldif_dlg.file_entry), "" ); + gtk_label_set_text( GTK_LABEL(impldif_dlg.name_ldif), "" ); + gtk_entry_set_text( GTK_ENTRY(impldif_dlg.name_attrib), "" ); + gtk_clist_clear( GTK_CLIST(impldif_dlg.clist_field) ); + gtk_notebook_set_current_page( GTK_NOTEBOOK(impldif_dlg.notebook), PAGE_FILE_INFO ); + gtk_widget_set_sensitive( impldif_dlg.btnPrev, FALSE ); + gtk_widget_set_sensitive( impldif_dlg.btnNext, TRUE ); + gtk_button_set_label( GTK_BUTTON(impldif_dlg.btnCancel), GTK_STOCK_CANCEL ); + stock_pixmap_gdk( impldif_dlg.window, STOCK_PIXMAP_MARK, + &markxpm, &markxpmmask ); + imp_ldif_message(); + gtk_widget_grab_focus(impldif_dlg.file_entry); + + impldif_dlg.rowIndSelect = -1; + impldif_dlg.rowCount = 0; + g_free( impldif_dlg.nameBook ); + g_free( impldif_dlg.fileName ); + impldif_dlg.nameBook = NULL; + impldif_dlg.fileName = NULL; + + _ldifFile_ = ldif_create(); + + gtk_widget_show(impldif_dlg.window); + + gtk_main(); + gtk_widget_hide(impldif_dlg.window); + ldif_free( _ldifFile_ ); + _ldifFile_ = NULL; + _imp_addressIndex_ = NULL; + + g_free( impldif_dlg.nameBook ); + g_free( impldif_dlg.fileName ); + impldif_dlg.nameBook = NULL; + impldif_dlg.fileName = NULL; + + if( impldif_dlg.cancelled == TRUE ) return NULL; + return _importedBook_; +} + +AddressBookFile *addressbook_imp_ldif_file( AddressIndex *addrIndex, + const gchar *file, + const gchar *book_name ) { + gchar *fsfile; + GList *node, *list; + gboolean ret = FALSE; + + g_return_val_if_fail(addrIndex != NULL, NULL); + g_return_val_if_fail(file != NULL, NULL); + g_return_val_if_fail(book_name != NULL, NULL); + + debug_print("addressbook_imp_ldif_file: file: %s name: %s\n", + file, book_name); + + _importedBook_ = NULL; + _imp_addressIndex_ = addrIndex; + _ldifFile_ = ldif_create(); + + fsfile = conv_filename_from_utf8( file ); + ldif_set_file(_ldifFile_, fsfile); + g_free( fsfile ); + + if( ldif_read_tags( _ldifFile_ ) != MGU_SUCCESS ) + goto finish; + list = ldif_get_fieldlist( _ldifFile_ ); + node = list; + while( node ) { + Ldif_FieldRec *rec = node->data; + if( ! rec->reserved ) { + if( g_ascii_strcasecmp( rec->tagName, "dn" ) != 0 ) { + rec->selected = TRUE; + } + } + node = g_list_next( node ); + } + g_list_free( list ); + + g_free( impldif_dlg.nameBook ); + impldif_dlg.nameBook = g_strdup(book_name); + + ret = imp_ldif_field_move(); + + g_free( impldif_dlg.nameBook ); + impldif_dlg.nameBook = NULL; + +finish: + ldif_free( _ldifFile_ ); + _ldifFile_ = NULL; + _imp_addressIndex_ = NULL; + + if (ret) + debug_print("addressbook_imp_ldif_file: import succeeded\n"); + + return _importedBook_; +} + +/* +* End of Source. +*/ + diff --git a/src/importldif.h b/src/importldif.h new file mode 100644 index 0000000..2cc10e3 --- /dev/null +++ b/src/importldif.h @@ -0,0 +1,37 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Import LDIF data. + */ + +#ifndef __IMPORT_LDIF_H__ +#define __IMPORT_LDIF_H__ + +/* Function prototypes */ +AddressBookFile *addressbook_imp_ldif( AddressIndex *addrIndex ); +AddressBookFile *addressbook_imp_ldif_file( AddressIndex *addrIndex, + const gchar *file, + const gchar *book_name ); + +#endif /* __IMPORT_LDIF_H__ */ + +/* +* End of Source. +*/ diff --git a/src/inc.c b/src/inc.c new file mode 100644 index 0000000..011a9fa --- /dev/null +++ b/src/inc.c @@ -0,0 +1,1936 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "inc.h" +#include "mainwindow.h" +#include "folderview.h" +#include "summaryview.h" +#include "prefs_common.h" +#include "prefs_account.h" +#include "account.h" +#include "procmsg.h" +#include "socket.h" +#include "socks.h" +#include "pop.h" +#include "recv.h" +#include "mbox.h" +#include "imap.h" +#include "utils.h" +#include "gtkutils.h" +#include "statusbar.h" +#include "manage_window.h" +#include "stock_pixmap.h" +#include "progressdialog.h" +#include "alertpanel.h" +#include "trayicon.h" +#include "notificationwindow.h" +#include "filter.h" +#include "folder.h" +#include "procheader.h" +#include "plugin.h" + + +typedef struct _IncAccountNewMsgCount +{ + PrefsAccount *account; + gint new_messages; +} IncAccountNewMsgCount; + +typedef struct _IncMsgSummary +{ + gchar *subject; + gchar *from; +} IncMsgSummary; + +struct _IncResult +{ + GSList *count_list; + GSList *msg_summaries; +}; + + +static GList *inc_dialog_list = NULL; + +static gboolean inc_is_running = FALSE; + +static guint inc_lock_count = 0; +static gboolean block_notify = FALSE; + +static GdkPixbuf *current_pixbuf; +static GdkPixbuf *error_pixbuf; +static GdkPixbuf *ok_pixbuf; + + +static void inc_finished (MainWindow *mainwin, + IncResult *result); +static GSList *inc_add_message_count (GSList *list, + PrefsAccount *account, + gint new_messages); +static void inc_result_free (IncResult *result, + gboolean free_self); + +static gint inc_remote_account_mail (MainWindow *mainwin, + PrefsAccount *account); +static gint inc_account_mail_real (MainWindow *mainwin, + PrefsAccount *account, + IncResult *result); + +static IncProgressDialog *inc_progress_dialog_create + (gboolean autocheck); +static void inc_progress_dialog_set_list(IncProgressDialog *inc_dialog); +static void inc_progress_dialog_destroy (IncProgressDialog *inc_dialog); + +static IncSession *inc_session_new (PrefsAccount *account); +static void inc_session_destroy (IncSession *session); +static gint inc_start (IncProgressDialog *inc_dialog); +static IncState inc_pop3_session_do (IncSession *session); + +static void inc_progress_dialog_update (IncProgressDialog *inc_dialog, + IncSession *inc_session); + +static void inc_progress_dialog_set_label + (IncProgressDialog *inc_dialog, + IncSession *inc_session); +static void inc_progress_dialog_set_progress + (IncProgressDialog *inc_dialog, + IncSession *inc_session); + +static void inc_update_folderview (IncProgressDialog *inc_dialog, + IncSession *inc_session); + +static void inc_progress_dialog_update_periodic + (IncProgressDialog *inc_dialog, + IncSession *inc_session); +static void inc_update_folderview_periodic + (IncProgressDialog *inc_dialog, + IncSession *inc_session); + +static gint inc_recv_data_progressive (Session *session, + guint cur_len, + guint total_len, + gpointer data); +static gint inc_recv_data_finished (Session *session, + guint len, + gpointer data); +static gint inc_recv_message (Session *session, + const gchar *msg, + gpointer data); +static gint inc_drop_message (Pop3Session *session, + const gchar *file); + +static void inc_put_error (IncSession *session, + IncState istate, + const gchar *pop3_msg); + +static void inc_cancel_cb (GtkWidget *widget, + gpointer data); +static void inc_cancel_all_cb (GtkWidget *widget, + gpointer data); +static gint inc_dialog_delete_cb (GtkWidget *widget, + GdkEventAny *event, + gpointer data); + +static gint inc_spool (void); +static gint get_spool (FolderItem *dest, + const gchar *mbox); + +static void inc_autocheck_timer_set_interval (guint interval); +static gint inc_autocheck_func (gpointer data); + + +/** + * inc_finished: + * @mainwin: Main window. + * @result: Information of incorporation result. + * @new_messages: Number of received messages. + * + * Update the folder view and the summary view after receiving + * messages. If @new_messages is 0, this function avoids unneeded + * updating. + **/ +static void inc_finished(MainWindow *mainwin, IncResult *result) +{ + FolderItem *item; + gint new_messages = 0; + gint other_new = 0; + IncAccountNewMsgCount *count; + GSList *cur; + + if (result) { + for (cur = result->count_list; cur != NULL; cur = cur->next) { + count = cur->data; + if (count->new_messages > 0) + new_messages += count->new_messages; + } + } + + debug_print("inc_finished: %d new message(s)\n", new_messages); + + if (prefs_common.scan_all_after_inc) { + other_new = folderview_check_new(NULL); + new_messages += other_new; + } + + if (new_messages > 0 && !block_notify) { + GString *str; + gint c = 0; + + str = g_string_new(""); + g_string_printf(str, _("Sylpheed: %d new messages"), + new_messages); + if (result) { + for (cur = result->count_list; cur != NULL; cur = cur->next) { + count = cur->data; + if (count->new_messages > 0) { + if (c == 0) + g_string_append(str, "\n"); + c++; + g_string_append(str, "\n"); + if (!count->account) + g_string_append_printf(str, _("[Local]: %d"), count->new_messages); + else + g_string_append_printf(str, "%s: %d", count->account->account_name ? count->account->account_name : "[?]", count->new_messages); + } + } + } + debug_print("inc_finished: %s\n", str->str); + trayicon_set_tooltip(str->str); + trayicon_set_notify(TRUE); + + if (prefs_common.enable_newmsg_notify_window) { + gchar buf[1024]; + + g_snprintf(buf, sizeof(buf), _("Sylpheed: %d new messages"), new_messages); + g_string_truncate(str, 0); + if (result) { + for (cur = result->msg_summaries; cur != NULL; cur = cur->next) { + IncMsgSummary *summary = cur->data; + gchar *markup; + + if (str->len > 0) + g_string_append_c(str, '\n'); + markup = g_markup_printf_escaped("%s %s", summary->subject, summary->from); + g_string_append(str, markup); + g_free(markup); + } + } + + notification_window_open(buf, str->str, prefs_common.notify_window_period); + } + + g_string_free(str, TRUE); + } + + syl_plugin_signal_emit("inc-mail-finished", new_messages); + + inc_block_notify(FALSE); + + if (new_messages <= 0 && !prefs_common.scan_all_after_inc) return; + + if (prefs_common.open_inbox_on_inc) { + item = cur_account && cur_account->inbox + ? folder_find_item_from_identifier(cur_account->inbox) + : folder_get_default_inbox(); + folderview_select(mainwin->folderview, item); + } else if (prefs_common.scan_all_after_inc) { + item = mainwin->summaryview->folder_item; + if (item) + folderview_update_item(item, TRUE); + } + + /* Notification */ + + if (new_messages > 0 && + prefs_common.enable_newmsg_notify_sound && + prefs_common.newmsg_notify_sound) { + play_sound(prefs_common.newmsg_notify_sound, TRUE); + } + + if (new_messages > 0 && + prefs_common.enable_newmsg_notify && + prefs_common.newmsg_notify_cmd) { + gchar buf[1024]; + + if (str_find_format_times + (prefs_common.newmsg_notify_cmd, 'd') == 1) + g_snprintf(buf, sizeof(buf), + prefs_common.newmsg_notify_cmd, + new_messages); + else + strncpy2(buf, prefs_common.newmsg_notify_cmd, + sizeof(buf)); + execute_command_line(buf, TRUE); + } +} + +static GSList *inc_add_message_count(GSList *list, PrefsAccount *account, + gint new_messages) +{ + IncAccountNewMsgCount *count; + + count = g_new(IncAccountNewMsgCount, 1); + count->account = account; + count->new_messages = new_messages; + + return g_slist_append(list, count); +} + +static void inc_result_free(IncResult *result, gboolean free_self) +{ + GSList *cur; + + slist_free_strings(result->count_list); + g_slist_free(result->count_list); + for (cur = result->msg_summaries; cur != NULL; cur = cur->next) { + IncMsgSummary *sum = cur->data; + g_free(sum->subject); + g_free(sum->from); + g_free(sum); + } + g_slist_free(result->msg_summaries); + + if (free_self) + g_free(result); +} + +void inc_mail(MainWindow *mainwin) +{ + IncResult result = {NULL, NULL}; + gint new_msgs = 0; + + if (inc_lock_count) return; + if (inc_is_active()) return; + + if (!main_window_toggle_online_if_offline(mainwin)) + return; + + inc_is_running = TRUE; + + inc_autocheck_timer_remove(); + summary_write_cache(mainwin->summaryview); + main_window_lock(mainwin); + + syl_plugin_signal_emit("inc-mail-start", cur_account); + + if (prefs_common.use_extinc && prefs_common.extinc_cmd) { + /* external incorporating program */ + if (execute_command_line(prefs_common.extinc_cmd, FALSE) != 0) { + inc_is_running = FALSE; + main_window_unlock(mainwin); + inc_autocheck_timer_set(); + return; + } + + if (prefs_common.inc_local) { + new_msgs = inc_spool(); + result.count_list = inc_add_message_count(result.count_list, NULL, new_msgs); + } + } else { + if (prefs_common.inc_local) { + new_msgs = inc_spool(); + if (new_msgs < 0) + new_msgs = 0; + result.count_list = inc_add_message_count(result.count_list, NULL, new_msgs); + } + + new_msgs = inc_account_mail_real(mainwin, cur_account, &result); + } + + inc_finished(mainwin, &result); + inc_result_free(&result, FALSE); + + inc_is_running = FALSE; + + main_window_unlock(mainwin); + inc_autocheck_timer_set(); +} + +static gint inc_remote_account_mail(MainWindow *mainwin, PrefsAccount *account) +{ + FolderItem *item = mainwin->summaryview->folder_item; + gint new_msgs = 0; + gboolean update_summary = FALSE; + + g_return_val_if_fail(account != NULL, 0); + g_return_val_if_fail(account->folder != NULL, 0); + + if (account->protocol == A_IMAP4 && + account->imap_filter_inbox_on_recv) { + FolderItem *inbox = FOLDER(account->folder)->inbox; + GSList *mlist, *cur; + FilterInfo *fltinfo; + GSList junk_fltlist = {NULL, NULL}; + FilterRule *junk_rule; + gint n_filtered = 0; + + debug_print("inc_remote_account_mail(): filtering IMAP4 INBOX\n"); + mlist = folder_item_get_uncached_msg_list(inbox); + debug_print("inc_remote_account_mail(): uncached messages: %d\n", g_slist_length(mlist)); + + junk_rule = filter_junk_rule_create(account, NULL, TRUE); + if (junk_rule) + junk_fltlist.data = junk_rule; + + for (cur = mlist; cur != NULL; cur = cur->next) { + MsgInfo *msginfo = (MsgInfo *)cur->data; + gboolean is_junk = FALSE; + + fltinfo = filter_info_new(); + fltinfo->account = account; + fltinfo->flags = msginfo->flags; + + if (prefs_common.enable_junk && + prefs_common.filter_junk_on_recv && + prefs_common.filter_junk_before && junk_rule) { + filter_apply_msginfo + (&junk_fltlist, msginfo, fltinfo); + if (fltinfo->drop_done) + is_junk = TRUE; + } + + if (!fltinfo->drop_done) { + filter_apply_msginfo(prefs_common.fltlist, + msginfo, fltinfo); + } + + if (!fltinfo->drop_done && + prefs_common.enable_junk && + prefs_common.filter_junk_on_recv && + !prefs_common.filter_junk_before && junk_rule) { + filter_apply_msginfo + (&junk_fltlist, msginfo, fltinfo); + if (fltinfo->drop_done) + is_junk = TRUE; + } + + if (msginfo->flags.perm_flags != + fltinfo->flags.perm_flags) { + msginfo->flags = fltinfo->flags; + inbox->mark_dirty = TRUE; + if (fltinfo->actions[FLT_ACTION_MARK]) + imap_msg_set_perm_flags + (msginfo, MSG_MARKED); + if (fltinfo->actions[FLT_ACTION_MARK_READ]) + imap_msg_unset_perm_flags + (msginfo, MSG_NEW|MSG_UNREAD); + } + + if (fltinfo->actions[FLT_ACTION_MOVE] && + fltinfo->move_dest) { + folder_item_move_msg + (fltinfo->move_dest, msginfo); + if (account->imap_check_inbox_only || + fltinfo->move_dest->folder != + inbox->folder) { + if (!is_junk && + fltinfo->move_dest->stype != F_TRASH && + fltinfo->move_dest->stype != F_JUNK && + (MSG_IS_NEW(fltinfo->flags) || + MSG_IS_UNREAD(fltinfo->flags))) + ++new_msgs; + } + } else if (fltinfo->actions[FLT_ACTION_DELETE]) + folder_item_remove_msg(inbox, msginfo); + else if (!is_junk && (MSG_IS_NEW(msginfo->flags) || + MSG_IS_UNREAD(msginfo->flags))) + ++new_msgs; + + if (fltinfo->drop_done) + ++n_filtered; + + filter_info_free(fltinfo); + } + + if (junk_rule) + filter_rule_free(junk_rule); + + procmsg_msg_list_free(mlist); + + debug_print("inc_remote_account_mail(): INBOX: %d new, %d filtered\n", + new_msgs, n_filtered); + + if (!prefs_common.scan_all_after_inc && item != NULL && + inbox == item) + update_summary = TRUE; + } + + if (account->protocol == A_IMAP4 && account->imap_check_inbox_only) { + FolderItem *inbox = FOLDER(account->folder)->inbox; + + new_msgs += folderview_check_new_item(inbox); + if (!prefs_common.scan_all_after_inc && item != NULL && + inbox == item) + update_summary = TRUE; + } else { + new_msgs += folderview_check_new(FOLDER(account->folder)); + if (!prefs_common.scan_all_after_inc && item != NULL && + FOLDER(account->folder) == item->folder) + update_summary = TRUE; + } + + if (update_summary) + folderview_update_item(item, TRUE); + folderview_update_all_updated(FALSE); + + return new_msgs; +} + +static gint inc_account_mail_real(MainWindow *mainwin, PrefsAccount *account, + IncResult *result) +{ + IncProgressDialog *inc_dialog; + IncSession *session; + + g_return_val_if_fail(account != NULL, 0); + + if (account->protocol == A_IMAP4 || account->protocol == A_NNTP) + return inc_remote_account_mail(mainwin, account); + + session = inc_session_new(account); + if (!session) return 0; + + inc_dialog = inc_progress_dialog_create(FALSE); + inc_dialog->queue_list = g_list_append(inc_dialog->queue_list, session); + inc_dialog->mainwin = mainwin; + inc_dialog->result = result; + inc_progress_dialog_set_list(inc_dialog); + + main_window_set_toolbar_sensitive(mainwin); + main_window_set_menu_sensitive(mainwin); + + return inc_start(inc_dialog); +} + +gint inc_account_mail(MainWindow *mainwin, PrefsAccount *account) +{ + IncResult result = {NULL, NULL}; + gint new_msgs; + + if (inc_lock_count) return 0; + if (inc_is_active()) return 0; + + if (!main_window_toggle_online_if_offline(mainwin)) + return 0; + + inc_is_running = TRUE; + + inc_autocheck_timer_remove(); + summary_write_cache(mainwin->summaryview); + main_window_lock(mainwin); + + syl_plugin_signal_emit("inc-mail-start", account); + + new_msgs = inc_account_mail_real(mainwin, account, &result); + + inc_finished(mainwin, &result); + inc_result_free(&result, FALSE); + + inc_is_running = FALSE; + + main_window_unlock(mainwin); + inc_autocheck_timer_set(); + + return new_msgs; +} + +void inc_all_account_mail(MainWindow *mainwin, gboolean autocheck) +{ + GList *list, *queue_list = NULL; + IncProgressDialog *inc_dialog; + IncResult result = {NULL, NULL}; + gint new_msgs = 0; + + if (inc_lock_count) return; + if (inc_is_active()) return; + + if (!main_window_toggle_online_if_offline(mainwin)) + return; + + inc_is_running = TRUE; + + inc_autocheck_timer_remove(); + summary_write_cache(mainwin->summaryview); + main_window_lock(mainwin); + + syl_plugin_signal_emit("inc-mail-start", NULL); + + if (prefs_common.inc_local) { + new_msgs = inc_spool(); + if (new_msgs < 0) + new_msgs = 0; + result.count_list = inc_add_message_count(result.count_list, NULL, new_msgs); + } + + /* check IMAP4 / News folders */ + for (list = account_get_list(); list != NULL; list = list->next) { + PrefsAccount *account = list->data; + if ((account->protocol == A_IMAP4 || + account->protocol == A_NNTP) && account->recv_at_getall) { + new_msgs = inc_remote_account_mail(mainwin, account); + result.count_list = inc_add_message_count(result.count_list, account, new_msgs); + } + } + + /* check POP3 accounts */ + for (list = account_get_list(); list != NULL; list = list->next) { + IncSession *session; + PrefsAccount *account = list->data; + + if (account->recv_at_getall) { + session = inc_session_new(account); + if (session) + queue_list = g_list_append(queue_list, session); + } + } + + if (queue_list) { + inc_dialog = inc_progress_dialog_create(autocheck); + inc_dialog->queue_list = queue_list; + inc_dialog->mainwin = mainwin; + inc_dialog->result = &result; + inc_progress_dialog_set_list(inc_dialog); + + main_window_set_toolbar_sensitive(mainwin); + main_window_set_menu_sensitive(mainwin); + + inc_start(inc_dialog); + } + + inc_finished(mainwin, &result); + inc_result_free(&result, FALSE); + + inc_is_running = FALSE; + + main_window_unlock(mainwin); + inc_autocheck_timer_set(); +} + +gint inc_pop_before_smtp(PrefsAccount *account) +{ + MainWindow *mainwin; + IncProgressDialog *inc_dialog; + IncSession *session; + + if (inc_lock_count) return -1; + + mainwin = main_window_get(); + + if (!main_window_toggle_online_if_offline(mainwin)) + return -1; + + inc_is_running = TRUE; + + inc_autocheck_timer_remove(); + main_window_lock(mainwin); + + session = inc_session_new(account); + if (!session) return -1; + POP3_SESSION(session->session)->auth_only = TRUE; + + inc_dialog = inc_progress_dialog_create(FALSE); + gtk_window_set_title(GTK_WINDOW(inc_dialog->dialog->window), + _("Authenticating with POP3")); + inc_dialog->queue_list = g_list_append(inc_dialog->queue_list, session); + inc_dialog->mainwin = mainwin; + inc_dialog->result = NULL; + inc_progress_dialog_set_list(inc_dialog); + inc_dialog->show_dialog = TRUE; + + main_window_set_toolbar_sensitive(mainwin); + main_window_set_menu_sensitive(mainwin); + + inc_start(inc_dialog); + + inc_is_running = FALSE; + + main_window_unlock(mainwin); + inc_autocheck_timer_set(); + + return 0; +} + +static IncProgressDialog *inc_progress_dialog_create(gboolean autocheck) +{ + IncProgressDialog *dialog; + ProgressDialog *progress; + GtkWidget *cancel_all_btn; + + dialog = g_new0(IncProgressDialog, 1); + + progress = progress_dialog_create(); + gtk_window_set_title(GTK_WINDOW(progress->window), + _("Retrieving new messages")); + cancel_all_btn = gtk_dialog_add_button(GTK_DIALOG(progress->window), + _("Cancel _all"), + GTK_RESPONSE_NONE); + g_signal_connect(G_OBJECT(progress->cancel_btn), "clicked", + G_CALLBACK(inc_cancel_cb), dialog); + g_signal_connect(G_OBJECT(cancel_all_btn), "clicked", + G_CALLBACK(inc_cancel_all_cb), dialog); + g_signal_connect(G_OBJECT(progress->window), "delete_event", + G_CALLBACK(inc_dialog_delete_cb), dialog); + /* manage_window_set_transient(GTK_WINDOW(progress->window)); */ + + progress_dialog_set_value(progress, 0.0); + + stock_pixbuf_gdk(progress->treeview, STOCK_PIXMAP_COMPLETE, &ok_pixbuf); + stock_pixbuf_gdk(progress->treeview, STOCK_PIXMAP_CONTINUE, + ¤t_pixbuf); + stock_pixbuf_gdk(progress->treeview, STOCK_PIXMAP_ERROR, &error_pixbuf); + + if (prefs_common.recv_dialog_mode == RECV_DIALOG_ALWAYS || + (prefs_common.recv_dialog_mode == RECV_DIALOG_MANUAL && + !autocheck)) { + dialog->show_dialog = TRUE; + gtk_widget_show_now(progress->window); + } + + dialog->dialog = progress; + g_get_current_time(&dialog->progress_tv); + g_get_current_time(&dialog->folder_tv); + dialog->queue_list = NULL; + dialog->cur_row = 0; + + inc_dialog_list = g_list_append(inc_dialog_list, dialog); + + return dialog; +} + +static void inc_progress_dialog_set_list(IncProgressDialog *inc_dialog) +{ + GList *list; + + for (list = inc_dialog->queue_list; list != NULL; list = list->next) { + IncSession *session = list->data; + Pop3Session *pop3_session = POP3_SESSION(session->session); + + session->data = inc_dialog; + progress_dialog_append(inc_dialog->dialog, NULL, + pop3_session->ac_prefs->account_name, + _("Standby"), "", NULL); + } +} + +static void inc_progress_dialog_clear(IncProgressDialog *inc_dialog) +{ + progress_dialog_set_value(inc_dialog->dialog, 0.0); + progress_dialog_set_label(inc_dialog->dialog, ""); + main_window_progress_off(inc_dialog->mainwin); +} + +static void inc_progress_dialog_destroy(IncProgressDialog *inc_dialog) +{ + g_return_if_fail(inc_dialog != NULL); + + inc_dialog_list = g_list_remove(inc_dialog_list, inc_dialog); + + manage_window_destroy(inc_dialog->dialog->window, NULL); + + main_window_progress_off(inc_dialog->mainwin); + progress_dialog_destroy(inc_dialog->dialog); + + g_free(inc_dialog); +} + +static IncSession *inc_session_new(PrefsAccount *account) +{ + IncSession *session; + FilterRule *rule; + + g_return_val_if_fail(account != NULL, NULL); + + if (account->protocol != A_POP3) + return NULL; + if (!account->recv_server || !account->userid) + return NULL; + + session = g_new0(IncSession, 1); + + session->session = pop3_session_new(account); + session->session->data = session; + POP3_SESSION(session->session)->drop_message = inc_drop_message; + session_set_recv_message_notify(session->session, + inc_recv_message, session); + session_set_recv_data_progressive_notify(session->session, + inc_recv_data_progressive, + session); + session_set_recv_data_notify(session->session, + inc_recv_data_finished, session); + + session->inc_state = INC_SUCCESS; + + session->folder_table = g_hash_table_new(NULL, NULL); + session->tmp_folder_table = g_hash_table_new(NULL, NULL); + + rule = filter_junk_rule_create(account, NULL, FALSE); + if (rule) + session->junk_fltlist = g_slist_append(NULL, rule); + else + session->junk_fltlist = NULL; + + session->cur_total_bytes = 0; + session->new_msgs = 0; + + session->start_num = 0; + session->start_recv_bytes = 0; + + session->retr_count = 0; + + return session; +} + +static void inc_session_destroy(IncSession *session) +{ + g_return_if_fail(session != NULL); + + session_destroy(session->session); + g_hash_table_destroy(session->folder_table); + g_hash_table_destroy(session->tmp_folder_table); + if (session->junk_fltlist) + filter_rule_list_free(session->junk_fltlist); + g_free(session); +} + +static void inc_update_folder_foreach(GHashTable *table) +{ + procmsg_flush_folder_foreach(table); + folderview_update_item_foreach(table, TRUE); +} + +static gint inc_start(IncProgressDialog *inc_dialog) +{ + IncSession *session; + GList *qlist; + Pop3Session *pop3_session; + IncState inc_state; + gint error_num = 0; + gint new_msgs = 0; + gchar *msg; + gchar *fin_msg; + + qlist = inc_dialog->queue_list; + while (qlist != NULL) { + GList *next = qlist->next; + + session = qlist->data; + if (session->inc_state == INC_CANCEL) { + qlist = next; + continue; + } + + pop3_session = POP3_SESSION(session->session); + if (!pop3_session->pass) { + gchar *pass; + + if (inc_dialog->show_dialog) + manage_window_focus_in + (inc_dialog->dialog->window, + NULL, NULL); + + pass = input_query_password + (pop3_session->ac_prefs->recv_server, + pop3_session->user); + + if (inc_dialog->show_dialog) + manage_window_focus_out + (inc_dialog->dialog->window, + NULL, NULL); + + if (pass) { + pop3_session->ac_prefs->tmp_pass = + g_strdup(pass); + pop3_session->pass = pass; + } + } + + qlist = next; + } + +#define SET_PIXMAP_AND_TEXT(pixbuf, status, progress) \ +{ \ + progress_dialog_set_row_pixbuf(inc_dialog->dialog, \ + inc_dialog->cur_row, pixbuf); \ + progress_dialog_set_row_status(inc_dialog->dialog, \ + inc_dialog->cur_row, status); \ + if (progress) \ + progress_dialog_set_row_progress(inc_dialog->dialog, \ + inc_dialog->cur_row, \ + progress); \ +} + + for (; inc_dialog->queue_list != NULL; inc_dialog->cur_row++) { + session = inc_dialog->queue_list->data; + pop3_session = POP3_SESSION(session->session); + + if (session->inc_state == INC_CANCEL || + pop3_session->pass == NULL) { + SET_PIXMAP_AND_TEXT(ok_pixbuf, _("Cancelled"), NULL); + inc_session_destroy(session); + inc_dialog->queue_list = + g_list_remove(inc_dialog->queue_list, session); + continue; + } + + inc_progress_dialog_clear(inc_dialog); + progress_dialog_scroll_to_row(inc_dialog->dialog, + inc_dialog->cur_row); + + SET_PIXMAP_AND_TEXT(current_pixbuf, _("Retrieving"), NULL); + + /* begin POP3 session */ + inc_state = inc_pop3_session_do(session); + + switch (inc_state) { + case INC_SUCCESS: + if (pop3_session->cur_total_num > 0) + msg = g_strdup_printf + (_("%d message(s) (%s) received"), + pop3_session->cur_total_num, + to_human_readable(pop3_session->cur_total_recv_bytes)); + else + msg = g_strdup_printf(_("no new messages")); + SET_PIXMAP_AND_TEXT(ok_pixbuf, _("Done"), msg); + g_free(msg); + break; + case INC_LOOKUP_ERROR: + SET_PIXMAP_AND_TEXT(error_pixbuf, + _("Server not found"), NULL); + break; + case INC_CONNECT_ERROR: + SET_PIXMAP_AND_TEXT(error_pixbuf, + _("Connection failed"), NULL); + break; + case INC_AUTH_FAILED: + SET_PIXMAP_AND_TEXT(error_pixbuf, _("Auth failed"), + NULL); + break; + case INC_LOCKED: + SET_PIXMAP_AND_TEXT(error_pixbuf, _("Locked"), NULL); + break; + case INC_ERROR: + case INC_NO_SPACE: + case INC_IO_ERROR: + case INC_SOCKET_ERROR: + case INC_EOF: + SET_PIXMAP_AND_TEXT(error_pixbuf, _("Error"), NULL); + break; + case INC_TIMEOUT: + SET_PIXMAP_AND_TEXT(error_pixbuf, _("Timeout"), NULL); + break; + case INC_CANCEL: + SET_PIXMAP_AND_TEXT(ok_pixbuf, _("Cancelled"), NULL); + break; + default: + break; + } + + if (inc_dialog->result) + inc_dialog->result->count_list = inc_add_message_count(inc_dialog->result->count_list, pop3_session->ac_prefs, session->new_msgs); + new_msgs += session->new_msgs; + + if (!prefs_common.scan_all_after_inc) { + inc_update_folder_foreach(session->folder_table); + } + + if (pop3_session->error_val == PS_AUTHFAIL && + pop3_session->ac_prefs->tmp_pass) { + g_free(pop3_session->ac_prefs->tmp_pass); + pop3_session->ac_prefs->tmp_pass = NULL; + } + + pop3_write_uidl_list(pop3_session); + + if (inc_state != INC_SUCCESS && inc_state != INC_CANCEL) { + error_num++; + if (inc_dialog->show_dialog) + manage_window_focus_in + (inc_dialog->dialog->window, + NULL, NULL); + inc_put_error(session, inc_state, + pop3_session->error_msg); + if (inc_dialog->show_dialog) + manage_window_focus_out + (inc_dialog->dialog->window, + NULL, NULL); + if (inc_state == INC_NO_SPACE || + inc_state == INC_IO_ERROR) + break; + } + + inc_session_destroy(session); + inc_dialog->queue_list = + g_list_remove(inc_dialog->queue_list, session); + } + +#undef SET_PIXMAP_AND_TEXT + + if (new_msgs > 0) + fin_msg = g_strdup_printf(_("Finished (%d new message(s))"), + new_msgs); + else + fin_msg = g_strdup_printf(_("Finished (no new messages)")); + + progress_dialog_set_label(inc_dialog->dialog, fin_msg); + +#if 0 + if (error_num && !prefs_common.no_recv_err_panel) { + if (inc_dialog->show_dialog) + manage_window_focus_in(inc_dialog->dialog->window, + NULL, NULL); + alertpanel_error(_("Some errors occurred while getting mail.")); + if (inc_dialog->show_dialog) + manage_window_focus_out(inc_dialog->dialog->window, + NULL, NULL); + } +#endif + + while (inc_dialog->queue_list != NULL) { + session = inc_dialog->queue_list->data; + inc_session_destroy(session); + inc_dialog->queue_list = + g_list_remove(inc_dialog->queue_list, session); + } + + if (prefs_common.close_recv_dialog || !inc_dialog->show_dialog) + inc_progress_dialog_destroy(inc_dialog); + else { + gtk_window_set_title(GTK_WINDOW(inc_dialog->dialog->window), + fin_msg); + gtk_button_set_label(GTK_BUTTON(inc_dialog->dialog->cancel_btn), + GTK_STOCK_CLOSE); + } + + g_free(fin_msg); + + return new_msgs; +} + +static IncState inc_pop3_session_do(IncSession *session) +{ + Pop3Session *pop3_session = POP3_SESSION(session->session); + IncProgressDialog *inc_dialog = (IncProgressDialog *)session->data; + PrefsAccount *ac = pop3_session->ac_prefs; + SocksInfo *socks_info = NULL; + gchar *buf; + + debug_print(_("getting new messages of account %s...\n"), + ac->account_name); + + if (pop3_session->auth_only) + buf = g_strdup_printf(_("%s: Authenticating with POP3"), + ac->recv_server); + else + buf = g_strdup_printf(_("%s: Retrieving new messages"), + ac->recv_server); + gtk_window_set_title(GTK_WINDOW(inc_dialog->dialog->window), buf); + g_free(buf); + + buf = g_strdup_printf(_("Connecting to POP3 server: %s..."), + ac->recv_server); + log_message("%s\n", buf); + progress_dialog_set_label(inc_dialog->dialog, buf); + g_free(buf); + + session_set_timeout(SESSION(pop3_session), + prefs_common.io_timeout_secs * 1000); + + if (ac->use_socks && ac->use_socks_for_recv) { + socks_info = socks_info_new(ac->socks_type, ac->proxy_host, ac->proxy_port, ac->use_proxy_auth ? ac->proxy_name : NULL, ac->use_proxy_auth ? ac->proxy_pass : NULL); + } + + GTK_EVENTS_FLUSH(); + + if (session_connect_full(SESSION(pop3_session), + SESSION(pop3_session)->server, + SESSION(pop3_session)->port, socks_info) < 0) { + log_warning(_("Can't connect to POP3 server: %s:%d\n"), + SESSION(pop3_session)->server, + SESSION(pop3_session)->port); + session->inc_state = INC_CONNECT_ERROR; + if (session_get_error(SESSION(pop3_session)) == SESSION_ERROR_LOOKUP) + session->inc_state = INC_LOOKUP_ERROR; + statusbar_pop_all(); + return session->inc_state; + } + + while (session_is_connected(SESSION(pop3_session)) && + session->inc_state != INC_CANCEL) { + gtk_main_iteration(); + } + log_window_flush(); + + debug_print("inc_state: %d\n", session->inc_state); + debug_print("pop3_session.error_val: %d\n", pop3_session->error_val); + debug_print("pop3_session.error_msg: %s\n", pop3_session->error_msg ? pop3_session->error_msg : "(empty)"); + + if (session->inc_state == INC_SUCCESS) { + switch (pop3_session->error_val) { + case PS_SUCCESS: + switch (SESSION(pop3_session)->state) { + case SESSION_ERROR: + if (pop3_session->state == POP3_READY) + session->inc_state = INC_CONNECT_ERROR; + else + session->inc_state = INC_ERROR; + if (session_get_error(SESSION(pop3_session)) == SESSION_ERROR_LOOKUP) + session->inc_state = INC_LOOKUP_ERROR; + break; + case SESSION_EOF: + session->inc_state = INC_EOF; + break; + case SESSION_TIMEOUT: + session->inc_state = INC_TIMEOUT; + break; + default: + session->inc_state = INC_SUCCESS; + break; + } + break; + case PS_AUTHFAIL: + session->inc_state = INC_AUTH_FAILED; + break; + case PS_IOERR: + session->inc_state = INC_IO_ERROR; + break; + case PS_SOCKET: + session->inc_state = INC_SOCKET_ERROR; + break; + case PS_LOCKBUSY: + session->inc_state = INC_LOCKED; + break; + default: + session->inc_state = INC_ERROR; + break; + } + } + + session_disconnect(SESSION(pop3_session)); + statusbar_pop_all(); + + return session->inc_state; +} + +static void inc_progress_dialog_update(IncProgressDialog *inc_dialog, + IncSession *inc_session) +{ + inc_progress_dialog_set_label(inc_dialog, inc_session); + inc_progress_dialog_set_progress(inc_dialog, inc_session); +} + +static void inc_progress_dialog_set_label(IncProgressDialog *inc_dialog, + IncSession *inc_session) +{ + ProgressDialog *dialog = inc_dialog->dialog; + Pop3Session *session; + + g_return_if_fail(inc_session != NULL); + + session = POP3_SESSION(inc_session->session); + + switch (session->state) { + case POP3_GREETING: + break; + case POP3_GETAUTH_USER: + case POP3_GETAUTH_PASS: + case POP3_GETAUTH_APOP: + progress_dialog_set_label(dialog, _("Authenticating...")); + statusbar_print_all(_("Retrieving messages from %s..."), + SESSION(session)->server); + break; + case POP3_GETRANGE_STAT: + progress_dialog_set_label + (dialog, _("Getting the number of new messages (STAT)...")); + break; + case POP3_GETRANGE_LAST: + progress_dialog_set_label + (dialog, _("Getting the number of new messages (LAST)...")); + break; + case POP3_GETRANGE_UIDL: + progress_dialog_set_label + (dialog, _("Getting the number of new messages (UIDL)...")); + break; + case POP3_GETSIZE_LIST: + progress_dialog_set_label + (dialog, _("Getting the size of messages (LIST)...")); + break; + case POP3_RETR: + case POP3_RETR_RECV: + break; + case POP3_DELETE: +#if 0 + if (session->msg[session->cur_msg].recv_time < + session->current_time) { + gchar buf[BUFFSIZE]; + g_snprintf(buf, sizeof(buf), _("Deleting message %d"), + session->cur_msg); + progress_dialog_set_label(dialog, buf); + } +#endif + break; + case POP3_LOGOUT: + progress_dialog_set_label(dialog, _("Quitting")); + break; + default: + break; + } +} + +static void inc_progress_dialog_set_progress(IncProgressDialog *inc_dialog, + IncSession *inc_session) +{ + gchar buf[BUFFSIZE]; + Pop3Session *pop3_session = POP3_SESSION(inc_session->session); + gint64 cur_total; + gint64 total; + gint cur_num; + gint total_num_to_recv; + + if (!pop3_session->new_msg_exist) return; + + if (inc_session->retr_count == 0) { + cur_num = total_num_to_recv = 0; + cur_total = total = 0; + } else { + cur_num = pop3_session->cur_msg - inc_session->start_num + 1; + total_num_to_recv = pop3_session->count - inc_session->start_num + 1; + cur_total = inc_session->cur_total_bytes - inc_session->start_recv_bytes; + total = pop3_session->total_bytes - inc_session->start_recv_bytes; + } + + if ((pop3_session->state == POP3_RETR || + pop3_session->state == POP3_RETR_RECV || + pop3_session->state == POP3_DELETE) && total_num_to_recv > 0) { + gchar total_size_str[16]; + + to_human_readable_buf(total_size_str, sizeof(total_size_str), + total); + g_snprintf(buf, sizeof(buf), + _("Retrieving message (%d / %d) (%s / %s)"), + cur_num, total_num_to_recv, + to_human_readable(cur_total), total_size_str); + progress_dialog_set_label(inc_dialog->dialog, buf); + } + + if (total > 0) + progress_dialog_set_percentage + (inc_dialog->dialog, (gfloat)cur_total / (gfloat)total); + + gtk_progress_set_show_text + (GTK_PROGRESS(inc_dialog->mainwin->progressbar), TRUE); + if (total_num_to_recv > 0) + g_snprintf(buf, sizeof(buf), "%d / %d", cur_num, total_num_to_recv); + else + buf[0] = '\0'; + gtk_progress_set_format_string + (GTK_PROGRESS(inc_dialog->mainwin->progressbar), buf); + if (total > 0) + gtk_progress_bar_update + (GTK_PROGRESS_BAR(inc_dialog->mainwin->progressbar), + (gfloat)cur_total / (gfloat)total); + + if (pop3_session->cur_total_num > 0) { + g_snprintf(buf, sizeof(buf), + _("%d message(s) (%s) received"), + pop3_session->cur_total_num, + to_human_readable(pop3_session->cur_total_recv_bytes)); + progress_dialog_set_row_progress(inc_dialog->dialog, + inc_dialog->cur_row, buf); + } +} + +static gboolean hash_remove_func(gpointer key, gpointer value, gpointer data) +{ + return TRUE; +} + +static void inc_update_folderview(IncProgressDialog *inc_dialog, + IncSession *inc_session) +{ + MainWindow *mainwin; + + if (g_hash_table_size(inc_session->tmp_folder_table) > 0) { + folderview_update_item_foreach(inc_session->tmp_folder_table, + FALSE); + g_hash_table_foreach_remove(inc_session->tmp_folder_table, + hash_remove_func, NULL); + } + + mainwin = main_window_get(); + summary_show_queued_msgs(mainwin->summaryview); +} + +static void inc_progress_dialog_update_periodic(IncProgressDialog *inc_dialog, + IncSession *inc_session) +{ + GTimeVal tv_cur; + GTimeVal tv_result; + gint msec; + + g_get_current_time(&tv_cur); + + tv_result.tv_sec = tv_cur.tv_sec - inc_dialog->progress_tv.tv_sec; + tv_result.tv_usec = tv_cur.tv_usec - inc_dialog->progress_tv.tv_usec; + if (tv_result.tv_usec < 0) { + tv_result.tv_sec--; + tv_result.tv_usec += G_USEC_PER_SEC; + } + + msec = tv_result.tv_sec * 1000 + tv_result.tv_usec / 1000; + if (msec > PROGRESS_UPDATE_INTERVAL) { + inc_progress_dialog_update(inc_dialog, inc_session); + inc_dialog->progress_tv.tv_sec = tv_cur.tv_sec; + inc_dialog->progress_tv.tv_usec = tv_cur.tv_usec; + } +} + +static void inc_update_folderview_periodic(IncProgressDialog *inc_dialog, + IncSession *inc_session) +{ + GTimeVal tv_cur; + GTimeVal tv_result; + gint msec; + + g_get_current_time(&tv_cur); + + tv_result.tv_sec = tv_cur.tv_sec - inc_dialog->folder_tv.tv_sec; + tv_result.tv_usec = tv_cur.tv_usec - inc_dialog->folder_tv.tv_usec; + if (tv_result.tv_usec < 0) { + tv_result.tv_sec--; + tv_result.tv_usec += G_USEC_PER_SEC; + } + + msec = tv_result.tv_sec * 1000 + tv_result.tv_usec / 1000; + if (msec > FOLDER_UPDATE_INTERVAL) { + inc_update_folderview(inc_dialog, inc_session); + inc_dialog->folder_tv.tv_sec = tv_cur.tv_sec; + inc_dialog->folder_tv.tv_usec = tv_cur.tv_usec; + } +} + +static gint inc_recv_data_progressive(Session *session, guint cur_len, + guint total_len, gpointer data) +{ + IncSession *inc_session = (IncSession *)data; + Pop3Session *pop3_session = POP3_SESSION(session); + IncProgressDialog *inc_dialog; + gint64 cur_total; + + g_return_val_if_fail(inc_session != NULL, -1); + + if (pop3_session->state != POP3_RETR && + pop3_session->state != POP3_RETR_RECV && + pop3_session->state != POP3_DELETE && + pop3_session->state != POP3_LOGOUT) return 0; + + if (!pop3_session->new_msg_exist) return 0; + + gdk_threads_enter(); + + cur_total = pop3_session->cur_total_bytes + cur_len; + if (cur_total > pop3_session->total_bytes) + cur_total = pop3_session->total_bytes; + inc_session->cur_total_bytes = cur_total; + + inc_dialog = (IncProgressDialog *)inc_session->data; + inc_progress_dialog_update_periodic(inc_dialog, inc_session); + inc_update_folderview_periodic(inc_dialog, inc_session); + + gdk_threads_leave(); + return 0; +} + +static gint inc_recv_data_finished(Session *session, guint len, gpointer data) +{ + IncSession *inc_session = (IncSession *)data; + IncProgressDialog *inc_dialog; + + g_return_val_if_fail(inc_session != NULL, -1); + + inc_dialog = (IncProgressDialog *)inc_session->data; + + inc_recv_data_progressive(session, 0, 0, inc_session); + + gdk_threads_enter(); + + if (POP3_SESSION(session)->state == POP3_LOGOUT) { + inc_progress_dialog_update(inc_dialog, inc_session); + inc_update_folderview(inc_dialog, inc_session); + } + + gdk_threads_leave(); + return 0; +} + +static gint inc_recv_message(Session *session, const gchar *msg, gpointer data) +{ + IncSession *inc_session = (IncSession *)data; + IncProgressDialog *inc_dialog; + Pop3Session *pop3_session = POP3_SESSION(session); + + g_return_val_if_fail(inc_session != NULL, -1); + + inc_dialog = (IncProgressDialog *)inc_session->data; + + switch (POP3_SESSION(session)->state) { + case POP3_GETAUTH_USER: + case POP3_GETAUTH_PASS: + case POP3_GETAUTH_APOP: + case POP3_GETRANGE_STAT: + case POP3_GETRANGE_LAST: + case POP3_GETRANGE_UIDL: + case POP3_GETSIZE_LIST: + gdk_threads_enter(); + inc_progress_dialog_update(inc_dialog, inc_session); + gdk_threads_leave(); + break; + case POP3_RETR_RECV: + if (inc_session->retr_count == 0) { + inc_session->start_num = pop3_session->cur_msg; + inc_session->start_recv_bytes = pop3_session->cur_total_bytes; + inc_session->cur_total_bytes = pop3_session->cur_total_bytes; +#if 0 + g_print("total_bytes_to_recv = %lld total_num_to_recv = %d\n", pop3_session->total_bytes - inc_session->start_recv_bytes, pop3_session->count - inc_session->start_num + 1); + g_print("pop: total_bytes = %lld cur_total_bytes = %lld\n", pop3_session->total_bytes, pop3_session->cur_total_bytes); + g_print("pop: count = %d cur_msg = %d\n", pop3_session->count, pop3_session->cur_msg); +#endif + inc_session->retr_count++; + gdk_threads_enter(); + inc_progress_dialog_update(inc_dialog, inc_session); + gdk_threads_leave(); + } else { + inc_session->retr_count++; + inc_recv_data_progressive(session, 0, 0, inc_session); + } + break; + case POP3_LOGOUT: + gdk_threads_enter(); + inc_progress_dialog_update(inc_dialog, inc_session); + inc_update_folderview(inc_dialog, inc_session); + gdk_threads_leave(); + break; + default: + break; + } + + return 0; +} + +/** + * inc_drop_message: + * @session: Current Pop3Session. + * @file: Received message file. + * + * Callback function to drop received message into local mailbox. + * + * Return value: DROP_OK if succeeded. DROP_ERROR if error occurred. + * DROP_DONT_RECEIVE if the message should be skipped. + * DROP_DELETE if the message should be deleted. + **/ +static gint inc_drop_message(Pop3Session *session, const gchar *file) +{ + FolderItem *inbox; + GSList *cur; + MsgInfo *msginfo; + FilterInfo *fltinfo; + IncSession *inc_session = (IncSession *)(SESSION(session)->data); + gint val; + gboolean is_junk = FALSE; + gboolean is_counted = FALSE; + IncProgressDialog *inc_dialog; + + g_return_val_if_fail(inc_session != NULL, DROP_ERROR); + + gdk_threads_enter(); + + inc_dialog = (IncProgressDialog *)inc_session->data; + + if (session->ac_prefs->inbox) { + inbox = folder_find_item_from_identifier + (session->ac_prefs->inbox); + if (!inbox) + inbox = folder_get_default_inbox(); + } else + inbox = folder_get_default_inbox(); + if (!inbox) { + gdk_threads_leave(); + return DROP_ERROR; + } + + fltinfo = filter_info_new(); + fltinfo->account = session->ac_prefs; + fltinfo->flags.perm_flags = MSG_NEW|MSG_UNREAD; + fltinfo->flags.tmp_flags = MSG_RECEIVED; + + msginfo = procheader_parse_file(file, fltinfo->flags, FALSE); + if (!msginfo) { + g_warning("inc_drop_message: procheader_parse_file failed"); + filter_info_free(fltinfo); + gdk_threads_leave(); + return DROP_ERROR; + } + fltinfo->flags = msginfo->flags; + msginfo->file_path = g_strdup(file); + + if (prefs_common.enable_junk && + prefs_common.filter_junk_on_recv && + prefs_common.filter_junk_before && + inc_session->junk_fltlist) { + filter_apply_msginfo(inc_session->junk_fltlist, msginfo, + fltinfo); + if (fltinfo->drop_done) + is_junk = TRUE; + else if (fltinfo->error == FLT_ERROR_EXEC_FAILED || + fltinfo->last_exec_exit_status >= 3) { + g_warning("inc_drop_message: junk filter command returned %d", + fltinfo->last_exec_exit_status); + alertpanel_error + (_("Execution of the junk filter command failed.\n" + "Please check the junk mail control setting.")); + procmsg_msginfo_free(msginfo); + filter_info_free(fltinfo); + inc_session->inc_state = INC_ERROR; + gdk_threads_leave(); + return DROP_ERROR; + } + } + + if (!fltinfo->drop_done && session->ac_prefs->filter_on_recv) + filter_apply_msginfo(prefs_common.fltlist, msginfo, fltinfo); + + if (!fltinfo->drop_done) { + if (prefs_common.enable_junk && + prefs_common.filter_junk_on_recv && + !prefs_common.filter_junk_before && + inc_session->junk_fltlist) { + filter_apply_msginfo(inc_session->junk_fltlist, + msginfo, fltinfo); + if (fltinfo->drop_done) + is_junk = TRUE; + else if (fltinfo->error == FLT_ERROR_EXEC_FAILED || + fltinfo->last_exec_exit_status >= 3) { + g_warning("inc_drop_message: junk filter command returned %d", + fltinfo->last_exec_exit_status); + alertpanel_error + (_("Execution of the junk filter command failed.\n" + "Please check the junk mail control setting.")); + procmsg_msginfo_free(msginfo); + filter_info_free(fltinfo); + inc_session->inc_state = INC_ERROR; + gdk_threads_leave(); + return DROP_ERROR; + } + } + } + + if (!fltinfo->drop_done) { + msginfo->flags = fltinfo->flags; + if (folder_item_add_msg_msginfo(inbox, msginfo, FALSE) < 0) { + procmsg_msginfo_free(msginfo); + filter_info_free(fltinfo); + gdk_threads_leave(); + return DROP_ERROR; + } + fltinfo->dest_list = g_slist_append(fltinfo->dest_list, inbox); + } + + for (cur = fltinfo->dest_list; cur != NULL; cur = cur->next) { + FolderItem *drop_folder = (FolderItem *)cur->data; + + val = GPOINTER_TO_INT(g_hash_table_lookup + (inc_session->folder_table, drop_folder)); + if (val == 0) + g_hash_table_insert(inc_session->folder_table, + drop_folder, GINT_TO_POINTER(1)); + g_hash_table_insert(inc_session->tmp_folder_table, drop_folder, + GINT_TO_POINTER(1)); + + if (drop_folder->stype != F_TRASH && + drop_folder->stype != F_JUNK) + is_counted = TRUE; + } + + if (fltinfo->actions[FLT_ACTION_NOT_RECEIVE] == TRUE) + val = DROP_DONT_RECEIVE; + else if (fltinfo->actions[FLT_ACTION_DELETE] == TRUE) + val = DROP_DELETE; + else { + val = DROP_OK; + if (!is_junk && is_counted && + fltinfo->actions[FLT_ACTION_MARK_READ] == FALSE) { + inc_session->new_msgs++; + + if (inc_dialog->result && + msginfo->subject && msginfo->fromname && + g_slist_length(inc_dialog->result->msg_summaries) < 5) { + IncMsgSummary *summary; + summary = g_new(IncMsgSummary, 1); + summary->subject = g_strdup(msginfo->subject); + summary->from = g_strdup(msginfo->fromname); + inc_dialog->result->msg_summaries = g_slist_append(inc_dialog->result->msg_summaries, summary); + } + } + } + + procmsg_msginfo_free(msginfo); + filter_info_free(fltinfo); + + gdk_threads_leave(); + return val; +} + +static void inc_put_error(IncSession *session, IncState istate, const gchar *pop3_msg) +{ + gchar *log_msg = NULL; + gchar *err_msg = NULL; + gboolean fatal_error = FALSE; + + switch (istate) { + case INC_LOOKUP_ERROR: + log_msg = _("Server not found."); + if (prefs_common.no_recv_err_panel) + break; + err_msg = g_strdup_printf + (_("Server %s not found."), session->session->server); + break; + case INC_CONNECT_ERROR: + log_msg = _("Connection failed."); + if (prefs_common.no_recv_err_panel) + break; + err_msg = g_strdup_printf + (_("Connection to %s:%d failed."), + session->session->server, session->session->port); + break; + case INC_ERROR: + log_msg = _("Error occurred while processing mail."); + if (prefs_common.no_recv_err_panel) + break; + if (pop3_msg) + err_msg = g_strdup_printf + (_("Error occurred while processing mail:\n%s"), + pop3_msg); + else + err_msg = g_strdup(log_msg); + break; + case INC_NO_SPACE: + log_msg = _("No disk space left."); + err_msg = g_strdup(log_msg); + fatal_error = TRUE; + break; + case INC_IO_ERROR: + log_msg = _("Can't write file."); + err_msg = g_strdup(log_msg); + fatal_error = TRUE; + break; + case INC_SOCKET_ERROR: + log_msg = _("Socket error."); + if (prefs_common.no_recv_err_panel) + break; + err_msg = g_strdup(log_msg); + break; + case INC_EOF: + log_msg = _("Connection closed by the remote host."); + if (prefs_common.no_recv_err_panel) + break; + err_msg = g_strdup(log_msg); + break; + case INC_LOCKED: + log_msg = _("Mailbox is locked."); + if (prefs_common.no_recv_err_panel) + break; + if (pop3_msg) + err_msg = g_strdup_printf(_("Mailbox is locked:\n%s"), + pop3_msg); + else + err_msg = g_strdup(log_msg); + break; + case INC_AUTH_FAILED: + log_msg = _("Authentication failed."); + if (prefs_common.no_recv_err_panel) + break; + if (pop3_msg) + err_msg = g_strdup_printf + (_("Authentication failed:\n%s"), pop3_msg); + else + err_msg = g_strdup(log_msg); + break; + case INC_TIMEOUT: + log_msg = _("Session timed out."); + if (prefs_common.no_recv_err_panel) + break; + err_msg = g_strdup(log_msg); + break; + default: + break; + } + + if (log_msg) { + if (fatal_error) + log_error("%s\n", log_msg); + else + log_warning("%s\n", log_msg); + } + if (err_msg) { + alertpanel_error("%s", err_msg); + g_free(err_msg); + } +} + +static void inc_cancel(IncProgressDialog *dialog, gboolean cancel_all) +{ + IncSession *session; + GList *list; + + g_return_if_fail(dialog != NULL); + + if (dialog->queue_list == NULL) { + inc_progress_dialog_destroy(dialog); + return; + } + + for (list = dialog->queue_list; list != NULL; list = list->next) { + session = list->data; + session->inc_state = INC_CANCEL; + session_disconnect(session->session); + if (!cancel_all) + break; + } + + log_message(_("Incorporation cancelled\n")); +} + +gboolean inc_is_active(void) +{ + GList *cur; + + if (inc_is_running) + return TRUE; + + if (inc_dialog_list == NULL) + return FALSE; + + for (cur = inc_dialog_list; cur != NULL; cur = cur->next) { + IncProgressDialog *dialog = cur->data; + if (dialog->queue_list) + return TRUE; + } + + return FALSE; +} + +void inc_block_notify(gboolean block) +{ + if (!block) + block_notify = FALSE; + else if (inc_is_active()) + block_notify = TRUE; +} + +void inc_cancel_all(void) +{ + GList *cur; + + for (cur = inc_dialog_list; cur != NULL; cur = cur->next) + inc_cancel((IncProgressDialog *)cur->data, TRUE); +} + +static void inc_cancel_cb(GtkWidget *widget, gpointer data) +{ + inc_cancel((IncProgressDialog *)data, FALSE); +} + +static void inc_cancel_all_cb(GtkWidget *widget, gpointer data) +{ + inc_cancel((IncProgressDialog *)data, TRUE); +} + +static gint inc_dialog_delete_cb(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + IncProgressDialog *dialog = (IncProgressDialog *)data; + + if (dialog->queue_list == NULL) + inc_progress_dialog_destroy(dialog); + else + inc_cancel(dialog, TRUE); + + return TRUE; +} + +static gint inc_spool(void) +{ + gchar *spool_path; + gchar *mbox; + gint msgs; + + spool_path = prefs_common.spool_path + ? prefs_common.spool_path : DEFAULT_SPOOL_PATH; + if (is_file_exist(spool_path)) + mbox = g_strdup(spool_path); + else if (is_dir_exist(spool_path)) + mbox = g_strconcat(spool_path, G_DIR_SEPARATOR_S, + g_get_user_name(), NULL); + else { + debug_print("%s: local mailbox not found.\n", spool_path); + return -1; + } + + msgs = get_spool(folder_get_default_inbox(), mbox); + g_free(mbox); + + return msgs; +} + +static gint get_spool(FolderItem *dest, const gchar *mbox) +{ + gint msgs, size; + gint lockfd; + LockType locktype = LOCK_FLOCK; + gchar tmp_mbox[MAXPATHLEN + 1]; + GHashTable *folder_table = NULL; + + g_return_val_if_fail(dest != NULL, -1); + g_return_val_if_fail(mbox != NULL, -1); + + if (!is_file_exist(mbox) || (size = get_file_size(mbox)) == 0) { + debug_print("%s: no messages in local mailbox.\n", mbox); + return 0; + } else if (size < 0) + return -1; + + if ((lockfd = lock_mbox(mbox, locktype)) < 0) { + locktype = LOCK_FILE; + if ((lockfd = lock_mbox(mbox, locktype)) < 0) + return -1; + } + + g_snprintf(tmp_mbox, sizeof(tmp_mbox), "%s%ctmpmbox.%p", + get_tmp_dir(), G_DIR_SEPARATOR, mbox); + + if (copy_mbox(mbox, tmp_mbox) < 0) { + unlock_mbox(mbox, lockfd, locktype); + return -1; + } + + debug_print(_("Getting new messages from %s into %s...\n"), + mbox, dest->path); + + folder_table = g_hash_table_new(NULL, NULL); + + msgs = proc_mbox_full(dest, tmp_mbox, folder_table, + prefs_common.filter_on_inc, + prefs_common.enable_junk && + prefs_common.filter_junk_on_recv); + + g_unlink(tmp_mbox); + if (msgs >= 0) empty_mbox(mbox); + unlock_mbox(mbox, lockfd, locktype); + + if (!prefs_common.scan_all_after_inc) { + inc_update_folder_foreach(folder_table); + } + + g_hash_table_destroy(folder_table); + + return msgs; +} + +void inc_lock(void) +{ + inc_lock_count++; +} + +void inc_unlock(void) +{ + if (inc_lock_count > 0) + inc_lock_count--; +} + +static guint autocheck_timer = 0; +static gpointer autocheck_data = NULL; + +void inc_autocheck_timer_init(MainWindow *mainwin) +{ + autocheck_data = mainwin; + inc_autocheck_timer_set(); +} + +static void inc_autocheck_timer_set_interval(guint interval) +{ + inc_autocheck_timer_remove(); + + if (prefs_common.autochk_newmail && autocheck_data) { + autocheck_timer = g_timeout_add_full + (G_PRIORITY_LOW, interval, inc_autocheck_func, + autocheck_data, NULL); + debug_print("added timer = %d\n", autocheck_timer); + } +} + +void inc_autocheck_timer_set(void) +{ + inc_autocheck_timer_set_interval(prefs_common.autochk_itv * 60000); +} + +void inc_autocheck_timer_remove(void) +{ + if (autocheck_timer) { + debug_print("removed timer = %d\n", autocheck_timer); + g_source_remove(autocheck_timer); + autocheck_timer = 0; + } +} + +static gint inc_autocheck_func(gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + gdk_threads_enter(); + + if (inc_lock_count) { + debug_print("autocheck is locked.\n"); + inc_autocheck_timer_set_interval(1000); + gdk_threads_leave(); + return FALSE; + } + + inc_all_account_mail(mainwin, TRUE); + + gdk_threads_leave(); + + return FALSE; +} diff --git a/src/inc.h b/src/inc.h new file mode 100644 index 0000000..98ca198 --- /dev/null +++ b/src/inc.h @@ -0,0 +1,115 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2012 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __INC_H__ +#define __INC_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "mainwindow.h" +#include "progressdialog.h" +#include "prefs_account.h" +#include "session.h" +#include "pop.h" + +typedef struct _IncResult IncResult; +typedef struct _IncProgressDialog IncProgressDialog; +typedef struct _IncSession IncSession; + +typedef enum +{ + INC_SUCCESS, + INC_CONNECT_ERROR, + INC_AUTH_FAILED, + INC_LOCKED, + INC_ERROR, + INC_NO_SPACE, + INC_IO_ERROR, + INC_SOCKET_ERROR, + INC_EOF, + INC_TIMEOUT, + INC_CANCEL, + INC_LOOKUP_ERROR +} IncState; + +struct _IncProgressDialog +{ + ProgressDialog *dialog; + + MainWindow *mainwin; + + gboolean show_dialog; + + GTimeVal progress_tv; + GTimeVal folder_tv; + + GList *queue_list; /* list of IncSession */ + gint cur_row; + + IncResult *result; +}; + +struct _IncSession +{ + Session *session; + IncState inc_state; + + GHashTable *folder_table; /* table of destination folders */ + GHashTable *tmp_folder_table; /* for progressive update */ + + GSList *junk_fltlist; + + gint64 cur_total_bytes; + gint new_msgs; + + gint start_num; + gint64 start_recv_bytes; + + gint retr_count; + + gpointer data; +}; + +#define TIMEOUT_ITV 200 + +void inc_mail (MainWindow *mainwin); +gint inc_account_mail (MainWindow *mainwin, + PrefsAccount *account); +void inc_all_account_mail (MainWindow *mainwin, + gboolean autocheck); + +gint inc_pop_before_smtp (PrefsAccount *account); + +gboolean inc_is_active (void); +void inc_block_notify (gboolean notify); + +void inc_cancel_all (void); + +void inc_lock (void); +void inc_unlock (void); + +void inc_autocheck_timer_init (MainWindow *mainwin); +void inc_autocheck_timer_set (void); +void inc_autocheck_timer_remove (void); + +#endif /* __INC_H__ */ diff --git a/src/inputdialog.c b/src/inputdialog.c new file mode 100644 index 0000000..a601085 --- /dev/null +++ b/src/inputdialog.c @@ -0,0 +1,389 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inputdialog.h" +#include "manage_window.h" +#include "inc.h" +#include "filesel.h" +#include "prefs_common.h" +#include "gtkutils.h" +#include "utils.h" + +#define DIALOG_WIDTH 420 + +typedef enum +{ + INPUT_DIALOG_NORMAL, + INPUT_DIALOG_INVISIBLE, + INPUT_DIALOG_COMBO, + INPUT_DIALOG_FILESEL +} InputDialogType; + +static gboolean ack; +static gboolean fin; + +static InputDialogType type; +static GtkFileChooserAction chooser_action; + +static GtkWidget *dialog; +static GtkWidget *msg_label; +static GtkWidget *entry; +static GtkWidget *combo; +static GtkWidget *confirm_area; +static GtkWidget *ok_button; + +static void input_dialog_create (InputDialogType dialog_type); + +static gchar *input_dialog_open (const gchar *title, + const gchar *message, + const gchar *default_string); +static void input_dialog_set (const gchar *title, + const gchar *message, + const gchar *default_string); + +static void ok_clicked (GtkWidget *widget, + gpointer data); +static void cancel_clicked (GtkWidget *widget, + gpointer data); +static gint delete_event (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); +static void entry_activated (GtkEditable *editable); +static void combo_activated (GtkEditable *editable); +static void sel_btn_clicked (GtkButton *button, + gpointer data); +static gint focus_out (GtkWidget *widget, + GdkEventFocus *event, + gpointer data); + + +gchar *input_dialog(const gchar *title, const gchar *message, + const gchar *default_string) +{ + if (dialog) + return NULL; + + input_dialog_create(INPUT_DIALOG_NORMAL); + + return input_dialog_open(title, message, default_string); +} + +gchar *input_dialog_with_invisible(const gchar *title, const gchar *message, + const gchar *default_string) +{ + if (dialog) + return NULL; + + input_dialog_create(INPUT_DIALOG_INVISIBLE); + + return input_dialog_open(title, message, default_string); +} + +gchar *input_dialog_combo(const gchar *title, const gchar *message, + const gchar *default_string, GList *list, + gboolean case_sensitive) +{ + if (dialog) + return NULL; + + input_dialog_create(INPUT_DIALOG_COMBO); + + if (!list) { + GList empty_list; + + empty_list.data = (gpointer)""; + empty_list.next = NULL; + empty_list.prev = NULL; + gtk_combo_set_popdown_strings(GTK_COMBO(combo), &empty_list); + } else + gtk_combo_set_popdown_strings(GTK_COMBO(combo), list); + + gtk_combo_set_case_sensitive(GTK_COMBO(combo), case_sensitive); + + return input_dialog_open(title, message, default_string); +} + +gchar *input_dialog_query_password(const gchar *server, const gchar *user) +{ + gchar *message; + gchar *pass; + + message = g_strdup_printf(_("Input password for %s on %s:"), + user, server); + pass = input_dialog_with_invisible(_("Input password"), message, NULL); + g_free(message); + + return pass; +} + +gchar *input_dialog_with_filesel(const gchar *title, const gchar *message, + const gchar *default_string, + GtkFileChooserAction action) +{ + if (dialog) + return NULL; + + input_dialog_create(INPUT_DIALOG_FILESEL); + chooser_action = action; + + return input_dialog_open(title, message, default_string); +} + +static void input_dialog_create(InputDialogType dialog_type) +{ + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *sel_btn; + GtkWidget *cancel_button; + + dialog = gtk_dialog_new(); + gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); + gtk_widget_set_size_request(dialog, DIALOG_WIDTH * gtkut_get_dpi_multiplier(), -1); + gtk_container_set_border_width + (GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), 5 * gtkut_get_dpi_multiplier()); + gtk_window_set_position(GTK_WINDOW(dialog), + GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + manage_window_set_transient(GTK_WINDOW(dialog)); + g_signal_connect(G_OBJECT(dialog), "delete_event", + G_CALLBACK(delete_event), NULL); + g_signal_connect(G_OBJECT(dialog), "key_press_event", + G_CALLBACK(key_pressed), NULL); + g_signal_connect(G_OBJECT(dialog), "focus_out_event", + G_CALLBACK(focus_out), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(dialog); + + vbox = gtk_vbox_new(FALSE, 8 * gtkut_get_dpi_multiplier()); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), vbox); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 8); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + msg_label = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(hbox), msg_label, FALSE, FALSE, 0); + gtk_label_set_justify(GTK_LABEL(msg_label), GTK_JUSTIFY_LEFT); + + hbox = gtk_hbox_new(FALSE, 4 * gtkut_get_dpi_multiplier()); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + type = dialog_type; + + if (dialog_type == INPUT_DIALOG_COMBO) { + combo = gtk_combo_new(); + gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0); + g_signal_connect(G_OBJECT(GTK_COMBO(combo)->entry), "activate", + G_CALLBACK(combo_activated), NULL); + } else { + entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); + g_signal_connect(G_OBJECT(entry), "activate", + G_CALLBACK(entry_activated), NULL); + if (dialog_type == INPUT_DIALOG_FILESEL) { + sel_btn = gtk_button_new_with_label("..."); + gtk_box_pack_start(GTK_BOX(hbox), sel_btn, + FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(sel_btn), "clicked", + G_CALLBACK(sel_btn_clicked), NULL); + } + if (dialog_type == INPUT_DIALOG_INVISIBLE) + gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); + } + + gtkut_stock_button_set_create(&confirm_area, + &ok_button, GTK_STOCK_OK, + &cancel_button, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), + confirm_area); + gtk_widget_grab_default(ok_button); + + g_signal_connect(G_OBJECT(ok_button), "clicked", + G_CALLBACK(ok_clicked), NULL); + g_signal_connect(G_OBJECT(cancel_button), "clicked", + G_CALLBACK(cancel_clicked), NULL); + + gtk_widget_show_all(GTK_DIALOG(dialog)->vbox); +} + +static gchar *input_dialog_open(const gchar *title, const gchar *message, + const gchar *default_string) +{ + gchar *str; + + gtkut_box_set_reverse_order(GTK_BOX(confirm_area), + !prefs_common.comply_gnome_hig); + input_dialog_set(title, message, default_string); + gtk_widget_show(dialog); + + ack = fin = FALSE; + + inc_lock(); + + while (fin == FALSE) + gtk_main_iteration(); + + manage_window_focus_out(dialog, NULL, NULL); + + if (ack) { + GtkEditable *editable; + + if (type == INPUT_DIALOG_COMBO) + editable = GTK_EDITABLE(GTK_COMBO(combo)->entry); + else + editable = GTK_EDITABLE(entry); + + str = gtk_editable_get_chars(editable, 0, -1); + if (str && *str == '\0') { + g_free(str); + str = NULL; + } + } else + str = NULL; + + gtk_widget_destroy(dialog); + dialog = msg_label = entry = combo = confirm_area = ok_button = NULL; + + GTK_EVENTS_FLUSH(); + + inc_unlock(); + + if (type != INPUT_DIALOG_INVISIBLE) + debug_print("return string = %s\n", str ? str : "(none)"); + + return str; +} + +static void input_dialog_set(const gchar *title, const gchar *message, + const gchar *default_string) +{ + GtkWidget *entry_; + + if (type == INPUT_DIALOG_COMBO) + entry_ = GTK_COMBO(combo)->entry; + else + entry_ = entry; + + gtk_window_set_title(GTK_WINDOW(dialog), title); + gtk_label_set_text(GTK_LABEL(msg_label), message); + if (default_string && *default_string) { + gtk_entry_set_text(GTK_ENTRY(entry_), default_string); + gtk_entry_set_position(GTK_ENTRY(entry_), 0); + gtk_entry_select_region(GTK_ENTRY(entry_), 0, -1); + } else + gtk_entry_set_text(GTK_ENTRY(entry_), ""); + + gtk_widget_grab_focus(ok_button); + gtk_widget_grab_focus(entry_); +} + +static void ok_clicked(GtkWidget *widget, gpointer data) +{ + ack = TRUE; + fin = TRUE; +} + +static void cancel_clicked(GtkWidget *widget, gpointer data) +{ + ack = FALSE; + fin = TRUE; +} + +static gint delete_event(GtkWidget *widget, GdkEventAny *event, gpointer data) +{ + ack = FALSE; + fin = TRUE; + + return TRUE; +} + +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + if (event && event->keyval == GDK_Escape) { + ack = FALSE; + fin = TRUE; + } + + return FALSE; +} + +static void entry_activated(GtkEditable *editable) +{ + ack = TRUE; + fin = TRUE; +} + +static void combo_activated(GtkEditable *editable) +{ + ack = TRUE; + fin = TRUE; +} + +static void sel_btn_clicked(GtkButton *button, gpointer data) +{ + gchar *file; + gchar *utf8_file; + + g_signal_handlers_block_by_func(dialog, focus_out, NULL); + + if (chooser_action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + file = filesel_select_dir(NULL); + else + file = filesel_select_file(_("Select file"), NULL, + chooser_action); + if (file) { + utf8_file = conv_filename_to_utf8(file); + gtk_entry_set_text(GTK_ENTRY(entry), utf8_file); + g_free(utf8_file); + } + + g_signal_handlers_unblock_by_func(dialog, focus_out, NULL); +} + +static gint focus_out(GtkWidget *widget, GdkEventFocus *event, gpointer data) +{ +#ifdef G_OS_WIN32 + gtk_window_present(GTK_WINDOW(widget)); +#endif + return FALSE; +} diff --git a/src/inputdialog.h b/src/inputdialog.h new file mode 100644 index 0000000..02bdda3 --- /dev/null +++ b/src/inputdialog.h @@ -0,0 +1,45 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2009 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __INPUTDIALOG_H__ +#define __INPUTDIALOG_H__ + +#include +#include + +gchar *input_dialog (const gchar *title, + const gchar *message, + const gchar *default_string); +gchar *input_dialog_with_invisible (const gchar *title, + const gchar *message, + const gchar *default_string); +gchar *input_dialog_combo (const gchar *title, + const gchar *message, + const gchar *default_string, + GList *list, + gboolean case_sensitive); +gchar *input_dialog_query_password (const gchar *server, + const gchar *user); + +gchar *input_dialog_with_filesel (const gchar *title, + const gchar *message, + const gchar *default_string, + GtkFileChooserAction action); + +#endif /* __INPUTDIALOG_H__ */ diff --git a/src/jpilot.c b/src/jpilot.c new file mode 100644 index 0000000..9df9916 --- /dev/null +++ b/src/jpilot.c @@ -0,0 +1,1739 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Functions necessary to access JPilot database files. + * JPilot is Copyright(c) by Judd Montgomery. + * Visit http://www.jpilot.org for more details. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef USE_JPILOT + +#include +#include +#include +#include +#include +#include +/* #include */ +#include + +#ifdef HAVE_LIBPISOCK_PI_ARGS_H +# include +# include +# include +# include +#else +# include +# include +# include +# include +#endif + +#include "mgutils.h" +#include "addritem.h" +#include "addrcache.h" +#include "jpilot.h" +#include "codeconv.h" +#include "utils.h" + +#define JPILOT_DBHOME_DIR ".jpilot" +#define JPILOT_DBHOME_FILE "AddressDB.pdb" +#define PILOT_LINK_LIB_NAME "libpisock.so" + +#define IND_LABEL_LASTNAME 0 /* Index of last name in address data */ +#define IND_LABEL_FIRSTNAME 1 /* Index of first name in address data */ +#define IND_PHONE_EMAIL 4 /* Index of E-Mail address in phone labels */ +#define OFFSET_PHONE_LABEL 3 /* Offset to phone data in address data */ +#define IND_CUSTOM_LABEL 14 /* Offset to custom label names */ +#define NUM_CUSTOM_LABEL 4 /* Number of custom labels */ + +/* Shamelessly copied from JPilot (libplugin.h) */ +typedef struct { + unsigned char db_name[32]; + unsigned char flags[2]; + unsigned char version[2]; + unsigned char creation_time[4]; + unsigned char modification_time[4]; + unsigned char backup_time[4]; + unsigned char modification_number[4]; + unsigned char app_info_offset[4]; + unsigned char sort_info_offset[4]; + unsigned char type[4];/*Database ID */ + unsigned char creator_id[4];/*Application ID */ + unsigned char unique_id_seed[4]; + unsigned char next_record_list_id[4]; + unsigned char number_of_records[2]; +} RawDBHeader; + +/* Shamelessly copied from JPilot (libplugin.h) */ +typedef struct { + char db_name[32]; + unsigned int flags; + unsigned int version; + time_t creation_time; + time_t modification_time; + time_t backup_time; + unsigned int modification_number; + unsigned int app_info_offset; + unsigned int sort_info_offset; + char type[5];/*Database ID */ + char creator_id[5];/*Application ID */ + char unique_id_seed[5]; + unsigned int next_record_list_id; + unsigned int number_of_records; +} DBHeader; + +/* Shamelessly copied from JPilot (libplugin.h) */ +typedef struct { + unsigned char Offset[4]; /*4 bytes offset from BOF to record */ + unsigned char attrib; + unsigned char unique_ID[3]; +} record_header; + +/* Shamelessly copied from JPilot (libplugin.h) */ +typedef struct mem_rec_header_s { + unsigned int rec_num; + unsigned int offset; + unsigned int unique_id; + unsigned char attrib; + struct mem_rec_header_s *next; +} mem_rec_header; + +/* Shamelessly copied from JPilot (libplugin.h) */ +#define SPENT_PC_RECORD_BIT 256 + +typedef enum { + PALM_REC = 100L, + MODIFIED_PALM_REC = 101L, + DELETED_PALM_REC = 102L, + NEW_PC_REC = 103L, + DELETED_PC_REC = SPENT_PC_RECORD_BIT + 104L, + DELETED_DELETED_PALM_REC = SPENT_PC_RECORD_BIT + 105L +} PCRecType; + +/* Shamelessly copied from JPilot (libplugin.h) */ +typedef struct { + PCRecType rt; + unsigned int unique_id; + unsigned char attrib; + void *buf; + int size; +} buf_rec; + +/* Shamelessly copied from JPilot (libplugin.h) */ +typedef struct { + unsigned long header_len; + unsigned long header_version; + unsigned long rec_len; + unsigned long unique_id; + unsigned long rt; /* Record Type */ + unsigned char attrib; +} PC3RecordHeader; + +enum { + FAMILY_LAST = 0, + FAMILY_FIRST = 1 +} name_order; + +gboolean convert_charcode; + +static gchar *jpilot_convert_encoding(const gchar *str) +{ + if (convert_charcode) + return conv_codeset_strdup(str, CS_SHIFT_JIS, CS_INTERNAL); + + return g_strdup(str); +} + +/* +* Create new pilot file object. +*/ +JPilotFile *jpilot_create() { + JPilotFile *pilotFile; + pilotFile = g_new0( JPilotFile, 1 ); + pilotFile->name = NULL; + pilotFile->file = NULL; + pilotFile->path = NULL; + pilotFile->addressCache = addrcache_create(); + pilotFile->readMetadata = FALSE; + pilotFile->customLabels = NULL; + pilotFile->labelInd = NULL; + pilotFile->retVal = MGU_SUCCESS; + pilotFile->accessFlag = FALSE; + pilotFile->havePC3 = FALSE; + pilotFile->pc3ModifyTime = 0; + return pilotFile; +} + +/* +* Create new pilot file object for specified file. +*/ +JPilotFile *jpilot_create_path( const gchar *path ) { + JPilotFile *pilotFile; + pilotFile = jpilot_create(); + jpilot_set_file( pilotFile, path ); + return pilotFile; +} + +/* +* Properties... +*/ +void jpilot_set_name( JPilotFile* pilotFile, const gchar *value ) { + g_return_if_fail( pilotFile != NULL ); + pilotFile->name = mgu_replace_string( pilotFile->name, value ); +} +void jpilot_set_file( JPilotFile* pilotFile, const gchar *value ) { + g_return_if_fail( pilotFile != NULL ); + addrcache_refresh( pilotFile->addressCache ); + pilotFile->readMetadata = FALSE; + pilotFile->path = mgu_replace_string( pilotFile->path, value ); +} +void jpilot_set_accessed( JPilotFile *pilotFile, const gboolean value ) { + g_return_if_fail( pilotFile != NULL ); + pilotFile->accessFlag = value; +} + +gint jpilot_get_status( JPilotFile *pilotFile ) { + g_return_val_if_fail( pilotFile != NULL, -1 ); + return pilotFile->retVal; +} +ItemFolder *jpilot_get_root_folder( JPilotFile *pilotFile ) { + g_return_val_if_fail( pilotFile != NULL, NULL ); + return addrcache_get_root_folder( pilotFile->addressCache ); +} +gchar *jpilot_get_name( JPilotFile *pilotFile ) { + g_return_val_if_fail( pilotFile != NULL, NULL ); + return pilotFile->name; +} + +/* +* Test whether file was read. +* Return: TRUE if file was read. +*/ +gboolean jpilot_get_read_flag( JPilotFile *pilotFile ) { + g_return_val_if_fail( pilotFile != NULL, FALSE ); + return pilotFile->addressCache->dataRead; +} + +/* +* Free up custom label list. +*/ +void jpilot_clear_custom_labels( JPilotFile *pilotFile ) { + GList *node; + + g_return_if_fail( pilotFile != NULL ); + + /* Release custom labels */ + mgu_free_dlist( pilotFile->customLabels ); + pilotFile->customLabels = NULL; + + /* Release indexes */ + node = pilotFile->labelInd; + while( node ) { + node->data = NULL; + node = g_list_next( node ); + } + g_list_free( pilotFile->labelInd ); + pilotFile->labelInd = NULL; + + /* Force a fresh read */ + addrcache_refresh( pilotFile->addressCache ); +} + +/* +* Append a custom label, representing an E-Mail address field to the +* custom label list. +*/ +void jpilot_add_custom_label( JPilotFile *pilotFile, const gchar *labelName ) { + g_return_if_fail( pilotFile != NULL ); + + if( labelName ) { + gchar *labelCopy = g_strdup( labelName ); + g_strstrip( labelCopy ); + if( *labelCopy == '\0' ) { + g_free( labelCopy ); + } + else { + pilotFile->customLabels = g_list_append( pilotFile->customLabels, labelCopy ); + /* Force a fresh read */ + addrcache_refresh( pilotFile->addressCache ); + } + } +} + +/* +* Get list of custom labels. +* Return: List of labels. Must use g_free() when done. +*/ +GList *jpilot_get_custom_labels( JPilotFile *pilotFile ) { + GList *retVal = NULL; + GList *node; + + g_return_val_if_fail( pilotFile != NULL, NULL ); + + node = pilotFile->customLabels; + while( node ) { + retVal = g_list_append( retVal, g_strdup( node->data ) ); + node = g_list_next( node ); + } + return retVal; +} + +/* +* Return filespec of PC3 file corresponding to JPilot PDB file. +* Note: Filespec should be g_free() when done. +*/ +static gchar *jpilot_get_pc3_file( JPilotFile *pilotFile ) { + gchar *fileSpec, *r; + gint i, len, pos; + + if( pilotFile == NULL ) return NULL; + if( pilotFile->path == NULL ) return NULL; + + fileSpec = g_strdup( pilotFile->path ); + len = strlen( fileSpec ); + pos = -1; + r = NULL; + for( i = len; i > 0; i-- ) { + if( *(fileSpec + i) == '.' ) { + pos = i + 1; + r = fileSpec + pos; + break; + } + } + if( r ) { + if( len - pos == 3 ) { + *r++ = 'p'; *r++ = 'c'; *r = '3'; + return fileSpec; + } + } + g_free( fileSpec ); + return NULL; +} + +/* +* Save PC3 file time to cache. +* return: TRUE if time marked. +*/ +static gboolean jpilot_mark_files( JPilotFile *pilotFile ) { + gboolean retVal = FALSE; + GStatBuf filestat; + gchar *pcFile; + + /* Mark PDB file cache */ + retVal = addrcache_mark_file( pilotFile->addressCache, pilotFile->path ); + + /* Now mark PC3 file */ + pilotFile->havePC3 = FALSE; + pilotFile->pc3ModifyTime = 0; + pcFile = jpilot_get_pc3_file( pilotFile ); + if( pcFile == NULL ) return retVal; + if( 0 == g_lstat( pcFile, &filestat ) ) { + pilotFile->havePC3 = TRUE; + pilotFile->pc3ModifyTime = filestat.st_mtime; + retVal = TRUE; + } + g_free( pcFile ); + return retVal; +} + +/* +* Check whether JPilot PDB or PC3 file has changed by comparing +* with cached data. +* return: TRUE if file has changed. +*/ +static gboolean jpilot_check_files( JPilotFile *pilotFile ) { + gboolean retVal = TRUE; + GStatBuf filestat; + gchar *pcFile; + + /* Check main file */ + if( addrcache_check_file( pilotFile->addressCache, pilotFile->path ) ) + return TRUE; + + /* Test PC3 file */ + if( ! pilotFile->havePC3 ) return FALSE; + pcFile = jpilot_get_pc3_file( pilotFile ); + if( pcFile == NULL ) return FALSE; + + if( 0 == g_lstat( pcFile, &filestat ) ) { + if( filestat.st_mtime == pilotFile->pc3ModifyTime ) retVal = FALSE; + } + g_free( pcFile ); + return retVal; +} + +/* +* Test whether file was modified since last access. +* Return: TRUE if file was modified. +*/ +gboolean jpilot_get_modified( JPilotFile *pilotFile ) { + g_return_val_if_fail( pilotFile != NULL, FALSE ); + return jpilot_check_files( pilotFile ); +} +gboolean jpilot_get_accessed( JPilotFile *pilotFile ) { + g_return_val_if_fail( pilotFile != NULL, FALSE ); + return pilotFile->accessFlag; +} + +/* +* Free up pilot file object by releasing internal memory. +*/ +void jpilot_free( JPilotFile *pilotFile ) { + g_return_if_fail( pilotFile != NULL ); + + /* Free internal stuff */ + g_free( pilotFile->path ); + + /* Release custom labels */ + jpilot_clear_custom_labels( pilotFile ); + + /* Clear cache */ + addrcache_clear( pilotFile->addressCache ); + addrcache_free( pilotFile->addressCache ); + pilotFile->addressCache = NULL; + pilotFile->readMetadata = FALSE; + pilotFile->accessFlag = FALSE; + pilotFile->havePC3 = FALSE; + pilotFile->pc3ModifyTime = 0; + + /* Now release file object */ + g_free( pilotFile ); +} + +/* +* Refresh internal variables to force a file read. +*/ +void jpilot_force_refresh( JPilotFile *pilotFile ) { + addrcache_refresh( pilotFile->addressCache ); +} + +/* +* Print object to specified stream. +*/ +void jpilot_print_file( JPilotFile *pilotFile, FILE *stream ) { + GList *node; + + g_return_if_fail( pilotFile != NULL ); + + fprintf( stream, "JPilotFile:\n" ); + fprintf( stream, "file spec: '%s'\n", pilotFile->path ); + fprintf( stream, " metadata: %s\n", pilotFile->readMetadata ? "yes" : "no" ); + fprintf( stream, " ret val: %d\n", pilotFile->retVal ); + + node = pilotFile->customLabels; + while( node ) { + fprintf( stream, " c label: %s\n", (gchar *)node->data ); + node = g_list_next( node ); + } + + node = pilotFile->labelInd; + while( node ) { + fprintf( stream, " labelind: %d\n", GPOINTER_TO_INT(node->data) ); + node = g_list_next( node ); + } + + addrcache_print( pilotFile->addressCache, stream ); + fprintf( stream, " ret val: %d\n", pilotFile->retVal ); + fprintf( stream, " have pc3: %s\n", pilotFile->havePC3 ? "yes" : "no" ); + fprintf( stream, " pc3 time: %lu\n", pilotFile->pc3ModifyTime ); + addritem_print_item_folder( pilotFile->addressCache->rootFolder, stream ); +} + +/* +* Print summary of object to specified stream. +*/ +void jpilot_print_short( JPilotFile *pilotFile, FILE *stream ) { + GList *node; + g_return_if_fail( pilotFile != NULL ); + fprintf( stream, "JPilotFile:\n" ); + fprintf( stream, "file spec: '%s'\n", pilotFile->path ); + fprintf( stream, " metadata: %s\n", pilotFile->readMetadata ? "yes" : "no" ); + fprintf( stream, " ret val: %d\n", pilotFile->retVal ); + + node = pilotFile->customLabels; + while( node ) { + fprintf( stream, " c label: %s\n", (gchar *)node->data ); + node = g_list_next( node ); + } + + node = pilotFile->labelInd; + while( node ) { + fprintf( stream, " labelind: %d\n", GPOINTER_TO_INT(node->data) ); + node = g_list_next( node ); + } + addrcache_print( pilotFile->addressCache, stream ); + fprintf( stream, " have pc3: %s\n", pilotFile->havePC3 ? "yes" : "no" ); + fprintf( stream, " pc3 time: %lu\n", pilotFile->pc3ModifyTime ); +} + +/* Shamelessly copied from JPilot (libplugin.c) */ +static unsigned int bytes_to_bin(unsigned char *bytes, unsigned int num_bytes) { +unsigned int i, n; + n=0; + for (i=0;idb_name, (gchar *)rdbh->db_name, 31); + dbh->db_name[31] = '\0'; + dbh->flags = bytes_to_bin(rdbh->flags, 2); + dbh->version = bytes_to_bin(rdbh->version, 2); + temp = bytes_to_bin(rdbh->creation_time, 4); + dbh->creation_time = pilot_time_to_unix_time(temp); + temp = bytes_to_bin(rdbh->modification_time, 4); + dbh->modification_time = pilot_time_to_unix_time(temp); + temp = bytes_to_bin(rdbh->backup_time, 4); + dbh->backup_time = pilot_time_to_unix_time(temp); + dbh->modification_number = bytes_to_bin(rdbh->modification_number, 4); + dbh->app_info_offset = bytes_to_bin(rdbh->app_info_offset, 4); + dbh->sort_info_offset = bytes_to_bin(rdbh->sort_info_offset, 4); + strncpy(dbh->type, (gchar *)rdbh->type, 4); + dbh->type[4] = '\0'; + strncpy(dbh->creator_id, (gchar *)rdbh->creator_id, 4); + dbh->creator_id[4] = '\0'; + strncpy(dbh->unique_id_seed, (gchar *)rdbh->unique_id_seed, 4); + dbh->unique_id_seed[4] = '\0'; + dbh->next_record_list_id = bytes_to_bin(rdbh->next_record_list_id, 4); + dbh->number_of_records = bytes_to_bin(rdbh->number_of_records, 2); + return 0; +} + +/* Shamelessly copied from JPilot (libplugin.c) */ +/* returns 1 if found */ +/* 0 if eof */ +static int find_next_offset( mem_rec_header *mem_rh, long fpos, + unsigned int *next_offset, unsigned char *attrib, unsigned int *unique_id ) +{ + mem_rec_header *temp_mem_rh; + unsigned char found = 0; + unsigned long found_at; + + found_at=0xFFFFFF; + for (temp_mem_rh=mem_rh; temp_mem_rh; temp_mem_rh = temp_mem_rh->next) { + if ((temp_mem_rh->offset > fpos) && (temp_mem_rh->offset < found_at)) { + found_at = temp_mem_rh->offset; + /* *attrib = temp_mem_rh->attrib; */ + /* *unique_id = temp_mem_rh->unique_id; */ + } + if ((temp_mem_rh->offset == fpos)) { + found = 1; + *attrib = temp_mem_rh->attrib; + *unique_id = temp_mem_rh->unique_id; + } + } + *next_offset = found_at; + return found; +} + +/* Shamelessly copied from JPilot (libplugin.c) */ +static void free_mem_rec_header(mem_rec_header **mem_rh) { + mem_rec_header *h, *next_h; + for (h=*mem_rh; h; h=next_h) { + next_h=h->next; + free(h); + } + *mem_rh = NULL; +} + +#if 0 +/* Shamelessly copied from JPilot (libplugin.c) */ +static int jpilot_free_db_list( GList **br_list ) { + GList *temp_list, *first; + buf_rec *br; + + /* Go to first entry in the list */ + first=NULL; + for( temp_list = *br_list; temp_list; temp_list = temp_list->prev ) { + first = temp_list; + } + for (temp_list = first; temp_list; temp_list = temp_list->next) { + if (temp_list->data) { + br=temp_list->data; + if (br->buf) { + free(br->buf); + temp_list->data=NULL; + } + free(br); + } + } + g_list_free(*br_list); + *br_list=NULL; + return 0; +} +#endif + +/* Shamelessly copied from JPilot (libplugin.c) */ +/* Read file size */ +static int jpilot_get_info_size( FILE *in, unsigned int *size ) { + RawDBHeader rdbh; + DBHeader dbh; + unsigned int offset; + record_header rh; + + fseek(in, 0, SEEK_SET); + fread(&rdbh, sizeof(RawDBHeader), 1, in); + if (feof(in)) { + return MGU_EOF; + } + + raw_header_to_header(&rdbh, &dbh); + if (dbh.app_info_offset==0) { + *size=0; + return MGU_SUCCESS; + } + if (dbh.sort_info_offset!=0) { + *size = dbh.sort_info_offset - dbh.app_info_offset; + return MGU_SUCCESS; + } + if (dbh.number_of_records==0) { + fseek(in, 0, SEEK_END); + *size=ftell(in) - dbh.app_info_offset; + return MGU_SUCCESS; + } + + fread(&rh, sizeof(record_header), 1, in); + offset = ((rh.Offset[0]*256+rh.Offset[1])*256+rh.Offset[2])*256+rh.Offset[3]; + *size=offset - dbh.app_info_offset; + + return MGU_SUCCESS; +} + +/* + * Read address file into address list. Based on JPilot's + * libplugin.c (jp_get_app_info) + */ +static gint jpilot_get_file_info( JPilotFile *pilotFile, unsigned char **buf, unsigned int *buf_size ) { + FILE *in; + int num; + unsigned int rec_size; + RawDBHeader rdbh; + DBHeader dbh; + + if( ( !buf_size ) || ( ! buf ) ) { + return MGU_BAD_ARGS; + } + + *buf = NULL; + *buf_size=0; + + if( pilotFile->path ) { + in = g_fopen( pilotFile->path, "rb" ); + if( !in ) { + return MGU_OPEN_FILE; + } + } + else { + return MGU_NO_FILE; + } + + num = fread( &rdbh, sizeof( RawDBHeader ), 1, in ); + if( num != 1 ) { + if( ferror(in) ) { + fclose(in); + return MGU_ERROR_READ; + } + } + if (feof(in)) { + fclose(in); + return MGU_EOF; + } + + /* Convert header into something recognizable */ + raw_header_to_header(&rdbh, &dbh); + + num = jpilot_get_info_size(in, &rec_size); + if (num) { + fclose(in); + return MGU_ERROR_READ; + } + + fseek(in, dbh.app_info_offset, SEEK_SET); + *buf = ( unsigned char * ) malloc(rec_size); + if (!(*buf)) { + fclose(in); + return MGU_OO_MEMORY; + } + num = fread(*buf, rec_size, 1, in); + if (num != 1) { + if (ferror(in)) { + fclose(in); + free(*buf); + return MGU_ERROR_READ; + } + } + fclose(in); + + *buf_size = rec_size; + + return MGU_SUCCESS; +} + +/* Shamelessly copied from JPilot (libplugin.c) */ +static int unpack_header(PC3RecordHeader *header, unsigned char *packed_header) { + unsigned char *p; + unsigned long l; + + p = packed_header; + + memcpy(&l, p, sizeof(l)); + header->header_len=ntohl(l); + p+=sizeof(l); + + memcpy(&l, p, sizeof(l)); + header->header_version=ntohl(l); + p+=sizeof(l); + + memcpy(&l, p, sizeof(l)); + header->rec_len=ntohl(l); + p+=sizeof(l); + + memcpy(&l, p, sizeof(l)); + header->unique_id=ntohl(l); + p+=sizeof(l); + + memcpy(&l, p, sizeof(l)); + header->rt=ntohl(l); + p+=sizeof(l); + + memcpy(&(header->attrib), p, sizeof(unsigned char)); + p+=sizeof(unsigned char); + + return 0; +} + +/* Shamelessly copied from JPilot (libplugin.c) */ +static int read_header(FILE *pc_in, PC3RecordHeader *header) { + unsigned long l, len; + unsigned char packed_header[256]; + int num; + + num = fread(&l, sizeof(l), 1, pc_in); + if (feof(pc_in)) { + return -1; + } + if (num!=1) { + return num; + } + memcpy(packed_header, &l, sizeof(l)); + len=ntohl(l); + if (len > 255) { + return -1; + } + num = fread(packed_header+sizeof(l), len-sizeof(l), 1, pc_in); + if (feof(pc_in)) { + return -1; + } + if (num!=1) { + return num; + } + unpack_header(header, packed_header); + return 1; +} + +/* Read next record from PC3 file. Based on JPilot's + * pc_read_next_rec (libplugin.c) */ +static gint jpilot_read_next_pc( FILE *in, buf_rec *br ) { + PC3RecordHeader header; + int rec_len, num; + char *record; + + if(feof(in)) { + return MGU_EOF; + } + num = read_header(in, &header); + if (num < 1) { + if (ferror(in)) { + return MGU_ERROR_READ; + } + if (feof(in)) { + return MGU_EOF; + } + } + rec_len = header.rec_len; + record = malloc(rec_len); + if (!record) { + return MGU_OO_MEMORY; + } + num = fread(record, rec_len, 1, in); + if (num != 1) { + if (ferror(in)) { + free(record); + return MGU_ERROR_READ; + } + } + br->rt = header.rt; + br->unique_id = header.unique_id; + br->attrib = header.attrib; + br->buf = record; + br->size = rec_len; + + return MGU_SUCCESS; +} + +/* + * Read address file into a linked list. Based on JPilot's + * jp_read_DB_files (from libplugin.c) + */ +static gint jpilot_read_db_files( JPilotFile *pilotFile, GList **records ) { + FILE *in, *pc_in; + char *buf; + GList *temp_list; + int num_records, recs_returned, i, num, r; + unsigned int offset, prev_offset, next_offset, rec_size; + int out_of_order; + long fpos; /*file position indicator */ + unsigned char attrib; + unsigned int unique_id; + mem_rec_header *mem_rh, *temp_mem_rh, *last_mem_rh; + record_header rh; + RawDBHeader rdbh; + DBHeader dbh; + buf_rec *temp_br; + gchar *pcFile; + + mem_rh = last_mem_rh = NULL; + *records = NULL; + recs_returned = 0; + + if( pilotFile->path == NULL ) { + return MGU_BAD_ARGS; + } + + in = g_fopen( pilotFile->path, "rb" ); + if (!in) { + return MGU_OPEN_FILE; + } + + /* Read the database header */ + num = fread(&rdbh, sizeof(RawDBHeader), 1, in); + if (num != 1) { + if (ferror(in)) { + fclose(in); + return MGU_ERROR_READ; + } + if (feof(in)) { + fclose(in); + return MGU_EOF; + } + } + raw_header_to_header(&rdbh, &dbh); + + /* Read each record entry header */ + num_records = dbh.number_of_records; + out_of_order = 0; + prev_offset = 0; + + for (i = 1; i < num_records + 1; i++) { + num = fread(&rh, sizeof(record_header), 1, in); + if (num != 1) { + if (ferror(in)) { + break; + } + if (feof(in)) { + free_mem_rec_header(&mem_rh); + fclose(in); + return MGU_EOF; + } + } + + offset = ((rh.Offset[0]*256+rh.Offset[1])*256+rh.Offset[2])*256+rh.Offset[3]; + if (offset < prev_offset) { + out_of_order = 1; + } + prev_offset = offset; + temp_mem_rh = (mem_rec_header *)malloc(sizeof(mem_rec_header)); + if (!temp_mem_rh) { + break; + } + temp_mem_rh->next = NULL; + temp_mem_rh->rec_num = i; + temp_mem_rh->offset = offset; + temp_mem_rh->attrib = rh.attrib; + temp_mem_rh->unique_id = (rh.unique_ID[0]*256+rh.unique_ID[1])*256+rh.unique_ID[2]; + if (mem_rh == NULL) { + mem_rh = temp_mem_rh; + last_mem_rh = temp_mem_rh; + } else { + last_mem_rh->next = temp_mem_rh; + last_mem_rh = temp_mem_rh; + } + } + + temp_mem_rh = mem_rh; + + if (num_records) { + attrib = unique_id = 0; + if (out_of_order) { + find_next_offset(mem_rh, 0, &next_offset, &attrib, &unique_id); + } else { + next_offset = 0xFFFFFF; + if (mem_rh) { + next_offset = mem_rh->offset; + attrib = mem_rh->attrib; + unique_id = mem_rh->unique_id; + } + } + fseek(in, next_offset, SEEK_SET); + while(!feof(in)) { + fpos = ftell(in); + if (out_of_order) { + find_next_offset(mem_rh, fpos, &next_offset, &attrib, &unique_id); + } else { + next_offset = 0xFFFFFF; + if (temp_mem_rh) { + attrib = temp_mem_rh->attrib; + unique_id = temp_mem_rh->unique_id; + if (temp_mem_rh->next) { + temp_mem_rh = temp_mem_rh->next; + next_offset = temp_mem_rh->offset; + } + } + } + rec_size = next_offset - fpos; + buf = malloc(rec_size); + if (!buf) break; + num = fread(buf, rec_size, 1, in); + if ((num != 1)) { + if (ferror(in)) { + free(buf); + break; + } + } + + temp_br = malloc(sizeof(buf_rec)); + if (!temp_br) { + free(buf); + break; + } + temp_br->rt = PALM_REC; + temp_br->unique_id = unique_id; + temp_br->attrib = attrib; + temp_br->buf = buf; + temp_br->size = rec_size; + + *records = g_list_append(*records, temp_br); + + recs_returned++; + } + } + fclose(in); + free_mem_rec_header(&mem_rh); + + /* Read the PC3 file, if present */ + pcFile = jpilot_get_pc3_file( pilotFile ); + if( pcFile == NULL ) return MGU_SUCCESS; + pc_in = g_fopen( pcFile, "rb"); + g_free( pcFile ); + + if( pc_in == NULL ) { + return MGU_SUCCESS; + } + + while( ! feof( pc_in ) ) { + temp_br = malloc(sizeof(buf_rec)); + if (!temp_br) { + break; + } + r = jpilot_read_next_pc( pc_in, temp_br ); + if ( r != MGU_SUCCESS ) { + free(temp_br); + break; + } + if ((temp_br->rt!=DELETED_PC_REC) + &&(temp_br->rt!=DELETED_PALM_REC) + &&(temp_br->rt!=MODIFIED_PALM_REC) + &&(temp_br->rt!=DELETED_DELETED_PALM_REC)) { + *records = g_list_append(*records, temp_br); + recs_returned++; + } + if ((temp_br->rt==DELETED_PALM_REC) || (temp_br->rt==MODIFIED_PALM_REC)) { + temp_list=*records; + if (*records) { + while(temp_list->next) { + temp_list=temp_list->next; + } + } + for (; temp_list; temp_list=temp_list->prev) { + if (((buf_rec *)temp_list->data)->unique_id == temp_br->unique_id) { + ((buf_rec *)temp_list->data)->rt = temp_br->rt; + } + } + } + free(temp_br); + } + fclose(pc_in); + + return MGU_SUCCESS; +} + +#define FULLNAME_BUFSIZE 256 +#define EMAIL_BUFSIZE 256 +/* + * Unpack address, building new data inside cache. + */ +static void jpilot_load_address( JPilotFile *pilotFile, buf_rec *buf, ItemFolder *folderInd[] ) { + struct Address addr; + gchar **addrEnt; + gint num, k; + gint cat_id = 0; + guint unique_id; + guchar attrib; + gchar fullName[ FULLNAME_BUFSIZE ]; + gchar bufEMail[ EMAIL_BUFSIZE ]; + ItemPerson *person; + ItemEMail *email; + gint *indPhoneLbl; + gchar *labelEntry; + GList *node; + gchar* extID; + struct AddressAppInfo *ai; + gchar **firstName = NULL; + gchar **lastName = NULL; +#if (PILOT_LINK_MAJOR > 11) + pi_buffer_t *RecordBuffer; +#endif /* PILOT_LINK_0_12 */ + + /* Retrieve address */ +#if (PILOT_LINK_MAJOR < 12) + num = unpack_Address( & addr, buf->buf, buf->size ); + if( num > 0 ) { +#else /* PILOT_LINK_0_12 */ + RecordBuffer = pi_buffer_new(buf->size); + memcpy(RecordBuffer->data, buf->buf, buf->size); + RecordBuffer->used = buf->size; + num = unpack_Address( & addr, RecordBuffer, address_v1 ); + pi_buffer_free(RecordBuffer); + if (num != -1) { +#endif + gchar *nameConv; + + addrEnt = addr.entry; + attrib = buf->attrib; + unique_id = buf->unique_id; + cat_id = attrib & 0x0F; + + *fullName = *bufEMail = '\0'; + + if( addrEnt[ IND_LABEL_FIRSTNAME ] ) { + firstName = g_strsplit( addrEnt[ IND_LABEL_FIRSTNAME ], "\01", 2 ); + } + if( addrEnt[ IND_LABEL_LASTNAME ] ) { + lastName = g_strsplit( addrEnt[ IND_LABEL_LASTNAME ], "\01", 2 ); + } + + if( name_order == FAMILY_LAST ) { + g_snprintf( fullName, FULLNAME_BUFSIZE, "%s %s", + firstName ? firstName[0] : "", + lastName ? lastName[0] : "" ); + } + else { + g_snprintf( fullName, FULLNAME_BUFSIZE, "%s %s", + lastName ? lastName[0] : "", + firstName ? firstName[0] : "" ); + } + + if( firstName ) { + g_strfreev( firstName ); + } + if( lastName ) { + g_strfreev( lastName ); + } + + g_strstrip( fullName ); + + nameConv = jpilot_convert_encoding( fullName ); + strncpy2( fullName, nameConv, FULLNAME_BUFSIZE ); + g_free( nameConv ); + + person = addritem_create_item_person(); + addritem_person_set_common_name( person, fullName ); + addritem_person_set_first_name( person, addrEnt[ IND_LABEL_FIRSTNAME ] ); + addritem_person_set_last_name( person, addrEnt[ IND_LABEL_LASTNAME ] ); + addrcache_id_person( pilotFile->addressCache, person ); + + extID = g_strdup_printf( "%d", unique_id ); + addritem_person_set_external_id( person, extID ); + g_free( extID ); + extID = NULL; + + /* Pointer to address metadata. */ + ai = & pilotFile->addrInfo; + + /* Add entry for each email address listed under phone labels. */ + indPhoneLbl = addr.phoneLabel; + for( k = 0; k < JPILOT_NUM_ADDR_PHONE; k++ ) { + gint ind; + + ind = indPhoneLbl[k]; + /* + * fprintf( stdout, "%d : %d : %20s : %s\n", k, ind, + * ai->phoneLabels[ind], addrEnt[3+k] ); + */ + if( indPhoneLbl[k] == IND_PHONE_EMAIL ) { + labelEntry = addrEnt[ OFFSET_PHONE_LABEL + k ]; + if( labelEntry ) { + strcpy( bufEMail, labelEntry ); + g_strchug( bufEMail ); + g_strchomp( bufEMail ); + + email = addritem_create_item_email(); + addritem_email_set_address( email, bufEMail ); + addrcache_id_email( pilotFile->addressCache, email ); + addrcache_person_add_email + ( pilotFile->addressCache, person, email ); + } + } + } + + /* Add entry for each custom label */ + node = pilotFile->labelInd; + while( node ) { + gint ind; + + ind = GPOINTER_TO_INT( node->data ); + if( ind > -1 ) { + /* + * fprintf( stdout, "%d : %20s : %s\n", ind, ai->labels[ind], + * addrEnt[ind] ); + */ + labelEntry = addrEnt[ind]; + if( labelEntry ) { + gchar *convertBuff; + + strcpy( bufEMail, labelEntry ); + g_strchug( bufEMail ); + g_strchomp( bufEMail ); + + email = addritem_create_item_email(); + addritem_email_set_address( email, bufEMail ); + + convertBuff = jpilot_convert_encoding( ai->labels[ind] ); + addritem_email_set_remarks( email, convertBuff ); + g_free( convertBuff ); + + addrcache_id_email( pilotFile->addressCache, email ); + addrcache_person_add_email + ( pilotFile->addressCache, person, email ); + } + } + + node = g_list_next( node ); + } + + if( person->listEMail ) { + if( cat_id > -1 && cat_id < JPILOT_NUM_CATEG ) { + /* Add to specified category */ + addrcache_folder_add_person + ( pilotFile->addressCache, folderInd[cat_id], person ); + } + else { + /* Add to root folder */ + addrcache_add_person( pilotFile->addressCache, person ); + } + } + else { + addritem_free_item_person( person ); + person = NULL; + } + } +} + +/* + * Free up address list. + */ +static void jpilot_free_addrlist( GList *records ) { + GList *node; + buf_rec *br; + + node = records; + while( node ) { + br = node->data; + free( br ); + node->data = NULL; + node = g_list_next( node ); + } + + /* Free up list */ + g_list_free( records ); +} + +/* + * Read address file into address cache. + */ +static gint jpilot_read_file( JPilotFile *pilotFile ) { + gint retVal, i; + GList *records = NULL; + GList *node; + buf_rec *br; + ItemFolder *folderInd[ JPILOT_NUM_CATEG ]; + + retVal = jpilot_read_db_files( pilotFile, &records ); + if( retVal != MGU_SUCCESS ) { + jpilot_free_addrlist( records ); + return retVal; + } + + /* Build array of pointers to categories */ + i = 0; + node = addrcache_get_list_folder( pilotFile->addressCache ); + while( node ) { + if( i < JPILOT_NUM_CATEG ) { + folderInd[i] = node->data; + } + node = g_list_next( node ); + i++; + } + + /* Load all addresses, free up old stuff as we go */ + node = records; + while( node ) { + br = node->data; + if( ( br->rt != DELETED_PC_REC ) && + ( br->rt != DELETED_PALM_REC ) && + ( br->rt != MODIFIED_PALM_REC ) && + ( br->rt != DELETED_DELETED_PALM_REC ) ) { + jpilot_load_address( pilotFile, br, folderInd ); + } + free( br ); + node->data = NULL; + node = g_list_next( node ); + } + + /* Free up list */ + g_list_free( records ); + + return retVal; +} + + +/* +* Read metadata from file. +*/ +static gint jpilot_read_metadata( JPilotFile *pilotFile ) { + gint retVal; + unsigned int rec_size; + unsigned char *buf; + int num; + + g_return_val_if_fail( pilotFile != NULL, -1 ); + + pilotFile->readMetadata = FALSE; + addrcache_clear( pilotFile->addressCache ); + + /* Read file info */ + retVal = jpilot_get_file_info( pilotFile, &buf, &rec_size); + if( retVal != MGU_SUCCESS ) { + pilotFile->retVal = retVal; + return pilotFile->retVal; + } + + num = unpack_AddressAppInfo( &pilotFile->addrInfo, buf, rec_size ); + if( buf ) { + free(buf); + } + if( num <= 0 ) { + pilotFile->retVal = MGU_ERROR_READ; + return pilotFile->retVal; + } + + pilotFile->readMetadata = TRUE; + pilotFile->retVal = MGU_SUCCESS; + return pilotFile->retVal; +} + +/* +* Setup labels and indexes from metadata. +* Return: TRUE is setup successfully. +*/ +static gboolean jpilot_setup_labels( JPilotFile *pilotFile ) { + gboolean retVal = FALSE; + struct AddressAppInfo *ai; + GList *node; + + g_return_val_if_fail( pilotFile != NULL, -1 ); + + /* Release indexes */ + node = pilotFile->labelInd; + while( node ) { + node->data = NULL; + node = g_list_next( node ); + } + pilotFile->labelInd = NULL; + + if( pilotFile->readMetadata ) { + ai = & pilotFile->addrInfo; + node = pilotFile->customLabels; + while( node ) { + gchar *lbl = node->data; + gint ind = -1; + gint i; + for( i = 0; i < JPILOT_NUM_LABELS; i++ ) { + gchar *labelName; + gchar convertBuff[ JPILOT_LEN_LABEL ]; + + labelName = jpilot_convert_encoding( ai->labels[i] ); + strncpy2( convertBuff, labelName, JPILOT_LEN_LABEL ); + g_free( labelName ); + labelName = convertBuff; + + if( g_ascii_strcasecmp( labelName, lbl ) == 0 ) { + ind = i; + break; + } + } + pilotFile->labelInd = g_list_append( pilotFile->labelInd, GINT_TO_POINTER(ind) ); + node = g_list_next( node ); + } + retVal = TRUE; + } + return retVal; +} + +/* +* Load list with character strings of label names. +*/ +GList *jpilot_load_label( JPilotFile *pilotFile, GList *labelList ) { + int i; + + g_return_val_if_fail( pilotFile != NULL, NULL ); + + if( pilotFile->readMetadata ) { + struct AddressAppInfo *ai = & pilotFile->addrInfo; + for( i = 0; i < JPILOT_NUM_LABELS; i++ ) { + gchar *labelName = ai->labels[i]; + + if( labelName ) { + labelName = jpilot_convert_encoding( labelName ); + labelList = g_list_append( labelList, labelName ); + } + else { + labelList = g_list_append( labelList, g_strdup( "" ) ); + } + } + } + return labelList; +} + +/* +* Return category name for specified category ID. +* Enter: Category ID. +* Return: Name, or empty string if not invalid ID. Name should be g_free() when done. +*/ +gchar *jpilot_get_category_name( JPilotFile *pilotFile, gint catID ) { + gchar *catName = NULL; + + g_return_val_if_fail( pilotFile != NULL, NULL ); + + if( pilotFile->readMetadata ) { + struct AddressAppInfo *ai = & pilotFile->addrInfo; + struct CategoryAppInfo *cat = & ai->category; + if( catID < 0 || catID > JPILOT_NUM_CATEG ) { + } + else { + catName = g_strdup( cat->name[catID] ); + } + } + if( ! catName ) catName = g_strdup( "" ); + return catName; +} + +/* +* Load list with character strings of phone label names. +*/ +GList *jpilot_load_phone_label( JPilotFile *pilotFile, GList *labelList ) { + gint i; + + g_return_val_if_fail( pilotFile != NULL, NULL ); + + if( pilotFile->readMetadata ) { + struct AddressAppInfo *ai = & pilotFile->addrInfo; + for( i = 0; i < JPILOT_NUM_PHONELABELS; i++ ) { + gchar *labelName = ai->phoneLabels[i]; + if( labelName ) { + labelList = g_list_append( labelList, g_strdup( labelName ) ); + } + else { + labelList = g_list_append( labelList, g_strdup( "" ) ); + } + } + } + return labelList; +} + +/* +* Load list with character strings of label names. Only none blank names +* are loaded. +*/ +GList *jpilot_load_custom_label( JPilotFile *pilotFile, GList *labelList ) { + gint i; + + g_return_val_if_fail( pilotFile != NULL, NULL ); + + if( pilotFile->readMetadata ) { + struct AddressAppInfo *ai = & pilotFile->addrInfo; + for( i = 0; i < NUM_CUSTOM_LABEL; i++ ) { + gchar *labelName = ai->labels[i+IND_CUSTOM_LABEL]; + if( labelName ) { + g_strchomp( labelName ); + g_strchug( labelName ); + if( *labelName != '\0' ) { + labelName = jpilot_convert_encoding( labelName ); + labelList = g_list_append( labelList, labelName ); + } + } + } + } + return labelList; +} + +/* +* Load list with character strings of category names. +*/ +GList *jpilot_get_category_list( JPilotFile *pilotFile ) { + GList *catList = NULL; + gint i; + + g_return_val_if_fail( pilotFile != NULL, NULL ); + + if( pilotFile->readMetadata ) { + struct AddressAppInfo *ai = & pilotFile->addrInfo; + struct CategoryAppInfo *cat = & ai->category; + for( i = 0; i < JPILOT_NUM_CATEG; i++ ) { + gchar *catName = cat->name[i]; + if( catName ) { + catList = g_list_append( catList, g_strdup( catName ) ); + } + else { + catList = g_list_append( catList, g_strdup( "" ) ); + } + } + } + return catList; +} + +/* +* Build folder for each category. +*/ +static void jpilot_build_category_list( JPilotFile *pilotFile ) { + struct AddressAppInfo *ai = & pilotFile->addrInfo; + struct CategoryAppInfo *cat = & ai->category; + gint i; + + for( i = 0; i < JPILOT_NUM_CATEG; i++ ) { + ItemFolder *folder = addritem_create_item_folder(); + gchar *catName; + + catName = jpilot_convert_encoding( cat->name[i] ); + addritem_folder_set_name( folder, catName ); + g_free( catName ); + + addrcache_id_folder( pilotFile->addressCache, folder ); + addrcache_add_folder( pilotFile->addressCache, folder ); + } +} + +/* +* Remove empty folders (categories). +*/ +static void jpilot_remove_empty( JPilotFile *pilotFile ) { + GList *listFolder; + GList *remList; + GList *node; + gint i = 0; + + listFolder = addrcache_get_list_folder( pilotFile->addressCache ); + node = listFolder; + remList = NULL; + while( node ) { + ItemFolder *folder = node->data; + if( ADDRITEM_NAME(folder) == NULL || *ADDRITEM_NAME(folder) == '\0' ) { + if( folder->listPerson ) { + /* Give name to folder */ + gchar name[20]; + sprintf( name, "? %d", i ); + addritem_folder_set_name( folder, name ); + } + else { + /* Mark for removal */ + remList = g_list_append( remList, folder ); + } + } + node = g_list_next( node ); + i++; + } + node = remList; + while( node ) { + ItemFolder *folder = node->data; + addrcache_remove_folder( pilotFile->addressCache, folder ); + node = g_list_next( node ); + } + g_list_free( remList ); +} + +/* +* ============================================================================================ +* Read file into list. Main entry point +* Return: TRUE if file read successfully. +* ============================================================================================ +*/ +gint jpilot_read_data( JPilotFile *pilotFile ) { + name_order = FAMILY_LAST; + convert_charcode = FALSE; + + if( conv_is_ja_locale() ) { + name_order = FAMILY_FIRST; + convert_charcode = TRUE; + } + + g_return_val_if_fail( pilotFile != NULL, -1 ); + + pilotFile->retVal = MGU_SUCCESS; + pilotFile->accessFlag = FALSE; + + if( jpilot_check_files( pilotFile ) ) { + addrcache_clear( pilotFile->addressCache ); + jpilot_read_metadata( pilotFile ); + if( pilotFile->retVal == MGU_SUCCESS ) { + jpilot_setup_labels( pilotFile ); + jpilot_build_category_list( pilotFile ); + pilotFile->retVal = jpilot_read_file( pilotFile ); + if( pilotFile->retVal == MGU_SUCCESS ) { + jpilot_remove_empty( pilotFile ); + jpilot_mark_files( pilotFile ); + pilotFile->addressCache->modified = FALSE; + pilotFile->addressCache->dataRead = TRUE; + } + } + } + return pilotFile->retVal; +} + +/* +* Return link list of persons. +*/ +GList *jpilot_get_list_person( JPilotFile *pilotFile ) { + g_return_val_if_fail( pilotFile != NULL, NULL ); + return addrcache_get_list_person( pilotFile->addressCache ); +} + +/* +* Return link list of folders. This is always NULL since there are +* no folders in GnomeCard. +* Return: NULL. +*/ +GList *jpilot_get_list_folder( JPilotFile *pilotFile ) { + g_return_val_if_fail( pilotFile != NULL, NULL ); + return addrcache_get_list_folder( pilotFile->addressCache ); +} + +/* +* Return link list of all persons. Note that the list contains references +* to items. Do *NOT* attempt to use the addrbook_free_xxx() functions... +* this will destroy the addressbook data! +* Return: List of items, or NULL if none. +*/ +GList *jpilot_get_all_persons( JPilotFile *pilotFile ) { + g_return_val_if_fail( pilotFile != NULL, NULL ); + return addrcache_get_all_persons( pilotFile->addressCache ); +} + +/* +* Check label list for specified label. +*/ +gint jpilot_check_label( struct AddressAppInfo *ai, gchar *lblCheck ) { + gint i; + gchar *lblName; + + if( lblCheck == NULL ) return -1; + if( strlen( lblCheck ) < 1 ) return -1; + for( i = 0; i < JPILOT_NUM_LABELS; i++ ) { + lblName = ai->labels[i]; + if( lblName ) { + if( strlen( lblName ) ) { + if( g_ascii_strcasecmp( lblName, lblCheck ) == 0 ) return i; + } + } + } + return -2; +} + +/* +* Validate that all parameters specified. +* Return: TRUE if data is good. +*/ +gboolean jpilot_validate( const JPilotFile *pilotFile ) { + gboolean retVal; + + g_return_val_if_fail( pilotFile != NULL, FALSE ); + + retVal = TRUE; + if( pilotFile->path ) { + if( strlen( pilotFile->path ) < 1 ) retVal = FALSE; + } + else { + retVal = FALSE; + } + if( pilotFile->name ) { + if( strlen( pilotFile->name ) < 1 ) retVal = FALSE; + } + else { + retVal = FALSE; + } + return retVal; +} + +#define WORK_BUFLEN 1024 + +/* +* Attempt to find a valid JPilot file. +* Return: Filename, or home directory if not found, or empty string if +* no home. Filename should be g_free() when done. +*/ +gchar *jpilot_find_pilotdb( void ) { + const gchar *homedir; + gchar str[ WORK_BUFLEN ]; + gint len; + FILE *fp; + + homedir = get_home_dir(); + if( ! homedir ) return g_strdup( "" ); + + strcpy( str, homedir ); + len = strlen( str ); + if( len > 0 ) { + if( str[ len-1 ] != G_DIR_SEPARATOR ) { + str[ len ] = G_DIR_SEPARATOR; + str[ ++len ] = '\0'; + } + } + strcat( str, JPILOT_DBHOME_DIR ); + strcat( str, G_DIR_SEPARATOR_S ); + strcat( str, JPILOT_DBHOME_FILE ); + + /* Attempt to open */ + if( ( fp = g_fopen( str, "rb" ) ) != NULL ) { + fclose( fp ); + } + else { + /* Truncate filename */ + str[ len ] = '\0'; + } + return g_strdup( str ); +} + +/* +* Attempt to read file, testing for valid JPilot format. +* Return: TRUE if file appears to be valid format. +*/ +gint jpilot_test_read_file( const gchar *fileSpec ) { + JPilotFile *pilotFile; + gint retVal; + + if( fileSpec ) { + pilotFile = jpilot_create_path( fileSpec ); + retVal = jpilot_read_metadata( pilotFile ); + jpilot_free( pilotFile ); + pilotFile = NULL; + } + else { + retVal = MGU_NO_FILE; + } + return retVal; +} + +/* +* Check whether label is in custom labels. +* Return: TRUE if found. +*/ +gboolean jpilot_test_custom_label( JPilotFile *pilotFile, const gchar *labelName ) { + gboolean retVal; + GList *node; + + g_return_val_if_fail( pilotFile != NULL, FALSE ); + + retVal = FALSE; + if( labelName ) { + node = pilotFile->customLabels; + while( node ) { + if( g_ascii_strcasecmp( labelName, node->data ) == 0 ) { + retVal = TRUE; + break; + } + node = g_list_next( node ); + } + } + return retVal; +} + +/* +* Test whether pilot link library installed. +* Return: TRUE if library available. +*/ +#if 0 +gboolean jpilot_test_pilot_lib( void ) { + void *handle, *fun; + + handle = dlopen( PILOT_LINK_LIB_NAME, RTLD_LAZY ); + if( ! handle ) { + return FALSE; + } + + /* Test for symbols we need */ + fun = dlsym( handle, "unpack_Address" ); + if( ! fun ) { + dlclose( handle ); + return FALSE; + } + + fun = dlsym( handle, "unpack_AddressAppInfo" ); + if( ! fun ) { + dlclose( handle ); + return FALSE; + } + dlclose( handle ); + return TRUE; +} +#endif /* 0 */ + +#endif /* USE_JPILOT */ + +/* +* End of Source. +*/ diff --git a/src/jpilot.h b/src/jpilot.h new file mode 100644 index 0000000..764e56f --- /dev/null +++ b/src/jpilot.h @@ -0,0 +1,117 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Definitions for accessing JPilot database files. + * JPilot is Copyright(c) by Judd Montgomery. + * Visit http://www.jpilot.org for more details. + */ + +#ifndef __JPILOT_H__ +#define __JPILOT_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef USE_JPILOT + +#include +#include + +#ifdef HAVE_LIBPISOCK_PI_ADDRESS_H +# include +#else +# include +#endif + +#include "addritem.h" +#include "addrcache.h" +#include "utils.h" + +typedef struct _JPilotFile JPilotFile; + +struct _JPilotFile { + gchar *name; + FILE *file; + gchar *path; + AddressCache *addressCache; + struct AddressAppInfo addrInfo; + gboolean readMetadata; + GList *customLabels; + GList *labelInd; + gint retVal; + gboolean accessFlag; + gboolean havePC3; + stime_t pc3ModifyTime; +}; + +/* Limits */ +#define JPILOT_NUM_LABELS 22 /* Number of labels */ +#define JPILOT_NUM_PHONELABELS 8 /* Number of phone number labels */ +#define JPILOT_NUM_CATEG 16 /* Number of categories */ +#define JPILOT_LEN_LABEL 15 /* Max length of label */ +#define JPILOT_LEN_CATEG 15 /* Max length of category */ +#define JPILOT_NUM_ADDR_PHONE 5 /* Number of phone entries a person + can have */ + +/* Function prototypes */ +JPilotFile *jpilot_create ( void ); +JPilotFile *jpilot_create_path ( const gchar *path ); +void jpilot_set_name ( JPilotFile* pilotFile, const gchar *value ); +void jpilot_set_file ( JPilotFile* pilotFile, const gchar *value ); +void jpilot_free ( JPilotFile *pilotFile ); +gint jpilot_get_status ( JPilotFile *pilotFile ); +gboolean jpilot_get_modified ( JPilotFile *pilotFile ); +gboolean jpilot_get_accessed ( JPilotFile *pilotFile ); +void jpilot_set_accessed ( JPilotFile *pilotFile, const gboolean value ); +gboolean jpilot_get_read_flag ( JPilotFile *pilotFile ); +ItemFolder *jpilot_get_root_folder ( JPilotFile *pilotFile ); +gchar *jpilot_get_name ( JPilotFile *pilotFile ); + +void jpilot_force_refresh ( JPilotFile *pilotFile ); +void jpilot_print_file ( JPilotFile *jpilotFile, FILE *stream ); +void jpilot_print_short ( JPilotFile *pilotFile, FILE *stream ); +gint jpilot_read_data ( JPilotFile *pilotFile ); +GList *jpilot_get_list_person ( JPilotFile *pilotFile ); +GList *jpilot_get_list_folder ( JPilotFile *pilotFile ); +GList *jpilot_get_all_persons ( JPilotFile *pilotFile ); + +GList *jpilot_load_label ( JPilotFile *pilotFile, GList *labelList ); +GList *jpilot_get_category_list ( JPilotFile *pilotFile ); +gchar *jpilot_get_category_name ( JPilotFile *pilotFile, gint catID ); +GList *jpilot_load_phone_label ( JPilotFile *pilotFile, GList *labelList ); +GList *jpilot_load_custom_label ( JPilotFile *pilotFile, GList *labelList ); + +gboolean jpilot_validate ( const JPilotFile *pilotFile ); +gchar *jpilot_find_pilotdb ( void ); + +gint jpilot_test_read_file ( const gchar *fileSpec ); + +void jpilot_clear_custom_labels ( JPilotFile *pilotFile ); +void jpilot_add_custom_label ( JPilotFile *pilotFile, const gchar *labelName ); +GList *jpilot_get_custom_labels ( JPilotFile *pilotFile ); +gboolean jpilot_test_custom_label ( JPilotFile *pilotFile, const gchar *labelName ); +/* gboolean jpilot_test_pilot_lib ( void ); */ + +gint jpilot_read_modified ( JPilotFile *pilotFile ); + +#endif /* USE_JPILOT */ + +#endif /* __JPILOT_H__ */ diff --git a/src/ldif.c b/src/ldif.c new file mode 100644 index 0000000..a158faa --- /dev/null +++ b/src/ldif.c @@ -0,0 +1,986 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Functions necessary to access LDIF files (LDAP Data Interchange Format + * files). + */ + +#include +#include +#include + +#include "mgutils.h" +#include "ldif.h" +#include "addritem.h" +#include "addrcache.h" + +#include "base64.h" +#include "codeconv.h" +#include "utils.h" + +/* +* Create new object. +*/ +LdifFile *ldif_create() { + LdifFile *ldifFile; + ldifFile = g_new0( LdifFile, 1 ); + ldifFile->path = NULL; + ldifFile->file = NULL; + ldifFile->bufptr = ldifFile->buffer; + ldifFile->hashFields = g_hash_table_new( g_str_hash, g_str_equal ); + ldifFile->tempList = NULL; + ldifFile->dirtyFlag = TRUE; + ldifFile->accessFlag = FALSE; + ldifFile->retVal = MGU_SUCCESS; + ldifFile->cbProgress = NULL; + ldifFile->importCount = 0; + return ldifFile; +} + +/* +* Properties... +*/ +void ldif_set_file( LdifFile *ldifFile, const gchar *value ) { + g_return_if_fail( ldifFile != NULL ); + + if( ldifFile->path ) { + if( strcmp( ldifFile->path, value ) != 0 ) + ldifFile->dirtyFlag = TRUE; + } + else { + ldifFile->dirtyFlag = TRUE; + } + ldifFile->path = mgu_replace_string( ldifFile->path, value ); + g_strstrip( ldifFile->path ); + ldifFile->importCount = 0; +} +void ldif_set_accessed( LdifFile *ldifFile, const gboolean value ) { + g_return_if_fail( ldifFile != NULL ); + ldifFile->accessFlag = value; +} + +/* +* Register a callback function. When called, the function will be passed +* the following arguments: +* LdifFile object, +* File size (long), +* Current position (long) +* This can be used for a progress indicator. +*/ +void ldif_set_callback( LdifFile *ldifFile, void *func ) { + ldifFile->cbProgress = func; +} + +/* +* Create field record object. +*/ +static Ldif_FieldRec *ldif_create_fieldrec( gchar *field ) { + Ldif_FieldRec *rec = g_new0( Ldif_FieldRec, 1 ); + rec->tagName = g_strdup( field ); + rec->userName = NULL; + rec->reserved = FALSE; + rec->selected = FALSE; + return rec; +} + +/* +* Free field record object. +*/ +static void ldif_free_fieldrec( Ldif_FieldRec *rec ) { + if( rec ) { + g_free( rec->tagName ); + g_free( rec->userName ); + rec->tagName = NULL; + rec->userName = NULL; + rec->reserved = FALSE; + rec->selected = FALSE; + g_free( rec ); + } +} + +/* +* Free hash table entry visitor function. +*/ +static gint ldif_hash_free_vis( gpointer key, gpointer value, gpointer data ) { + ldif_free_fieldrec( ( Ldif_FieldRec * ) value ); + value = NULL; + key = NULL; + return -1; +} + +/* +* Free up object by releasing internal memory. +*/ +void ldif_free( LdifFile *ldifFile ) { + g_return_if_fail( ldifFile != NULL ); + + /* Close file */ + if( ldifFile->file ) fclose( ldifFile->file ); + + /* Free internal stuff */ + g_free( ldifFile->path ); + + /* Free field list */ + g_hash_table_foreach_remove( ldifFile->hashFields, ldif_hash_free_vis, NULL ); + g_hash_table_destroy( ldifFile->hashFields ); + ldifFile->hashFields = NULL; + + /* Clear pointers */ + ldifFile->file = NULL; + ldifFile->path = NULL; + ldifFile->retVal = MGU_SUCCESS; + ldifFile->tempList = NULL; + ldifFile->dirtyFlag = FALSE; + ldifFile->accessFlag = FALSE; + ldifFile->cbProgress = NULL; + + /* Now release file object */ + g_free( ldifFile ); +} + +/* +* Display field record. +*/ +void ldif_print_fieldrec( Ldif_FieldRec *rec, FILE *stream ) { + fprintf( stream, "\ttag:\t%s", rec->reserved ? "yes" : "no" ); + fprintf( stream, "\t%s", rec->selected ? "yes" : "no" ); + fprintf( stream, "\t:%s:\t:%s:\n", rec->userName, rec->tagName ); +} + +/* +* Display field record. + * +*/ +static void ldif_print_file_vis( gpointer key, gpointer value, gpointer data ) { + Ldif_FieldRec *rec = value; + FILE *stream = data; + ldif_print_fieldrec( rec, stream ); +} + +/* +* Display object to specified stream. +*/ +void ldif_print_file( LdifFile *ldifFile, FILE *stream ) { + g_return_if_fail( ldifFile != NULL ); + fprintf( stream, "LDIF File:\n" ); + fprintf( stream, "file spec: '%s'\n", ldifFile->path ); + fprintf( stream, " ret val: %d\n", ldifFile->retVal ); + fprintf( stream, " fields: {\n" ); + g_hash_table_foreach( ldifFile->hashFields, ldif_print_file_vis, stream ); + fprintf( stream, "} ---\n" ); +} + +/* +* Open file for read. +* return: TRUE if file opened successfully. +*/ +static gint ldif_open_file( LdifFile* ldifFile ) { + /* printf( "Opening file\n" ); */ + if( ldifFile->path ) { + ldifFile->file = g_fopen( ldifFile->path, "rb" ); + if( ! ldifFile->file ) { + /* printf( "can't open %s\n", ldifFile->path ); */ + ldifFile->retVal = MGU_OPEN_FILE; + return ldifFile->retVal; + } + } + else { + /* printf( "file not specified\n" ); */ + ldifFile->retVal = MGU_NO_FILE; + return ldifFile->retVal; + } + + /* Setup a buffer area */ + ldifFile->buffer[0] = '\0'; + ldifFile->bufptr = ldifFile->buffer; + ldifFile->retVal = MGU_SUCCESS; + return ldifFile->retVal; +} + +/* +* Close file. +*/ +static void ldif_close_file( LdifFile *ldifFile ) { + g_return_if_fail( ldifFile != NULL ); + if( ldifFile->file ) fclose( ldifFile->file ); + ldifFile->file = NULL; +} + +/* +* Read line of text from file. +* Return: ptr to buffer where line starts. +*/ +static gchar *ldif_get_line( LdifFile *ldifFile ) { + gchar buf[ LDIFBUFSIZE ]; + + if( feof( ldifFile->file ) ) + return NULL; + + if( fgets( buf, sizeof( buf ), ldifFile->file ) == NULL ) + return NULL; + + strretchomp( buf ); + + /* Return a copy of buffer */ + return g_strdup( buf ); +} + +/* +* Parse tag name from line buffer. +* Enter: line Buffer. +* flag64 Base-64 encoder flag. +* Return: Buffer containing the tag name, or NULL if no delimiter char found. +* If a double delimiter (::) is found, flag64 is set. +*/ +static gchar *ldif_get_tagname( char* line, gboolean *flag64 ) { + gint len = 0; + gchar *tag = NULL; + gchar *lptr = line; + gchar *sptr = NULL; + + while( *lptr++ ) { + /* Check for language tag */ + if( *lptr == LDIF_LANG_TAG ) { + if( sptr == NULL ) sptr = lptr; + } + + /* Check for delimiter */ + if( *lptr == LDIF_SEP_TAG ) { + if( sptr ) { + len = sptr - line; + } + else { + len = lptr - line; + } + + /* Base-64 encoding? */ + if( * ++lptr == LDIF_SEP_TAG ) *flag64 = TRUE; + + tag = g_strndup( line, len+1 ); + tag[ len ] = '\0'; + g_strdown( tag ); + return tag; + } + } + return tag; +} + +/* +* Parse tag value from line buffer. +* Enter: line Buffer. +* Return: Buffer containing the tag value. Empty string is returned if +* no delimiter char found. +*/ +static gchar *ldif_get_tagvalue( gchar* line ) { + gchar *value = NULL; + gchar *start = NULL; + gchar *lptr; + gint len = 0; + + for( lptr = line; *lptr; lptr++ ) { + if( *lptr == LDIF_SEP_TAG ) { + if( ! start ) + start = lptr + 1; + } + } + if( start ) { + if( *start == LDIF_SEP_TAG ) start++; + len = lptr - start; + value = g_strndup( start, len+1 ); + g_strstrip( value ); + } + else { + /* Ensure that we get an empty string */ + value = g_strndup( "", 1 ); + } + value[ len ] = '\0'; + return value; +} + +#if 0 +/* +* Dump linked lists of character strings (for debug). +*/ +static void ldif_dump_lists( GSList *listName, GSList *listAddr, GSList *listRem, GSList *listID, FILE *stream ) { + fprintf( stream, "dump name\n" ); + fprintf( stream, "------------\n" ); + mgu_print_list( listName, stdout ); + fprintf( stream, "dump address\n" ); + fprintf( stream, "------------\n" ); + mgu_print_list( listAddr, stdout ); + fprintf( stream, "dump remarks\n" ); + fprintf( stdout, "------------\n" ); + mgu_print_list( listRem, stdout ); + fprintf( stream, "dump id\n" ); + fprintf( stdout, "------------\n" ); + mgu_print_list( listID, stdout ); +} +#endif + +/* +* Parsed address data. +*/ +typedef struct _Ldif_ParsedRec_ Ldif_ParsedRec; +struct _Ldif_ParsedRec_ { + GSList *listCName; + GSList *listFName; + GSList *listLName; + GSList *listNName; + GSList *listAddress; + GSList *listID; + GSList *userAttr; +}; + +/* +* User attribute data. +*/ +typedef struct _Ldif_UserAttr_ Ldif_UserAttr; +struct _Ldif_UserAttr_ { + gchar *name; + gchar *value; +}; + +/* +* Build an address list entry and append to list of address items. Name is formatted +* as " ". +*/ +static void ldif_build_items( LdifFile *ldifFile, Ldif_ParsedRec *rec, AddressCache *cache ) { + GSList *nodeFirst; + GSList *nodeAddress; + GSList *nodeAttr; + gchar *firstName = NULL, *lastName = NULL, *fullName = NULL, *nickName = NULL; + gint iLen = 0, iLenT = 0; + ItemPerson *person; + ItemEMail *email; + gboolean familyFirst = FALSE; + + nodeAddress = rec->listAddress; + if( nodeAddress == NULL ) return; + + /* Find longest first name in list */ + nodeFirst = rec->listFName; + while( nodeFirst ) { + if( firstName == NULL ) { + firstName = nodeFirst->data; + iLen = strlen( firstName ); + } + else { + if( ( iLenT = strlen( nodeFirst->data ) ) > iLen ) { + firstName = nodeFirst->data; + iLen = iLenT; + } + } + nodeFirst = g_slist_next( nodeFirst ); + } + + /* Format name */ + if( rec->listLName ) { + lastName = rec->listLName->data; + } + if( rec->listCName ) { + fullName = g_strdup((gchar *)rec->listCName->data); + } + + familyFirst = conv_is_ja_locale(); + + if( fullName == NULL ) { + if( familyFirst ) { + if( lastName ) { + if( firstName ) { + fullName = g_strdup_printf( "%s %s", lastName, firstName ); + } + else { + fullName = g_strdup_printf( "%s", lastName ); + } + } + else { + if( firstName ) { + fullName = g_strdup_printf( "%s", firstName ); + } + } + } else { + if( firstName ) { + if( lastName ) { + fullName = g_strdup_printf( "%s %s", firstName, lastName ); + } + else { + fullName = g_strdup_printf( "%s", firstName ); + } + } + else { + if( lastName ) { + fullName = g_strdup_printf( "%s", lastName ); + } + } + } + } + if( fullName ) { + g_strstrip( fullName ); + } + + if( rec->listNName ) { + nickName = rec->listNName->data; + } + + person = addritem_create_item_person(); + addritem_person_set_common_name( person, fullName ); + addritem_person_set_first_name( person, firstName ); + addritem_person_set_last_name( person, lastName ); + addritem_person_set_nick_name( person, nickName ); + addrcache_id_person( cache, person ); + addrcache_add_person( cache, person ); + ++ldifFile->importCount; + + /* Add address item */ + while( nodeAddress ) { + email = addritem_create_item_email(); + addritem_email_set_address( email, nodeAddress->data ); + addrcache_id_email( cache, email ); + addrcache_person_add_email( cache, person, email ); + nodeAddress = g_slist_next( nodeAddress ); + } + g_free( fullName ); + fullName = firstName = lastName = NULL; + + /* Add user attributes */ + nodeAttr = rec->userAttr; + while( nodeAttr ) { + Ldif_UserAttr *attr = nodeAttr->data; + UserAttribute *attrib = addritem_create_attribute(); + addritem_attrib_set_name( attrib, attr->name ); + addritem_attrib_set_value( attrib, attr->value ); + addritem_person_add_attribute( person, attrib ); + nodeAttr = g_slist_next( nodeAttr ); + } + nodeAttr = NULL; +} + +/* +* Add selected field as user attribute. +*/ +static void ldif_add_user_attr( Ldif_ParsedRec *rec, gchar *tagName, gchar *tagValue, GHashTable *hashField ) { + Ldif_FieldRec *fld = NULL; + Ldif_UserAttr *attr = NULL; + gchar *name; + + fld = g_hash_table_lookup( hashField, tagName ); + if( fld ) { + if( fld->reserved ) return; + if( ! fld->selected ) return; + + name = fld->tagName; + if( fld->userName && *(fld->userName) ) { + name = fld->userName; + } + attr = g_new0( Ldif_UserAttr, 1 ); + attr->name = g_strdup( name ); + attr->value = g_strdup( tagValue ); + rec->userAttr = g_slist_append( rec->userAttr, attr ); + } +} + +/* +* Add value to parsed data. +*/ +static void ldif_add_value( Ldif_ParsedRec *rec, gchar *tagName, gchar *tagValue, GHashTable *hashField ) { + gchar *nm, *val; + + nm = g_strdup( tagName ); + g_strdown( nm ); + if( tagValue ) { + val = g_strdup( tagValue ); + } + else { + val = g_strdup( "" ); + } + g_strstrip( val ); + if( g_ascii_strcasecmp( nm, LDIF_TAG_COMMONNAME ) == 0 ) { + rec->listCName = g_slist_append( rec->listCName, val ); + } + else if( g_ascii_strcasecmp( nm, LDIF_TAG_FIRSTNAME ) == 0 ) { + rec->listFName = g_slist_append( rec->listFName, val ); + } + else if( g_ascii_strcasecmp( nm, LDIF_TAG_LASTNAME ) == 0 ) { + rec->listLName = g_slist_append( rec->listLName, val ); + } + else if( g_ascii_strcasecmp( nm, LDIF_TAG_NICKNAME ) == 0 || + g_ascii_strcasecmp( nm, LDIF_TAG_NICKNAME2 ) == 0 || + g_ascii_strcasecmp( nm, LDIF_TAG_XNICKNAME ) == 0 ) { + rec->listNName = g_slist_append( rec->listNName, val ); + } + else if( g_ascii_strcasecmp( nm, LDIF_TAG_EMAIL ) == 0 ) { + if( g_slist_find_custom( rec->listAddress, val, (GCompareFunc)strcmp2 )) { + /* skip duplicated address */ + g_free( val ); + } else { + rec->listAddress = g_slist_append( rec->listAddress, val ); + } + } + else { + /* Add field as user attribute */ + ldif_add_user_attr( rec, tagName, tagValue, hashField ); + g_free( val ); + } + g_free( nm ); +} + +/* +* Clear parsed data. +*/ +static void ldif_clear_rec( Ldif_ParsedRec *rec ) { + GSList *list; + + /* Free up user attributes */ + list = rec->userAttr; + while( list ) { + Ldif_UserAttr *attr = list->data; + g_free( attr->name ); + g_free( attr->value ); + g_free( attr ); + list = g_slist_next( list ); + } + g_slist_free( rec->userAttr ); + + g_slist_free( rec->listCName ); + g_slist_free( rec->listFName ); + g_slist_free( rec->listLName ); + g_slist_free( rec->listNName ); + g_slist_free( rec->listAddress ); + g_slist_free( rec->listID ); + + rec->userAttr = NULL; + rec->listCName = NULL; + rec->listFName = NULL; + rec->listLName = NULL; + rec->listNName = NULL; + rec->listAddress = NULL; + rec->listID = NULL; +} + +#if 0 +/* +* Print parsed data. +*/ +static void ldif_print_record( Ldif_ParsedRec *rec, FILE *stream ) { + GSList *list; + + fprintf( stream, "LDIF Parsed Record:\n" ); + fprintf( stream, "common name:" ); + mgu_print_list( rec->listCName, stream ); + if( ! rec->listCName ) fprintf( stream, "\n" ); + fprintf( stream, "first name:" ); + mgu_print_list( rec->listFName, stream ); + if( ! rec->listFName ) fprintf( stream, "\n" ); + fprintf( stream, "last name:" ); + mgu_print_list( rec->listLName, stream ); + if( ! rec->listLName ) fprintf( stream, "\n" ); + fprintf( stream, "nick name:" ); + mgu_print_list( rec->listNName, stream ); + if( ! rec->listNName ) fprintf( stream, "\n" ); + fprintf( stream, "address:" ); + mgu_print_list( rec->listAddress, stream ); + if( ! rec->listAddress ) fprintf( stream, "\n" ); + fprintf( stream, "id:" ); + mgu_print_list( rec->listID, stream ); + if( ! rec->listID ) fprintf( stream, "\n" ); + + list = rec->userAttr; + while( list ) { + Ldif_UserAttr *attr = list->data; + fprintf( stream, "n/v:\t%s:\t:%s:\n", attr->name, attr->value ); + list = g_slist_next( list ); + } + list = NULL; +} + +static void ldif_dump_b64( gchar *buf ) { + Base64Decoder *decoder = NULL; + gchar outBuf[8192]; + gint len; + + g_print( "base-64 : inbuf : %s\n", buf ); + decoder = base64_decoder_new(); + len = base64_decoder_decode( decoder, buf, outBuf ); + if (len < 0) { + g_print( "base-64 : Bad BASE64 content\n" ); + } + else { + outBuf[len] = '\0'; + g_print( "base-64 : %d : %s\n\n", len, outBuf ); + } + base64_decoder_free( decoder ); + decoder = NULL; +} +#endif + +static gchar *ldif_conv_base64( gchar *buf ) { + gchar *outbuf; + gint len; + + outbuf = g_malloc(strlen(buf) + 1); + len = base64_decode((guchar *)outbuf, buf, -1); + outbuf[len] = '\0'; + if (g_utf8_validate(outbuf, -1, NULL)) + return outbuf; + else { + gchar *utf8str; + + if (conv_is_ja_locale()) + utf8str = conv_codeset_strdup(outbuf, NULL, NULL); + else + utf8str = conv_localetodisp(outbuf, NULL); + + g_free(outbuf); + return utf8str; + } +} + +/* +* Read file data into address cache. +* Note that one LDIF record identifies one entity uniquely with the +* distinguished name (dn) tag. Each person can have multiple E-Mail +* addresses. Also, each person can have many common name (cn) tags. +*/ +static void ldif_read_file( LdifFile *ldifFile, AddressCache *cache ) { + gchar *tagName = NULL, *tagValue = NULL; + gchar *lastTag = NULL; + GSList *listValue = NULL; + gboolean flagEOF = FALSE, flagEOR = FALSE; + gboolean flag64 = FALSE, last64 = FALSE; + Ldif_ParsedRec *rec; + long posEnd = 0L; + long posCur = 0L; + GHashTable *hashField; + + hashField = ldifFile->hashFields; + rec = g_new0( Ldif_ParsedRec, 1 ); + ldif_clear_rec( rec ); + + /* Find EOF for progress indicator */ + fseek( ldifFile->file, 0L, SEEK_END ); + posEnd = ftell( ldifFile->file ); + fseek( ldifFile->file, 0L, SEEK_SET ); + + while( ! flagEOF ) { + gchar *line = ldif_get_line( ldifFile ); + + posCur = ftell( ldifFile->file ); + if( ldifFile->cbProgress ) { + /* Call progress indicator */ + ( ldifFile->cbProgress ) ( ldifFile, & posEnd, & posCur ); + } + + flag64 = FALSE; + if( line == NULL ) { + flagEOF = flagEOR = TRUE; + } + else if( *line == '\0' ) { + flagEOR = TRUE; + } + + if( flagEOR ) { + /* EOR, Output address data */ + if( lastTag ) { + gchar *fullValue; + + /* Save record */ + fullValue = mgu_list_coalesce( listValue ); + + /* Base-64 encoded data */ + if( last64 ) { + gchar *decValue; + decValue = ldif_conv_base64( fullValue ); + g_free( fullValue ); + fullValue = decValue; + } + + ldif_add_value( rec, lastTag, fullValue, hashField ); + /* ldif_print_record( rec, stdout ); */ + ldif_build_items( ldifFile, rec, cache ); + ldif_clear_rec( rec ); + g_free( fullValue ); + g_free( lastTag ); + mgu_free_list( listValue ); + lastTag = NULL; + listValue = NULL; + last64 = FALSE; + } + } + if( line ) { + flagEOR = FALSE; + if( *line == ' ' ) { + /* Continuation line */ + listValue = g_slist_append( listValue, g_strdup( line+1 ) ); + } + else if( *line == '=' ) { + /* Base-64 encoded continuation field */ + listValue = g_slist_append( listValue, g_strdup( line ) ); + } + else { + /* Parse line */ + tagName = ldif_get_tagname( line, &flag64 ); + if( tagName ) { + tagValue = ldif_get_tagvalue( line ); + if( tagValue ) { + if( lastTag ) { + gchar *fullValue; + + /* Save data */ + fullValue = mgu_list_coalesce( listValue ); + /* Base-64 encoded data */ + if( last64 ) { + gchar *decValue; + decValue = ldif_conv_base64( fullValue ); + g_free( fullValue ); + fullValue = decValue; + } + + ldif_add_value( rec, lastTag, fullValue, hashField ); + g_free( fullValue ); + g_free( lastTag ); + mgu_free_list( listValue ); + lastTag = NULL; + listValue = NULL; + last64 = FALSE; + } + + lastTag = g_strdup( tagName ); + listValue = g_slist_append( listValue, g_strdup( tagValue ) ); + g_free( tagValue ); + last64 = flag64; + } + g_free( tagName ); + } + } + } + g_free( line ); + } + + /* Release data */ + ldif_clear_rec( rec ); + g_free( rec ); + g_free( lastTag ); + mgu_free_list( listValue ); +} + +/* +* Add list of field names to hash table. +*/ +static void ldif_hash_add_list( GHashTable *table, GSList *list ) { + GSList *node = list; + + /* mgu_print_list( list, stdout ); */ + while( node ) { + gchar *tag = node->data; + if( ! g_hash_table_lookup( table, tag ) ) { + Ldif_FieldRec *rec = NULL; + gchar *key = g_strdup( tag ); + + rec = ldif_create_fieldrec( tag ); + if( g_ascii_strcasecmp( tag, LDIF_TAG_COMMONNAME ) == 0 ) { + rec->reserved = TRUE; + } + else if( g_ascii_strcasecmp( tag, LDIF_TAG_FIRSTNAME ) == 0 ) { + rec->reserved = TRUE; + } + else if( g_ascii_strcasecmp( tag, LDIF_TAG_LASTNAME ) == 0 ) { + rec->reserved = TRUE; + } + else if( g_ascii_strcasecmp( tag, LDIF_TAG_NICKNAME ) == 0 || + g_ascii_strcasecmp( tag, LDIF_TAG_XNICKNAME) == 0 || + g_ascii_strcasecmp( tag, LDIF_TAG_NICKNAME2) == 0 ) { + rec->reserved = TRUE; + } + else if( g_ascii_strcasecmp( tag, LDIF_TAG_EMAIL ) == 0 ) { + rec->reserved = TRUE; + } + g_hash_table_insert( table, key, rec ); + } + node = g_slist_next( node ); + } +} + +/* +* Sorted list comparison function. +*/ +static int ldif_field_compare( gconstpointer ptr1, gconstpointer ptr2 ) { + const Ldif_FieldRec *rec1 = ptr1; + const Ldif_FieldRec *rec2 = ptr2; + return g_ascii_strcasecmp( rec1->tagName, rec2->tagName ); +} + +/* +* Append hash table entry to list - visitor function. +*/ +static void ldif_hash2list_vis( gpointer key, gpointer value, gpointer data ) { + LdifFile *ldf = data; + ldf->tempList = g_list_insert_sorted( ldf->tempList, value, ldif_field_compare ); +} + +/* +* Read tag names for file data. +*/ +static void ldif_read_tag_list( LdifFile *ldifFile ) { + gchar *tagName = NULL; + GSList *listTags = NULL; + gboolean flagEOF = FALSE, flagEOR = FALSE, flagMail = FALSE; + gboolean flag64 = FALSE; + long posEnd = 0L; + long posCur = 0L; + + /* Clear hash table */ + g_hash_table_foreach_remove( ldifFile->hashFields, ldif_hash_free_vis, NULL ); + + /* Find EOF for progress indicator */ + fseek( ldifFile->file, 0L, SEEK_END ); + posEnd = ftell( ldifFile->file ); + fseek( ldifFile->file, 0L, SEEK_SET ); + + /* Process file */ + while( ! flagEOF ) { + gchar *line = ldif_get_line( ldifFile ); + + posCur = ftell( ldifFile->file ); + if( ldifFile->cbProgress ) { + /* Call progress indicator */ + ( ldifFile->cbProgress ) ( ldifFile, & posEnd, & posCur ); + } + + flag64 = FALSE; + if( line == NULL ) { + flagEOF = flagEOR = TRUE; + } + else if( *line == '\0' ) { + flagEOR = TRUE; + } + + if( flagEOR ) { + /* EOR, Output address data */ + /* Save field list to hash table */ + if( flagMail ) { + ldif_hash_add_list( ldifFile->hashFields, listTags ); + } + mgu_free_list( listTags ); + listTags = NULL; + flagMail = FALSE; + } + if( line ) { + flagEOR = FALSE; + if( *line == ' ' ) { + /* Continuation line */ + } + else if( *line == '=' ) { + /* Base-64 encoded continuation field */ + } + else { + /* Parse line */ + tagName = ldif_get_tagname( line, &flag64 ); + if( tagName ) { + /* Add tag to list */ + listTags = g_slist_append( listTags, tagName ); + if( g_ascii_strcasecmp( tagName, LDIF_TAG_EMAIL ) == 0 ) { + flagMail = TRUE; + } + } + } + } + g_free( line ); + } + + /* Release data */ + mgu_free_list( listTags ); + listTags = NULL; +} + +/* +* ============================================================================ +* Read file into list. Main entry point +* Enter: ldifFile LDIF control data. +* cache Address cache to load. +* Return: Status code. +* ============================================================================ +*/ +gint ldif_import_data( LdifFile *ldifFile, AddressCache *cache ) { + g_return_val_if_fail( ldifFile != NULL, MGU_BAD_ARGS ); + ldifFile->retVal = MGU_SUCCESS; + addrcache_clear( cache ); + cache->dataRead = FALSE; + ldif_open_file( ldifFile ); + if( ldifFile->retVal == MGU_SUCCESS ) { + /* Read data into the cache */ + ldif_read_file( ldifFile, cache ); + ldif_close_file( ldifFile ); + + /* Mark cache */ + cache->modified = FALSE; + cache->dataRead = TRUE; + } + return ldifFile->retVal; +} + +/* +* ============================================================================ +* Process entire file reading list of unique fields. List of fields may be +* accessed with the ldif_get_fieldlist() function. +* Enter: ldifFile LDIF control data. +* Return: Status code. +* ============================================================================ +*/ +gint ldif_read_tags( LdifFile *ldifFile ) { + g_return_val_if_fail( ldifFile != NULL, MGU_BAD_ARGS ); + ldifFile->retVal = MGU_SUCCESS; + if( ldifFile->dirtyFlag ) { + ldif_open_file( ldifFile ); + if( ldifFile->retVal == MGU_SUCCESS ) { + /* Read data into the cache */ + ldif_read_tag_list( ldifFile ); + ldif_close_file( ldifFile ); + ldifFile->dirtyFlag = FALSE; + ldifFile->accessFlag = TRUE; + } + } + return ldifFile->retVal; +} + +/* +* Return list of fields for LDIF file. +* Enter: ldifFile LdifFile object. +* Return: Linked list of Ldif_FieldRec objects. This list may be g_free'd. +* Note that the objects in the list should not be freed since they refer to +* objects inside the internal cache. These objects will be freed when +* LDIF file object is freed. +*/ +GList *ldif_get_fieldlist( LdifFile *ldifFile ) { + GList *list = NULL; + + g_return_val_if_fail( ldifFile != NULL, NULL ); + if( ldifFile->hashFields ) { + ldifFile->tempList = NULL; + g_hash_table_foreach( ldifFile->hashFields, ldif_hash2list_vis, ldifFile ); + list = ldifFile->tempList; + ldifFile->tempList = NULL; + } + return list; +} + +/* +* End of Source. +*/ + diff --git a/src/ldif.h b/src/ldif.h new file mode 100644 index 0000000..05cd722 --- /dev/null +++ b/src/ldif.h @@ -0,0 +1,121 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Definitions necessary to access LDIF files (LDAP Data Interchange Format + * files). These files are used to load LDAP servers and to interchange data + * between servers. They are also used by several E-Mail client programs and + * other programs as a means of interchange address book data. + */ + +#ifndef __LDIF_H__ +#define __LDIF_H__ + +#include +#include + +#include "addrcache.h" + +#define LDIFBUFSIZE 2048 + +/* Reserved tag names - for address book import */ +#define LDIF_TAG_COMMONNAME "cn" +#define LDIF_TAG_FIRSTNAME "givenname" +#define LDIF_TAG_LASTNAME "sn" +#define LDIF_TAG_NICKNAME "mozillanickname" +#define LDIF_TAG_XNICKNAME "xmozillanickname" +#define LDIF_TAG_NICKNAME2 "nickname" +#define LDIF_TAG_EMAIL "mail" + +#define LDIF_SEP_TAG ':' +#define LDIF_LANG_TAG ';' + +/* +// Typical LDIF entry (similar to that generated by Netscape): +// +// dn: uid=axel, dc=axel, dc=com +// cn: Axel Rose +// sn: Rose +// givenname: Arnold +// xmozillanickname: Axel +// mail: axel@axelrose.com +// mail: axelrose@aol.com +// mail: axel@netscape.net +// mail: axel@hotmail.com +// uid: axelrose +// o: The Company +// locality: Denver +// st: CO +// streetaddress: 777 Lexington Avenue +// postalcode: 80298 +// countryname: USA +// telephonenumber: 303-555-1234 +// homephone: 303-555-2345 +// cellphone: 303-555-3456 +// homeurl: http://www.axelrose.com +// objectclass: top +// objectclass: person +// +// Note that first entry is always dn. An empty line defines end of +// record. Note that attribute names are case insensitive. There may +// also be several occurrences of an attribute, for example, as +// illustrated for "mail" and "objectclass" attributes. LDIF files +// can also use binary data using base-64 encoding. +// +*/ + +/* LDIF file object */ +typedef struct _LdifFile LdifFile; +struct _LdifFile { + FILE *file; + gchar *path; + gchar *bufptr; + gchar buffer[ LDIFBUFSIZE ]; + gint retVal; + GHashTable *hashFields; + GList *tempList; + gboolean dirtyFlag; + gboolean accessFlag; + void (*cbProgress)( void *, void *, void * ); + gint importCount; +}; + +/* Field list structure */ +typedef struct _Ldif_FieldRec_ Ldif_FieldRec; +struct _Ldif_FieldRec_ { + gchar *tagName; + gchar *userName; + gboolean reserved; + gboolean selected; +}; + +/* Function prototypes */ +LdifFile *ldif_create ( void ); +void ldif_set_file ( LdifFile* ldifFile, const gchar *value ); +void ldif_set_accessed ( LdifFile* ldifFile, const gboolean value ); +void ldif_set_callback ( LdifFile *ldifFile, void *func ); +void ldif_free ( LdifFile *ldifFile ); +void ldif_print_fieldrec ( Ldif_FieldRec *rec, FILE *stream ); +void ldif_print_file ( LdifFile *ldifFile, FILE *stream ); +gint ldif_import_data ( LdifFile *ldifFile, AddressCache *cache ); +gint ldif_read_tags ( LdifFile *ldifFile ); +GList *ldif_get_fieldlist ( LdifFile *ldifFile ); + +#endif /* __LDIF_H__ */ + diff --git a/src/libsylpheed-plugin-0.def b/src/libsylpheed-plugin-0.def new file mode 100644 index 0000000..95642c0 --- /dev/null +++ b/src/libsylpheed-plugin-0.def @@ -0,0 +1,114 @@ +EXPORTS + syl_plugin_add_factory_item @ 1 + syl_plugin_add_menuitem @ 2 + syl_plugin_add_symbol @ 3 + syl_plugin_alertpanel @ 4 + syl_plugin_alertpanel_full @ 5 + syl_plugin_alertpanel_message @ 6 + syl_plugin_alertpanel_message_with_disable @ 7 + syl_plugin_app_will_exit @ 8 + syl_plugin_check_version @ 9 + syl_plugin_compose_entry_append @ 10 + syl_plugin_compose_entry_get_text @ 11 + syl_plugin_compose_entry_set @ 12 + syl_plugin_compose_lock @ 13 + syl_plugin_compose_new @ 14 + syl_plugin_compose_unlock @ 15 + syl_plugin_folder_sel @ 16 + syl_plugin_folder_sel_full @ 17 + syl_plugin_folderview_add_sub_widget @ 18 + syl_plugin_folderview_check_new @ 19 + syl_plugin_folderview_check_new_all @ 20 + syl_plugin_folderview_check_new_item @ 21 + syl_plugin_folderview_check_new_selected @ 22 + syl_plugin_folderview_get @ 23 + syl_plugin_folderview_get_selected_item @ 24 + syl_plugin_folderview_select @ 25 + syl_plugin_folderview_select_next_unread @ 26 + syl_plugin_folderview_unselect @ 27 + syl_plugin_folderview_update_all_updated @ 28 + syl_plugin_folderview_update_item @ 29 + syl_plugin_folderview_update_item_foreach @ 30 + syl_plugin_get_info @ 31 + syl_plugin_get_module_list @ 32 + syl_plugin_get_prog_version @ 33 + syl_plugin_get_type @ 34 + syl_plugin_inc_is_active @ 35 + syl_plugin_inc_lock @ 36 + syl_plugin_inc_mail @ 37 + syl_plugin_inc_unlock @ 38 + syl_plugin_init_lib @ 39 + syl_plugin_input_dialog @ 40 + syl_plugin_input_dialog_with_invisible @ 41 + syl_plugin_load @ 42 + syl_plugin_load_all @ 43 + syl_plugin_lookup_symbol @ 44 + syl_plugin_main_window_get @ 45 + syl_plugin_main_window_get_statusbar @ 46 + syl_plugin_main_window_lock @ 47 + syl_plugin_main_window_popup @ 48 + syl_plugin_main_window_unlock @ 49 + syl_plugin_manage_window_get_focus_window @ 50 + syl_plugin_manage_window_set_transient @ 51 + syl_plugin_manage_window_signals_connect @ 52 + syl_plugin_menu_set_active @ 53 + syl_plugin_menu_set_sensitive @ 54 + syl_plugin_menu_set_sensitive_all @ 55 + syl_plugin_messageview_create_with_new_window @ 56 + syl_plugin_notification_window_close @ 57 + syl_plugin_notification_window_open @ 58 + syl_plugin_notification_window_set_message @ 59 + syl_plugin_open_message @ 60 + syl_plugin_open_message_by_new_window @ 61 + syl_plugin_send_message @ 62 + syl_plugin_send_message_queue_all @ 63 + syl_plugin_send_message_set_forward_flags @ 64 + syl_plugin_send_message_set_reply_flag @ 65 + syl_plugin_signal_connect @ 66 + syl_plugin_signal_disconnect @ 67 + syl_plugin_signal_emit @ 68 + syl_plugin_summary_get_current_folder @ 69 + syl_plugin_summary_get_msg_list @ 70 + syl_plugin_summary_get_selected_msg_list @ 71 + syl_plugin_summary_get_selection_type @ 72 + syl_plugin_summary_is_locked @ 73 + syl_plugin_summary_is_read_locked @ 74 + syl_plugin_summary_is_write_locked @ 75 + syl_plugin_summary_lock @ 76 + syl_plugin_summary_open_msg @ 77 + syl_plugin_summary_redisplay_msg @ 78 + syl_plugin_summary_reedit @ 79 + syl_plugin_summary_select_by_msginfo @ 80 + syl_plugin_summary_select_by_msgnum @ 81 + syl_plugin_summary_show_queued_msgs @ 82 + syl_plugin_summary_unlock @ 83 + syl_plugin_summary_update_by_msgnum @ 84 + syl_plugin_summary_update_selected_rows @ 85 + syl_plugin_summary_view_get @ 86 + syl_plugin_summary_view_source @ 87 + syl_plugin_summary_write_lock @ 88 + syl_plugin_summary_write_unlock @ 89 + syl_plugin_unload_all @ 90 + syl_plugin_update_check @ 91 + syl_plugin_update_check_get_check_plugin_url @ 92 + syl_plugin_update_check_get_check_url @ 93 + syl_plugin_update_check_get_download_url @ 94 + syl_plugin_update_check_get_jump_plugin_url @ 95 + syl_plugin_update_check_get_jump_url @ 96 + syl_plugin_update_check_set_check_plugin_url @ 97 + syl_plugin_update_check_set_check_url @ 98 + syl_plugin_update_check_set_download_url @ 99 + syl_plugin_update_check_set_jump_plugin_url @ 100 + syl_plugin_update_check_set_jump_url @ 101 + syl_plugin_compose_attach_append @ 102 + syl_plugin_compose_attach_remove_all @ 103 + syl_plugin_compose_forward @ 104 + syl_plugin_compose_get_misc_hbox @ 105 + syl_plugin_compose_get_textview @ 106 + syl_plugin_compose_get_toolbar @ 107 + syl_plugin_compose_redirect @ 108 + syl_plugin_compose_reedit @ 109 + syl_plugin_compose_reply @ 110 + syl_plugin_compose_send @ 111 + syl_plugin_get_attach_list @ 112 + syl_plugin_main_window_get_toolbar @ 113 diff --git a/src/logwindow.c b/src/logwindow.c new file mode 100644 index 0000000..4cfae8c --- /dev/null +++ b/src/logwindow.c @@ -0,0 +1,341 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logwindow.h" +#include "prefs_common.h" +#include "utils.h" +#include "gtkutils.h" +#include "codeconv.h" + +#define TRIM_LINES 25 + +static LogWindow *logwindow; + +#if USE_THREADS +static GThread *main_thread; +#endif + +static void log_window_print_func (const gchar *str); +static void log_window_message_func (const gchar *str); +static void log_window_warning_func (const gchar *str); +static void log_window_error_func (const gchar *str); + +static void hide_cb (GtkWidget *widget, + LogWindow *logwin); +static gboolean key_pressed (GtkWidget *widget, + GdkEventKey *event, + LogWindow *logwin); + +LogWindow *log_window_create(void) +{ + LogWindow *logwin; + GtkWidget *window; + GtkWidget *scrolledwin; + GtkWidget *text; + GtkTextBuffer *buffer; + GtkTextIter iter; + + debug_print("Creating log window...\n"); + logwin = g_new0(LogWindow, 1); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), _("Protocol log")); + gtk_window_set_wmclass(GTK_WINDOW(window), "log_window", "Sylpheed"); + gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE); + gtk_widget_set_size_request(window, 520 * gtkut_get_dpi_multiplier(), 400 * gtkut_get_dpi_multiplier()); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(gtk_widget_hide_on_delete), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(key_pressed), logwin); + g_signal_connect(G_OBJECT(window), "hide", + G_CALLBACK(hide_cb), logwin); + gtk_widget_realize(window); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(window), scrolledwin); + gtk_widget_show(scrolledwin); + + text = gtk_text_view_new(); + gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); + gtk_text_buffer_get_start_iter(buffer, &iter); + gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE); + gtk_container_add(GTK_CONTAINER(scrolledwin), text); + gtk_widget_show(text); + + logwin->window = window; + logwin->scrolledwin = scrolledwin; + logwin->text = text; + logwin->lines = 1; + +#if USE_THREADS + logwin->aqueue = g_async_queue_new(); + + main_thread = g_thread_self(); + debug_print("main_thread = %p\n", main_thread); +#endif + + logwindow = logwin; + + return logwin; +} + +void log_window_init(LogWindow *logwin) +{ + GtkTextBuffer *buffer; + GdkColormap *colormap; + GdkColor color[3] = + {{0, 0, 0xafff, 0}, {0, 0xefff, 0, 0}, {0, 0xefff, 0, 0}}; + gboolean success[3]; + gint i; + + logwin->msg_color = color[0]; + logwin->warn_color = color[1]; + logwin->error_color = color[2]; + + colormap = gdk_window_get_colormap(logwin->window->window); + gdk_colormap_alloc_colors(colormap, color, 3, FALSE, TRUE, success); + + for (i = 0; i < 3; i++) { + if (success[i] == FALSE) { + GtkStyle *style; + + g_warning("LogWindow: color allocation failed\n"); + style = gtk_widget_get_style(logwin->window); + logwin->msg_color = logwin->warn_color = + logwin->error_color = style->black; + break; + } + } + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(logwin->text)); + gtk_text_buffer_create_tag(buffer, "message", + "foreground-gdk", &logwindow->msg_color, + NULL); + gtk_text_buffer_create_tag(buffer, "warn", + "foreground-gdk", &logwindow->warn_color, + NULL); + gtk_text_buffer_create_tag(buffer, "error", + "foreground-gdk", &logwindow->error_color, + NULL); + + set_log_ui_func_full(log_window_print_func, log_window_message_func, + log_window_warning_func, log_window_error_func, + log_window_flush); +} + +void log_window_show(LogWindow *logwin) +{ + GtkTextView *text = GTK_TEXT_VIEW(logwin->text); + GtkTextBuffer *buffer; + GtkTextMark *mark; + + buffer = gtk_text_view_get_buffer(text); + mark = gtk_text_buffer_get_mark(buffer, "end"); + gtk_text_view_scroll_mark_onscreen(text, mark); + + gtk_window_present(GTK_WINDOW(logwin->window)); +} + +static void log_window_append_real(const gchar *str, LogType type) +{ + GtkTextView *text; + GtkTextBuffer *buffer; + GtkTextIter iter; + GdkColor *color = NULL; + gchar *head = NULL; + const gchar *tag; + gint line_limit = prefs_common.logwin_line_limit; + + g_return_if_fail(logwindow != NULL); + +#if USE_THREADS + if (g_thread_self() != main_thread) { + g_fprintf(stderr, "log_window_append_real called from non-main thread (%p)\n", g_thread_self()); + return; + } +#endif + + gdk_threads_enter(); + + text = GTK_TEXT_VIEW(logwindow->text); + buffer = gtk_text_view_get_buffer(text); + + if (line_limit > 0 && logwindow->lines >= line_limit) { + GtkTextIter start, end; + + gtk_text_buffer_get_start_iter(buffer, &start); + end = start; + gtk_text_iter_forward_lines(&end, TRIM_LINES); + gtk_text_buffer_delete(buffer, &start, &end); + logwindow->lines = gtk_text_buffer_get_line_count(buffer); + } + + switch (type) { + case LOG_MSG: + color = &logwindow->msg_color; + tag = "message"; + head = "* "; + break; + case LOG_WARN: + color = &logwindow->warn_color; + tag = "warn"; + head = "** "; + break; + case LOG_ERROR: + color = &logwindow->error_color; + tag = "error"; + head = "*** "; + break; + default: + tag = NULL; + break; + } + + gtk_text_buffer_get_end_iter(buffer, &iter); + + if (head) + gtk_text_buffer_insert_with_tags_by_name + (buffer, &iter, head, -1, tag, NULL); + + if (!g_utf8_validate(str, -1, NULL)) { + gchar *str_; + + str_ = conv_utf8todisp(str, NULL); + if (str_) { + gtk_text_buffer_insert_with_tags_by_name + (buffer, &iter, str_, -1, tag, NULL); + g_free(str_); + } + } else { + gtk_text_buffer_insert_with_tags_by_name + (buffer, &iter, str, -1, tag, NULL); + } + + if (GTK_WIDGET_VISIBLE(text)) { + GtkTextMark *mark; + mark = gtk_text_buffer_get_mark(buffer, "end"); + gtk_text_view_scroll_mark_onscreen(text, mark); + } + + logwindow->lines++; + + gdk_threads_leave(); +} + +void log_window_append(const gchar *str, LogType type) +{ +#if USE_THREADS + if (g_thread_self() != main_thread) { + log_window_append_queue(str, type); + return; + } + + log_window_flush(); +#endif + log_window_append_real(str, type); +} + +typedef struct _LogData +{ + gchar *str; + LogType type; +} LogData; + +void log_window_append_queue(const gchar *str, LogType type) +{ +#if USE_THREADS + LogData *logdata; + + logdata = g_new(LogData, 1); + logdata->str = g_strdup(str); + logdata->type = type; + + g_async_queue_push(logwindow->aqueue, logdata); +#endif +} + +void log_window_flush(void) +{ +#if USE_THREADS + LogData *logdata; + + if (g_thread_self() != main_thread) { + g_fprintf(stderr, "log_window_flush called from non-main thread (%p)\n", g_thread_self()); + return; + } + + while ((logdata = g_async_queue_try_pop(logwindow->aqueue))) { + log_window_append_real(logdata->str, logdata->type); + g_free(logdata->str); + g_free(logdata); + } +#endif +} + +static void log_window_print_func(const gchar *str) +{ + log_window_append(str, LOG_NORMAL); +} + +static void log_window_message_func(const gchar *str) +{ + log_window_append(str, LOG_MSG); +} + +static void log_window_warning_func(const gchar *str) +{ + log_window_append(str, LOG_WARN); +} + +static void log_window_error_func(const gchar *str) +{ + log_window_append(str, LOG_ERROR); +} + +static void hide_cb(GtkWidget *widget, LogWindow *logwin) +{ +} + +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, + LogWindow *logwin) +{ + if (event && event->keyval == GDK_Escape) + gtk_widget_hide(logwin->window); + return FALSE; +} diff --git a/src/logwindow.h b/src/logwindow.h new file mode 100644 index 0000000..7e4a1d4 --- /dev/null +++ b/src/logwindow.h @@ -0,0 +1,62 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2009 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __LOGWINDOW_H__ +#define __LOGWINDOW_H__ + +#include +#include + +typedef struct _LogWindow LogWindow; + +typedef enum +{ + LOG_NORMAL, + LOG_MSG, + LOG_WARN, + LOG_ERROR +} LogType; + +struct _LogWindow +{ + GtkWidget *window; + GtkWidget *scrolledwin; + GtkWidget *text; + + GdkColor msg_color; + GdkColor warn_color; + GdkColor error_color; + + gint lines; + +#if USE_THREADS + GAsyncQueue *aqueue; +#endif +}; + +LogWindow *log_window_create(void); +void log_window_init(LogWindow *logwin); +void log_window_show(LogWindow *logwin); + +void log_window_append(const gchar *str, LogType type); +void log_window_append_queue(const gchar *str, LogType type); + +void log_window_flush(void); + +#endif /* __LOGWINDOW_H__ */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..77d8192 --- /dev/null +++ b/src/main.c @@ -0,0 +1,2083 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2016 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef G_OS_UNIX +# include +#endif + +#if HAVE_LOCALE_H +# include +#endif + +#if USE_GPGME +# include +#endif + +#include "main.h" +#include "mainwindow.h" +#include "folderview.h" +#include "summaryview.h" +#include "prefs_common.h" +#include "prefs_account.h" +#include "prefs_actions.h" +#include "prefs_display_header.h" +#include "account.h" +#include "account_dialog.h" +#include "procmsg.h" +#include "procheader.h" +#include "filter.h" +#include "send_message.h" +#include "inc.h" +#include "manage_window.h" +#include "alertpanel.h" +#include "inputdialog.h" +#include "statusbar.h" +#include "addressbook.h" +#include "addrindex.h" +#include "compose.h" +#include "logwindow.h" +#include "folder.h" +#include "setup.h" +#include "sylmain.h" +#include "utils.h" +#include "gtkutils.h" +#include "socket.h" +#include "stock_pixmap.h" +#include "trayicon.h" +#include "notificationwindow.h" +#include "plugin.h" +#include "plugin_manager.h" +#include "foldersel.h" +#include "update_check.h" +#include "colorlabel.h" + +#if USE_GPGME +# include "rfc2015.h" +#endif +#if USE_SSL +# include "ssl.h" +# include "sslmanager.h" +#endif + +#ifdef G_OS_WIN32 +# include +# include +# include +# include +#endif + +#include "version.h" + +gchar *prog_version; + +#ifdef G_OS_WIN32 +static gboolean init_console_done = FALSE; +#endif + +static gint lock_socket = -1; +static gint lock_socket_tag = 0; +static GIOChannel *lock_ch = NULL; +static gchar *instance_id = NULL; + +#if USE_THREADS +static GThread *main_thread; +#endif + +static struct RemoteCmd { + gboolean receive; + gboolean receive_all; + gboolean compose; + const gchar *compose_mailto; + GPtrArray *attach_files; + gboolean send; + gboolean status; + gboolean status_full; + GPtrArray *status_folders; + GPtrArray *status_full_folders; + gchar *open_msg; + gboolean configdir; + gboolean safe_mode; + gboolean exit; + gboolean restart; + gchar *argv0; +#ifdef G_OS_WIN32 + gushort ipcport; +#endif +} cmd; + +#define STATUSBAR_PUSH(mainwin, str) \ +{ \ + gtk_statusbar_push(GTK_STATUSBAR(mainwin->statusbar), \ + mainwin->mainwin_cid, str); \ + gtkut_widget_draw_now(mainwin->statusbar); \ +} + +#define STATUSBAR_POP(mainwin) \ +{ \ + gtk_statusbar_pop(GTK_STATUSBAR(mainwin->statusbar), \ + mainwin->mainwin_cid); \ +} + +#if defined(G_OS_WIN32) || defined(__APPLE__) +static void fix_font_setting (void); +#endif + +static void parse_cmd_opt (int argc, + char *argv[]); + +static void app_init (void); +static void parse_gtkrc_files (void); +static void setup_rc_dir (void); +static void check_gpg (void); +static void set_log_handlers (gboolean enable); +static void register_system_events (void); +static void plugin_init (void); + +static gchar *get_socket_name (void); +static gint prohibit_duplicate_launch (void); +static gint lock_socket_remove (void); +static gboolean lock_socket_input_cb (GIOChannel *source, + GIOCondition condition, + gpointer data); + +static void remote_command_exec (void); + +#if !defined(G_OS_WIN32) && !defined(__APPLE__) +static void migrate_old_config (void); +#endif + +static void open_compose_new (const gchar *address, + GPtrArray *attach_files); +static void open_message (const gchar *path); + +static void send_queue (void); + +#define MAKE_DIR_IF_NOT_EXIST(dir) \ +{ \ + if (!is_dir_exist(dir)) { \ + if (is_file_exist(dir)) { \ + alertpanel_warning \ + (_("File `%s' already exists.\n" \ + "Can't create folder."), \ + dir); \ + exit(1); \ + } \ + if (make_dir(dir) < 0) \ + exit(1); \ + } \ +} + +#define CHDIR_EXIT_IF_FAIL(dir, val) \ +{ \ + if (change_dir(dir) < 0) \ + exit(val); \ +} + +static void load_cb(GObject *obj, GModule *module, gpointer data) +{ + debug_print("load_cb: %p (%s), %p\n", module, module ? g_module_name(module) : "(null)", data); +} + +int main(int argc, char *argv[]) +{ + MainWindow *mainwin; + FolderView *folderview; + GdkPixbuf *icon; +#ifdef G_OS_WIN32 + GList *iconlist = NULL; +#endif + GObject *syl_app; + PrefsAccount *new_account = NULL; + gboolean first_run = FALSE; + gchar *path; + + app_init(); + parse_cmd_opt(argc, argv); + + /* check and create (unix domain) socket for remote operation */ + lock_socket = prohibit_duplicate_launch(); + if (lock_socket < 0) return 0; + + if (cmd.status || cmd.status_full) { + puts("0 Sylpheed not running."); + lock_socket_remove(); + return 0; + } + +#if USE_THREADS + gdk_threads_enter(); +#endif + gtk_set_locale(); + gtk_init(&argc, &argv); + + syl_app = syl_app_create(); + + gdk_rgb_init(); + gtk_widget_set_default_colormap(gdk_rgb_get_cmap()); + gtk_widget_set_default_visual(gdk_rgb_get_visual()); + + parse_gtkrc_files(); + setup_rc_dir(); + + if (is_file_exist("sylpheed.log")) { + if (rename_force("sylpheed.log", "sylpheed.log.bak") < 0) + FILE_OP_ERROR("sylpheed.log", "rename"); + } + set_log_file("sylpheed.log"); + + set_ui_update_func(gtkut_events_flush); + set_progress_func(main_window_progress_show); + set_input_query_password_func(input_dialog_query_password); +#if USE_SSL + ssl_init(); + ssl_set_verify_func(ssl_manager_verify_cert); +#endif + + CHDIR_EXIT_IF_FAIL(get_home_dir(), 1); + + prefs_common_read_config(); + filter_set_addressbook_func(addressbook_has_address); + filter_read_config(); + prefs_actions_read_config(); + prefs_display_header_read_config(); + colorlabel_read_config(); + + prefs_common.user_agent_str = g_strdup_printf + ("%s (GTK+ %d.%d.%d; %s)", + prog_version, + gtk_major_version, gtk_minor_version, gtk_micro_version, + TARGET_ALIAS); + +#if defined(G_OS_WIN32) || defined(__APPLE__) + fix_font_setting(); +#endif + + gtkut_stock_button_set_set_reverse(!prefs_common.comply_gnome_hig); + + check_gpg(); + + sock_set_io_timeout(prefs_common.io_timeout_secs); + + gtkut_widget_init(); + + gtkut_get_dpi(); + + path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "icons", NULL); + if (is_dir_exist(path)) { + debug_print("icon theme dir: %s\n", path); + stock_pixbuf_set_theme_dir(path); + } else { + g_free(path); + if (g_path_is_absolute(THEMEDIR)) + path = g_strconcat(THEMEDIR, NULL); + else + path = g_strconcat(get_startup_dir(), G_DIR_SEPARATOR_S, THEMEDIR, NULL); + if (is_dir_exist(path)) { + debug_print("icon theme dir: %s\n", path); + stock_pixbuf_set_theme_dir(path); + } + } + g_free(path); + +#ifdef G_OS_WIN32 + stock_pixbuf_gdk(NULL, STOCK_PIXMAP_SYLPHEED_32, &icon); + iconlist = g_list_append(iconlist, icon); + stock_pixbuf_gdk(NULL, STOCK_PIXMAP_SYLPHEED_SMALL, &icon); + iconlist = g_list_append(iconlist, icon); + gtk_window_set_default_icon_list(iconlist); + g_list_free(iconlist); +#else + stock_pixbuf_gdk(NULL, STOCK_PIXMAP_SYLPHEED, &icon); + gtk_window_set_default_icon(icon); +#endif + + mainwin = main_window_create + (prefs_common.sep_folder | prefs_common.sep_msg << 1); + folderview = mainwin->folderview; + + /* register the callback of socket input */ + if (lock_socket > 0) { + lock_ch = g_io_channel_unix_new(lock_socket); + lock_socket_tag = g_io_add_watch(lock_ch, + G_IO_IN|G_IO_PRI|G_IO_ERR, + lock_socket_input_cb, mainwin); + } + + set_log_handlers(TRUE); + + account_read_config_all(); + account_set_menu(); + main_window_reflect_prefs_all(); + + if (folder_read_list() < 0) { + first_run = TRUE; + setup_mailbox(); + folder_write_list(); + } + if (!account_get_list()) { + new_account = setup_account(); + } + + account_set_menu(); + main_window_reflect_prefs_all(); + + account_set_missing_folder(); + folder_set_missing_folders(); + folderview_set(folderview); + if (new_account && new_account->folder) + folder_write_list(); + + addressbook_read_file(); + + register_system_events(); + + inc_autocheck_timer_init(mainwin); + + plugin_init(); + + g_signal_emit_by_name(syl_app, "init-done"); + + if (first_run) { + setup_import_data(); + setup_import_addressbook(); + } + + remote_command_exec(); + +#if USE_UPDATE_CHECK + if (prefs_common.auto_update_check) + update_check(FALSE); +#endif + + gtk_main(); +#if USE_THREADS + gdk_threads_leave(); +#endif + + return 0; +} + +static void init_console(void) +{ +#ifdef G_OS_WIN32 + gint fd; + FILE *fp; + + if (init_console_done) + return; + + if (!AllocConsole()) { + g_warning("AllocConsole() failed\n"); + return; + } + + fd = _open_osfhandle((glong)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT); + _dup2(fd, 1); + fp = _fdopen(fd, "w"); + *stdout = *fp; + setvbuf(stdout, NULL, _IONBF, 0); + fd = _open_osfhandle((glong)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT); + _dup2(fd, 2); + fp = _fdopen(fd, "w"); + *stderr = *fp; + setvbuf(stderr, NULL, _IONBF, 0); + + init_console_done = TRUE; +#endif +} + +static void cleanup_console(void) +{ +#ifdef G_OS_WIN32 + FreeConsole(); +#endif +} + +#ifdef G_OS_WIN32 +static void read_ini_file(void) +{ + static gushort ipcport = REMOTE_CMD_PORT; + static gchar *confdir = NULL; + + static PrefParam param[] = { + {"ipcport", "50215", &ipcport, P_USHORT}, + {"configdir", NULL, &confdir, P_STRING}, + + {NULL, NULL, NULL, P_OTHER} + }; + + gchar *file; + + file = g_strconcat(get_startup_dir(), G_DIR_SEPARATOR_S, "sylpheed.ini", + NULL); + if (!is_file_exist(file)) { + g_free(file); + return; + } + + prefs_read_config(param, "Sylpheed", file, + conv_get_locale_charset_str()); + g_free(file); + + cmd.ipcport = ipcport; + if (confdir) { + set_rc_dir(confdir); + g_free(confdir); + confdir = NULL; + cmd.configdir = TRUE; + } +} +#endif /* G_OS_WIN32 */ + +#if defined(G_OS_WIN32) || defined(__APPLE__) +static void fix_font_setting(void) +{ + gchar *str = NULL; + gchar **strv = NULL; + gchar *style = NULL; + gchar *size = NULL; + gchar *suffix = NULL; + + if (!conv_is_ja_locale()) + return; + + if (prefs_common.textfont && + strcmp(prefs_common.textfont, DEFAULT_MESSAGE_FONT) != 0) { + guint len; + + if (gtkut_font_can_load(prefs_common.textfont)) { + debug_print("font '%s' load ok\n", prefs_common.textfont); + return; + } + debug_print("font '%s' load failed\n", prefs_common.textfont); + debug_print("fixing prefs_common.textfont setting\n"); + + strv = g_strsplit(prefs_common.textfont, " ", -1); + len = g_strv_length(strv); + if (len > 0) { + if (g_ascii_isdigit(strv[len - 1][0])) + size = g_strdup(strv[len - 1]); + if (len > 2) { + if (g_ascii_strcasecmp(strv[len - 2], "Bold") == 0) + style = g_strdup(strv[len - 2]); + } + } + g_strfreev(strv); + } + + if (style && size) + suffix = g_strconcat(style, " ", size, NULL); + else if (size) + suffix = g_strdup(size); + else + suffix = g_strdup("12"); + g_free(style); + g_free(size); +#ifdef G_OS_WIN32 + str = g_strconcat("MS Gothic ", suffix, NULL); +#else /* __APPLE__ */ + str = g_strconcat("Hiragino Kaku Gothic Pro ", suffix, NULL); +#endif + + if (!gtkut_font_can_load(str)) { +#ifdef G_OS_WIN32 + debug_print("font '%s' load failed\n", str); + g_free(str); + str = g_strconcat("\xef\xbc\xad\xef\xbc\xb3 \xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf ", suffix, NULL); + if (!gtkut_font_can_load(str)) { + debug_print("font '%s' load failed\n", str); + g_free(str); + str = NULL; + } +#else /* __APPLE__ */ + debug_print("font '%s' load failed\n", str); + g_free(str); + str = NULL; +#endif + } + + if (str) { + debug_print("font '%s' load ok\n", str); + g_free(prefs_common.textfont); + prefs_common.textfont = g_strdup(str); + g_free(str); + } else + g_warning("failed to load text font!"); + + g_free(suffix); +} +#endif + +static void parse_cmd_opt(int argc, char *argv[]) +{ + gint i; + + for (i = 1; i < argc; i++) { + if (!strncmp(argv[i], "--debug", 7)) { + init_console(); + set_debug_mode(TRUE); + } else if (!strncmp(argv[i], "--receive-all", 13)) + cmd.receive_all = TRUE; + else if (!strncmp(argv[i], "--receive", 9)) + cmd.receive = TRUE; + else if (!strncmp(argv[i], "--compose", 9)) { + const gchar *p = argv[i + 1]; + + cmd.compose = TRUE; + cmd.compose_mailto = NULL; + if (p && *p != '\0' && *p != '-') { + if (!strncmp(p, "mailto:", 7)) + cmd.compose_mailto = p + 7; + else + cmd.compose_mailto = p; + i++; + } + } else if (!strncmp(argv[i], "--attach", 8)) { + const gchar *p = argv[i + 1]; + gchar *file; + + while (p && *p != '\0' && *p != '-') { + if (!cmd.attach_files) + cmd.attach_files = g_ptr_array_new(); + if (!g_path_is_absolute(p)) + file = g_strconcat(get_startup_dir(), + G_DIR_SEPARATOR_S, + p, NULL); + else + file = g_strdup(p); + g_ptr_array_add(cmd.attach_files, file); + i++; + p = argv[i + 1]; + } + } else if (!strncmp(argv[i], "--send", 6)) { + cmd.send = TRUE; + } else if (!strncmp(argv[i], "--version", 9)) { + puts("Sylpheed version " VERSION); + exit(0); + } else if (!strncmp(argv[i], "--status-full", 13)) { + const gchar *p = argv[i + 1]; + + cmd.status_full = TRUE; + while (p && *p != '\0' && *p != '-') { + if (!cmd.status_full_folders) + cmd.status_full_folders = + g_ptr_array_new(); + g_ptr_array_add(cmd.status_full_folders, + g_strdup(p)); + i++; + p = argv[i + 1]; + } + } else if (!strncmp(argv[i], "--status", 8)) { + const gchar *p = argv[i + 1]; + + cmd.status = TRUE; + while (p && *p != '\0' && *p != '-') { + if (!cmd.status_folders) + cmd.status_folders = g_ptr_array_new(); + g_ptr_array_add(cmd.status_folders, + g_strdup(p)); + i++; + p = argv[i + 1]; + } + } else if (!strncmp(argv[i], "--open", 6)) { + const gchar *p = argv[i + 1]; + + if (p && *p != '\0' && *p != '-') { + if (cmd.open_msg) + g_free(cmd.open_msg); + cmd.open_msg = g_locale_to_utf8 + (p, -1, NULL, NULL, NULL); + i++; + } + } else if (!strncmp(argv[i], "--configdir", 11)) { + const gchar *p = argv[i + 1]; + + if (p && *p != '\0' && *p != '-') { + /* this must only be done at startup */ +#ifdef G_OS_WIN32 + gchar *utf8dir; + + utf8dir = g_locale_to_utf8 + (p, -1, NULL, NULL, NULL); + if (utf8dir) { + set_rc_dir(utf8dir); + g_free(utf8dir); + } else + set_rc_dir(p); +#else + set_rc_dir(p); +#endif + cmd.configdir = TRUE; + i++; + } +#ifdef G_OS_WIN32 + } else if (!strncmp(argv[i], "--ipcport", 9)) { + if (argv[i + 1]) { + cmd.ipcport = atoi(argv[i + 1]); + i++; + } +#endif + } else if (!strncmp(argv[i], "--instance-id", 13)) { + if (argv[i + 1]) { + instance_id = g_locale_to_utf8 + (argv[i + 1], -1, NULL, NULL, NULL); + i++; + } + } else if (!strncmp(argv[i], "--safe-mode", 11)) { + cmd.safe_mode = TRUE; + } else if (!strncmp(argv[i], "--dpi", 5)) { + if (argv[i + 1]) { + gdouble dpi; + + dpi = strtod(argv[i + 1], NULL); + if (dpi > 0.0) + gtkut_set_dpi(dpi); + i++; + } + } else if (!strncmp(argv[i], "--exit", 6)) { + cmd.exit = TRUE; + } else if (!strncmp(argv[i], "--help", 6)) { + init_console(); + + g_print(_("Usage: %s [OPTIONS ...] [URL]\n"), + g_basename(argv[0])); + + g_print("%s\n", _(" --compose [mailto URL] open composition window")); + g_print("%s\n", _(" --attach file1 [file2]...\n" + " open composition window with specified files\n" + " attached")); + g_print("%s\n", _(" --receive receive new messages")); + g_print("%s\n", _(" --receive-all receive new messages of all accounts")); + g_print("%s\n", _(" --send send all queued messages")); + g_print("%s\n", _(" --status [folder]... show the total number of messages")); + g_print("%s\n", _(" --status-full [folder]...\n" + " show the status of each folder")); + g_print("%s\n", _(" --open folderid/msgnum open existing message in a new window")); + g_print("%s\n", _(" --open open an rfc822 message file in a new window")); + g_print("%s\n", _(" --configdir dirname specify directory which stores configuration files")); +#ifdef G_OS_WIN32 + g_print("%s\n", _(" --ipcport portnum specify port for IPC remote commands")); +#endif + g_print("%s\n", _(" --dpi dpinum force DPI")); + g_print("%s\n", _(" --exit exit Sylpheed")); + g_print("%s\n", _(" --debug debug mode")); + g_print("%s\n", _(" --safe-mode safe mode")); + g_print("%s\n", _(" --help display this help and exit")); + g_print("%s\n", _(" --version output version information and exit")); + +#ifdef G_OS_WIN32 + g_print("\n"); + g_print(_("Press any key...")); + _getch(); +#endif + + cleanup_console(); + exit(1); + } else { + /* file or URL */ + const gchar *p = argv[i]; + + if (p && *p != '\0') { + if (!strncmp(p, "mailto:", 7)) { + cmd.compose = TRUE; + cmd.compose_mailto = p + 7; + } else { + if (cmd.open_msg) + g_free(cmd.open_msg); + cmd.open_msg = g_locale_to_utf8 + (p, -1, NULL, NULL, NULL); + } + } + } + } + + if (cmd.attach_files && cmd.compose == FALSE) { + cmd.compose = TRUE; + cmd.compose_mailto = NULL; + } + + cmd.argv0 = g_locale_to_utf8(argv[0], -1, NULL, NULL, NULL); + if (!cmd.argv0) + cmd.argv0 = g_strdup(argv[0]); +} + +static gint get_queued_message_num(void) +{ + FolderItem *queue; + + queue = folder_get_default_queue(); + if (!queue) return -1; + + folder_item_scan(queue); + return queue->total; +} + +#if USE_THREADS +/* enables recursive locking with gdk_thread_enter / gdk_threads_leave */ +static GStaticRecMutex syl_mutex = G_STATIC_REC_MUTEX_INIT; + +static void thread_enter_func(void) +{ + g_static_rec_mutex_lock(&syl_mutex); +#if 0 + syl_mutex_lock_count++; + if (syl_mutex_lock_count > 1) + g_print("enter: syl_mutex_lock_count: %d\n", syl_mutex_lock_count); +#endif +} + +static void thread_leave_func(void) +{ +#if 0 + syl_mutex_lock_count--; + if (syl_mutex_lock_count > 0) + g_print("leave: syl_mutex_lock_count: %d\n", syl_mutex_lock_count); +#endif + g_static_rec_mutex_unlock(&syl_mutex); +} + +static void event_loop_iteration_func(void) +{ + if (g_thread_self() != main_thread) { + g_fprintf(stderr, "event_loop_iteration_func called from non-main thread (%p)\n", g_thread_self()); + g_usleep(10000); + return; + } + gtk_main_iteration(); +} +#endif + +static void app_init(void) +{ +#if USE_THREADS + if (!g_thread_supported()) + g_thread_init(NULL); + if (!g_thread_supported()) + g_error("g_thread is not supported by glib."); + else { + gdk_threads_set_lock_functions(thread_enter_func, + thread_leave_func); + gdk_threads_init(); + main_thread = g_thread_self(); + } +#endif + syl_init(); + +#if USE_THREADS + set_event_loop_func(event_loop_iteration_func); +#endif + prog_version = PROG_VERSION; + +#ifdef G_OS_WIN32 + read_ini_file(); +#endif +} + +static void parse_gtkrc_files(void) +{ + gchar *userrc; + + /* parse gtkrc files */ + userrc = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".gtkrc-2.0", + NULL); + gtk_rc_parse(userrc); + g_free(userrc); + userrc = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".gtk", + G_DIR_SEPARATOR_S, "gtkrc-2.0", NULL); + gtk_rc_parse(userrc); + g_free(userrc); + userrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "gtkrc", NULL); + gtk_rc_parse(userrc); + g_free(userrc); + + userrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, MENU_RC, NULL); + gtk_accel_map_load(userrc); + g_free(userrc); +} + +static void setup_rc_dir(void) +{ +#if !defined(G_OS_WIN32) && !defined(__APPLE__) + CHDIR_EXIT_IF_FAIL(get_home_dir(), 1); + + /* backup if old rc file exists */ + if (!cmd.configdir && is_file_exist(RC_DIR)) { + if (rename_force(RC_DIR, RC_DIR ".bak") < 0) + FILE_OP_ERROR(RC_DIR, "rename"); + } + + /* migration from ~/.sylpheed to ~/.sylpheed-2.0 */ + if (!cmd.configdir && !is_dir_exist(RC_DIR)) { + const gchar *envstr; + AlertValue val; + + /* check for filename encoding */ + if (conv_get_locale_charset() != C_UTF_8) { + envstr = g_getenv("G_FILENAME_ENCODING"); + if (!envstr) + envstr = g_getenv("G_BROKEN_FILENAMES"); + if (!envstr) { + val = alertpanel(_("Filename encoding"), + _("The locale encoding is not UTF-8, but the environmental variable G_FILENAME_ENCODING is not set.\n" + "If the locale encoding is used for file name or directory name, it will not work correctly.\n" + "In that case, you must set the following environmental variable (see README for detail):\n" + "\n" + "\tG_FILENAME_ENCODING=@locale\n" + "\n" + "Continue?"), + GTK_STOCK_OK, GTK_STOCK_QUIT, + NULL); + if (G_ALERTDEFAULT != val) + exit(1); + } + } + + if (make_dir(RC_DIR) < 0) + exit(1); + if (is_dir_exist(OLD_RC_DIR)) + migrate_old_config(); + } +#endif /* !G_OS_WIN32 && !__APPLE__ */ + + syl_setup_rc_dir(); +} + +static void app_restart(void) +{ + gchar *cmdline; + GError *error = NULL; +#ifdef G_OS_WIN32 + if (cmd.configdir) { + cmdline = g_strdup_printf("\"%s\"%s --configdir \"%s\" --ipcport %d", + cmd.argv0, + get_debug_mode() ? " --debug" : "", + get_rc_dir(), + cmd.ipcport); + } else { + cmdline = g_strdup_printf("\"%s\"%s --ipcport %d", + cmd.argv0, + get_debug_mode() ? " --debug" : "", + cmd.ipcport); + } +#else + if (cmd.configdir) { + cmdline = g_strdup_printf("\"%s\"%s --configdir \"%s\"", + cmd.argv0, + get_debug_mode() ? " --debug" : "", + get_rc_dir()); + } else { + cmdline = g_strdup_printf("\"%s\"%s", + cmd.argv0, + get_debug_mode() ? " --debug" : ""); + } +#endif + if (!g_spawn_command_line_async(cmdline, &error)) { + alertpanel_error("restart failed\n'%s'\n%s", cmdline, error->message); + g_error_free(error); + } + g_free(cmdline); +} + +void app_will_restart(gboolean force) +{ + cmd.restart = TRUE; + app_will_exit(force); + /* canceled */ + cmd.restart = FALSE; +} + +void app_will_exit(gboolean force) +{ + MainWindow *mainwin; + gchar *filename; + static gboolean on_exit = FALSE; + GList *cur; + + if (on_exit) + return; + on_exit = TRUE; + + mainwin = main_window_get(); + + if (!force && compose_get_compose_list()) { + if (alertpanel(_("Notice"), + _("Composing message exists. Really quit?"), + GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL) + != G_ALERTDEFAULT) { + on_exit = FALSE; + return; + } + manage_window_focus_in(mainwin->window, NULL, NULL); + } + + if (!force && + prefs_common.warn_queued_on_exit && get_queued_message_num() > 0) { + if (alertpanel(_("Queued messages"), + _("Some unsent messages are queued. Exit now?"), + GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL) + != G_ALERTDEFAULT) { + on_exit = FALSE; + return; + } + manage_window_focus_in(mainwin->window, NULL, NULL); + } + + if (force) + g_signal_emit_by_name(syl_app_get(), "app-force-exit"); + g_signal_emit_by_name(syl_app_get(), "app-exit"); + + inc_autocheck_timer_remove(); + + if (prefs_common.clean_on_exit) + main_window_empty_trash(mainwin, + !force && prefs_common.ask_on_clean); + + for (cur = account_get_list(); cur != NULL; cur = cur->next) { + PrefsAccount *ac = (PrefsAccount *)cur->data; + if (ac->protocol == A_IMAP4 && ac->imap_clear_cache_on_exit && + ac->folder) + procmsg_remove_all_cached_messages(FOLDER(ac->folder)); + } + + syl_plugin_unload_all(); + + trayicon_destroy(mainwin->tray_icon); + + /* save all state before exiting */ + summary_write_cache(mainwin->summaryview); + main_window_get_size(mainwin); + main_window_get_position(mainwin); + syl_save_all_state(); + addressbook_export_to_file(); + + filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, MENU_RC, NULL); + gtk_accel_map_save(filename); + g_free(filename); + + /* remove temporary files, close log file, socket cleanup */ +#if USE_SSL + ssl_done(); +#endif + syl_cleanup(); + lock_socket_remove(); + +#ifdef USE_UPDATE_CHECK_PLUGIN +#ifdef G_OS_WIN32 + cur = gtk_window_list_toplevels(); + g_list_foreach(cur, (GFunc)gtk_widget_hide, NULL); + g_list_free(cur); + update_check_spawn_plugin_updater(); +#endif +#endif + + cleanup_console(); + + if (gtk_main_level() > 0) + gtk_main_quit(); + + if (cmd.restart) + app_restart(); + + exit(0); +} + +#if 0 +#if USE_GPGME +static void idle_function_for_gpgme(void) +{ + while (gtk_events_pending()) + gtk_main_iteration(); +} +#endif /* USE_GPGME */ +#endif /* 0 */ + +static void check_gpg(void) +{ +#if USE_GPGME + const gchar *version; + gpgme_error_t err = 0; + + version = gpgme_check_version("1.0.0"); + if (version) { + debug_print("GPGME Version: %s\n", version); + err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP); + if (err) + debug_print("gpgme_engine_check_version: %s\n", + gpgme_strerror(err)); + } + + if (version && !err) { + /* Also does some gpgme init */ + gpgme_engine_info_t engineInfo; + +#if HAVE_LOCALE_H + gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL)); + gpgme_set_locale(NULL, LC_MESSAGES, + setlocale(LC_MESSAGES, NULL)); +#endif + + if (!gpgme_get_engine_info(&engineInfo)) { + while (engineInfo) { + debug_print("GPGME Protocol: %s\n Version: %s\n", + gpgme_get_protocol_name + (engineInfo->protocol), + engineInfo->version ? + engineInfo->version : "(unknown)"); + engineInfo = engineInfo->next; + } + } + + procmsg_set_decrypt_message_func + (rfc2015_open_message_decrypted); + procmsg_set_auto_decrypt_message(TRUE); + } else { + rfc2015_disable_all(); + + if (prefs_common.gpg_warning) { + AlertValue val; + + val = alertpanel_message_with_disable + (_("Warning"), + _("GnuPG is not installed properly, or its version is too old.\n" + "OpenPGP support disabled."), + ALERT_WARNING); + if (val & G_ALERTDISABLE) + prefs_common.gpg_warning = FALSE; + } + } + /* FIXME: This function went away. We can either block until gpgme + * operations finish (currently implemented) or register callbacks + * with the gtk main loop via the gpgme io callback interface instead. + * + * gpgme_register_idle(idle_function_for_gpgme); + */ +#endif +} + +static void default_log_func(const gchar *log_domain, GLogLevelFlags log_level, + const gchar *message, gpointer user_data) +{ + gchar *prefix = ""; + gchar *file_prefix = ""; + LogType level = LOG_NORMAL; + gchar *str; + const gchar *message_; + + switch (log_level) { + case G_LOG_LEVEL_ERROR: + prefix = "ERROR"; + file_prefix = "*** "; + level = LOG_ERROR; + break; + case G_LOG_LEVEL_CRITICAL: + prefix = "CRITICAL"; + file_prefix = "** "; + level = LOG_WARN; + break; + case G_LOG_LEVEL_WARNING: + prefix = "WARNING"; + file_prefix = "** "; + level = LOG_WARN; + break; + case G_LOG_LEVEL_MESSAGE: + prefix = "Message"; + file_prefix = "* "; + level = LOG_MSG; + break; + case G_LOG_LEVEL_INFO: + prefix = "INFO"; + file_prefix = "* "; + level = LOG_MSG; + break; + case G_LOG_LEVEL_DEBUG: + prefix = "DEBUG"; + break; + default: + prefix = "LOG"; + break; + } + + if (!message) + message_ = "(NULL) message"; + else + message_ = message; + if (log_domain) + str = g_strconcat(log_domain, "-", prefix, ": ", message_, "\n", + NULL); + else + str = g_strconcat(prefix, ": ", message_, "\n", NULL); + log_window_append(str, level); + log_write(str, file_prefix); + g_free(str); + + g_log_default_handler(log_domain, log_level, message, user_data); +} + +static void set_log_handlers(gboolean enable) +{ +#if GLIB_CHECK_VERSION(2, 6, 0) + if (enable) + g_log_set_default_handler(default_log_func, NULL); + else + g_log_set_default_handler(g_log_default_handler, NULL); +#else + static guint handler_id[4] = {0, 0, 0, 0}; + + if (enable) { + handler_id[0] = g_log_set_handler + ("GLib", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL + | G_LOG_FLAG_RECURSION, default_log_func, NULL); + handler_id[1] = g_log_set_handler + ("Gtk", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL + | G_LOG_FLAG_RECURSION, default_log_func, NULL); + handler_id[2] = g_log_set_handler + ("LibSylph", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL + | G_LOG_FLAG_RECURSION, default_log_func, NULL); + handler_id[3] = g_log_set_handler + ("Sylpheed", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL + | G_LOG_FLAG_RECURSION, default_log_func, NULL); + } else { + g_log_remove_handler("GLib", handler_id[0]); + g_log_remove_handler("Gtk", handler_id[1]); + g_log_remove_handler("LibSylph", handler_id[2]); + g_log_remove_handler("Sylpheed", handler_id[3]); + handler_id[0] = 0; + handler_id[1] = 0; + handler_id[2] = 0; + handler_id[3] = 0; + } +#endif +} + +#ifdef G_OS_WIN32 + +#if !GTK_CHECK_VERSION(2, 14, 0) +static UINT taskbar_created_msg; +#endif + +static BOOL WINAPI +ctrl_handler(DWORD dwctrltype) +{ + log_print("ctrl_handler: received %d\n", dwctrltype); + app_will_exit(TRUE); + + return TRUE; +} + +static LRESULT CALLBACK +wndproc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + switch (message) { + case WM_POWERBROADCAST: + debug_print("WM_POWERBROADCAST received: wparam = %d\n", + wparam); + if (wparam == PBT_APMSUSPEND || wparam == PBT_APMSTANDBY) { + debug_print("suspend now\n"); + inc_autocheck_timer_remove(); + } else if (wparam == PBT_APMRESUMESUSPEND || + wparam == PBT_APMRESUMESTANDBY) { + debug_print("resume now\n"); + inc_autocheck_timer_set(); + } + break; + case WM_ENDSESSION: + if (wparam == 1) { + log_print("WM_ENDSESSION received: system is quitting\n"); + app_will_exit(TRUE); + } + break; + default: +#if !GTK_CHECK_VERSION(2, 14, 0) + if (message == taskbar_created_msg) { + debug_print("TaskbarCreated received\n"); + + /* recreate tray icon */ + { + MainWindow *mainwin = main_window_get(); + if (mainwin && mainwin->tray_icon && + gtk_status_icon_get_visible(mainwin->tray_icon->status_icon)) { + trayicon_hide(mainwin->tray_icon); + trayicon_show(mainwin->tray_icon); + } + } + } +#endif + break; + } + + return DefWindowProc(hwnd, message, wparam, lparam); +} + +static void register_system_events(void) +{ + WNDCLASS wclass; + static HWND hwnd = NULL; + static BOOL ctrl_handler_set = FALSE; + ATOM klass; + HINSTANCE hmodule = GetModuleHandle(NULL); + + if (init_console_done && !ctrl_handler_set) { + debug_print("register_system_events(): SetConsoleCtrlHandler\n"); + ctrl_handler_set = SetConsoleCtrlHandler(ctrl_handler, TRUE); + if (!ctrl_handler_set) + g_warning("SetConsoleCtrlHandler() failed\n"); + } + + if (hwnd) + return; + +#if !GTK_CHECK_VERSION(2, 14, 0) + taskbar_created_msg = RegisterWindowMessage("TaskbarCreated"); +#endif + + debug_print("register_system_events(): RegisterClass\n"); + + memset(&wclass, 0, sizeof(WNDCLASS)); + wclass.lpszClassName = "sylpheed-observer"; + wclass.lpfnWndProc = wndproc; + wclass.hInstance = hmodule; + + klass = RegisterClass(&wclass); + if (!klass) + return; + + hwnd = CreateWindow(MAKEINTRESOURCE(klass), NULL, WS_POPUP, + 0, 0, 1, 1, NULL, NULL, hmodule, NULL); + if (!hwnd) + UnregisterClass(MAKEINTRESOURCE(klass), hmodule); +} +#else /* G_OS_WIN32 */ +static void sig_handler(gint signum) +{ + debug_print("signal %d received\n", signum); + + switch (signum) { + case SIGHUP: + case SIGINT: + case SIGTERM: + case SIGQUIT: + app_will_exit(TRUE); + break; + default: + break; + } +} + +static void register_system_events(void) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sig_handler; + sa.sa_flags = SA_RESTART; + + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGHUP); + sigaddset(&sa.sa_mask, SIGINT); + sigaddset(&sa.sa_mask, SIGTERM); + sigaddset(&sa.sa_mask, SIGQUIT); + sigaddset(&sa.sa_mask, SIGPIPE); + + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); +} +#endif + +#define ADD_SYM(sym) syl_plugin_add_symbol(#sym, sym) + +static void plugin_init(void) +{ + MainWindow *mainwin; + gchar *path; + + mainwin = main_window_get(); + + STATUSBAR_PUSH(mainwin, _("Loading plug-ins...")); + + if (syl_plugin_init_lib() != 0) { + STATUSBAR_POP(mainwin); + return; + } + + if (cmd.safe_mode) { + debug_print("plugin_init: safe mode enabled, skipping plug-in loading.\n"); + STATUSBAR_POP(mainwin); + return; + } + + ADD_SYM(prog_version); + ADD_SYM(app_will_exit); + + ADD_SYM(main_window_lock); + ADD_SYM(main_window_unlock); + ADD_SYM(main_window_get); + ADD_SYM(main_window_popup); + + syl_plugin_add_symbol("main_window_menu_factory", + mainwin->menu_factory); + syl_plugin_add_symbol("main_window_toolbar", mainwin->toolbar); + syl_plugin_add_symbol("main_window_statusbar", mainwin->statusbar); + + ADD_SYM(folderview_get); + ADD_SYM(folderview_add_sub_widget); + ADD_SYM(folderview_select); + ADD_SYM(folderview_unselect); + ADD_SYM(folderview_select_next_unread); + ADD_SYM(folderview_get_selected_item); + ADD_SYM(folderview_check_new); + ADD_SYM(folderview_check_new_item); + ADD_SYM(folderview_check_new_all); + ADD_SYM(folderview_update_item); + ADD_SYM(folderview_update_item_foreach); + ADD_SYM(folderview_update_all_updated); + ADD_SYM(folderview_check_new_selected); + + syl_plugin_add_symbol("folderview_mail_popup_factory", + mainwin->folderview->mail_factory); + syl_plugin_add_symbol("folderview_imap_popup_factory", + mainwin->folderview->imap_factory); + syl_plugin_add_symbol("folderview_news_popup_factory", + mainwin->folderview->news_factory); + + syl_plugin_add_symbol("summaryview", mainwin->summaryview); + syl_plugin_add_symbol("summaryview_popup_factory", + mainwin->summaryview->popupfactory); + + ADD_SYM(summary_select_by_msgnum); + ADD_SYM(summary_select_by_msginfo); + ADD_SYM(summary_lock); + ADD_SYM(summary_unlock); + ADD_SYM(summary_is_locked); + ADD_SYM(summary_is_read_locked); + ADD_SYM(summary_write_lock); + ADD_SYM(summary_write_unlock); + ADD_SYM(summary_is_write_locked); + ADD_SYM(summary_get_current_folder); + ADD_SYM(summary_get_selection_type); + ADD_SYM(summary_get_selected_msg_list); + ADD_SYM(summary_get_msg_list); + ADD_SYM(summary_show_queued_msgs); + ADD_SYM(summary_redisplay_msg); + ADD_SYM(summary_open_msg); + ADD_SYM(summary_view_source); + ADD_SYM(summary_reedit); + ADD_SYM(summary_update_selected_rows); + ADD_SYM(summary_update_by_msgnum); + + ADD_SYM(messageview_create_with_new_window); + ADD_SYM(messageview_show); + + ADD_SYM(compose_new); + ADD_SYM(compose_reply); + ADD_SYM(compose_forward); + ADD_SYM(compose_redirect); + ADD_SYM(compose_reedit); + ADD_SYM(compose_entry_set); + ADD_SYM(compose_entry_append); + ADD_SYM(compose_entry_get_text); + ADD_SYM(compose_lock); + ADD_SYM(compose_unlock); + ADD_SYM(compose_get_toolbar); + ADD_SYM(compose_get_misc_hbox); + ADD_SYM(compose_get_textview); + ADD_SYM(compose_attach_append); + ADD_SYM(compose_attach_remove_all); + ADD_SYM(compose_get_attach_list); + ADD_SYM(compose_send); + + ADD_SYM(foldersel_folder_sel); + ADD_SYM(foldersel_folder_sel_full); + + ADD_SYM(input_dialog); + ADD_SYM(input_dialog_with_invisible); + + ADD_SYM(manage_window_set_transient); + ADD_SYM(manage_window_signals_connect); + ADD_SYM(manage_window_get_focus_window); + + ADD_SYM(inc_mail); + ADD_SYM(inc_is_active); + ADD_SYM(inc_lock); + ADD_SYM(inc_unlock); + +#if USE_UPDATE_CHECK + ADD_SYM(update_check); + ADD_SYM(update_check_set_check_url); + ADD_SYM(update_check_get_check_url); + ADD_SYM(update_check_set_download_url); + ADD_SYM(update_check_get_download_url); + ADD_SYM(update_check_set_jump_url); + ADD_SYM(update_check_get_jump_url); +#ifdef USE_UPDATE_CHECK_PLUGIN + ADD_SYM(update_check_set_check_plugin_url); + ADD_SYM(update_check_get_check_plugin_url); + ADD_SYM(update_check_set_jump_plugin_url); + ADD_SYM(update_check_get_jump_plugin_url); +#endif /* USE_UPDATE_CHECK_PLUGIN */ +#endif + + ADD_SYM(alertpanel_full); + ADD_SYM(alertpanel); + ADD_SYM(alertpanel_message); + ADD_SYM(alertpanel_message_with_disable); + + ADD_SYM(send_message); + ADD_SYM(send_message_queue_all); + ADD_SYM(send_message_set_reply_flag); + ADD_SYM(send_message_set_forward_flags); + + ADD_SYM(notification_window_open); + ADD_SYM(notification_window_set_message); + ADD_SYM(notification_window_close); + + syl_plugin_signal_connect("plugin-load", G_CALLBACK(load_cb), NULL); + + /* loading plug-ins from user plug-in directory */ + path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, PLUGIN_DIR, NULL); + syl_plugin_load_all(path); + g_free(path); + + /* loading plug-ins from system plug-in directory */ +#ifdef G_OS_WIN32 + path = g_strconcat(get_startup_dir(), G_DIR_SEPARATOR_S, PLUGIN_DIR, + NULL); + syl_plugin_load_all(path); + g_free(path); +#elif defined(__APPLE__) + path = g_strconcat(get_startup_dir(), G_DIR_SEPARATOR_S, + "Contents" G_DIR_SEPARATOR_S + "Resources" G_DIR_SEPARATOR_S + "lib" G_DIR_SEPARATOR_S + "sylpheed" G_DIR_SEPARATOR_S + PLUGIN_DIR, + NULL); + syl_plugin_load_all(path); + g_free(path); +#else + syl_plugin_load_all(PLUGINDIR); +#endif + + STATUSBAR_POP(mainwin); +} + +static gchar *get_socket_name(void) +{ + static gchar *filename = NULL; + + if (filename == NULL) { + filename = g_strdup_printf("%s%c%s-%d", + g_get_tmp_dir(), G_DIR_SEPARATOR, + instance_id ? instance_id : "sylpheed", +#if HAVE_GETUID + getuid()); +#else + 0); +#endif + } + + return filename; +} + +static gint prohibit_duplicate_launch(void) +{ + gint sock; + +#ifdef G_OS_WIN32 + HANDLE hmutex; + const gchar *ins_id = instance_id ? instance_id : "Sylpheed"; + gushort port = cmd.ipcport ? cmd.ipcport : REMOTE_CMD_PORT; + + debug_print("prohibit_duplicate_launch: checking mutex: %s\n", ins_id); + hmutex = CreateMutexA(NULL, FALSE, ins_id); + if (!hmutex) { + g_warning("cannot create Mutex: %s\n", ins_id); + return -1; + } + if (GetLastError() != ERROR_ALREADY_EXISTS) { + debug_print("prohibit_duplicate_launch: creating socket: port %d\n", port); + sock = fd_open_inet(port); + if (sock < 0) + return 0; + return sock; + } + + debug_print("prohibit_duplicate_launch: connecting to socket: port %d\n", port); + sock = fd_connect_inet(port); + if (sock < 0) + return -1; +#else + gchar *path; + + path = get_socket_name(); + debug_print("prohibit_duplicate_launch: checking socket: %s\n", path); + sock = fd_connect_unix(path); + if (sock < 0) { + debug_print("prohibit_duplicate_launch: creating socket: %s\n", path); + g_unlink(path); + return fd_open_unix(path); + } +#endif + + /* remote command mode */ + + debug_print(_("another Sylpheed is already running.\n")); + + if (cmd.receive_all) + fd_write_all(sock, "receive_all\n", 12); + else if (cmd.receive) + fd_write_all(sock, "receive\n", 8); + else if (cmd.compose && cmd.attach_files) { + gchar *str, *compose_str; + gint i; + + if (cmd.compose_mailto) + compose_str = g_strdup_printf("compose_attach %s\n", + cmd.compose_mailto); + else + compose_str = g_strdup("compose_attach\n"); + + fd_write_all(sock, compose_str, strlen(compose_str)); + g_free(compose_str); + + for (i = 0; i < cmd.attach_files->len; i++) { + str = g_ptr_array_index(cmd.attach_files, i); + fd_write_all(sock, str, strlen(str)); + fd_write_all(sock, "\n", 1); + } + + fd_write_all(sock, ".\n", 2); + } else if (cmd.compose) { + gchar *compose_str; + + if (cmd.compose_mailto) + compose_str = g_strdup_printf + ("compose %s\n", cmd.compose_mailto); + else + compose_str = g_strdup("compose\n"); + + fd_write_all(sock, compose_str, strlen(compose_str)); + g_free(compose_str); + } else if (cmd.send) { + fd_write_all(sock, "send\n", 5); + } else if (cmd.status || cmd.status_full) { + gchar buf[BUFFSIZE]; + gint i; + const gchar *command; + GPtrArray *folders; + gchar *folder; + + command = cmd.status_full ? "status-full\n" : "status\n"; + folders = cmd.status_full ? cmd.status_full_folders : + cmd.status_folders; + + fd_write_all(sock, command, strlen(command)); + for (i = 0; folders && i < folders->len; ++i) { + folder = g_ptr_array_index(folders, i); + fd_write_all(sock, folder, strlen(folder)); + fd_write_all(sock, "\n", 1); + } + fd_write_all(sock, ".\n", 2); + for (;;) { + if (fd_gets(sock, buf, sizeof(buf)) <= 0) break; + if (!strncmp(buf, ".\n", 2)) break; + fputs(buf, stdout); + } + } else if (cmd.open_msg) { + gchar *str; + + str = g_strdup_printf("open %s\n", cmd.open_msg); + fd_write_all(sock, str, strlen(str)); + g_free(str); + } else if (cmd.exit) { + fd_write_all(sock, "exit\n", 5); + } else { +#ifdef G_OS_WIN32 + HWND hwnd; + + fd_write_all(sock, "popup\n", 6); + if (fd_read(sock, (gchar *)&hwnd, sizeof(hwnd)) == sizeof(hwnd)) + SetForegroundWindow(hwnd); +#else + fd_write_all(sock, "popup\n", 6); +#endif + } + + fd_close(sock); + return -1; +} + +static gint lock_socket_remove(void) +{ +#ifndef G_OS_WIN32 + gchar *filename; +#endif + + if (lock_socket < 0) return -1; + + if (lock_socket_tag > 0) + g_source_remove(lock_socket_tag); + if (lock_ch) { + g_io_channel_shutdown(lock_ch, FALSE, NULL); + g_io_channel_unref(lock_ch); + lock_ch = NULL; + } + +#ifndef G_OS_WIN32 + filename = get_socket_name(); + debug_print("lock_socket_remove: removing socket: %s\n", filename); + g_unlink(filename); +#endif + + return 0; +} + +static GPtrArray *get_folder_item_list(gint sock) +{ + gchar buf[BUFFSIZE]; + FolderItem *item; + GPtrArray *folders = NULL; + + for (;;) { + if (fd_gets(sock, buf, sizeof(buf)) <= 0) break; + if (!strncmp(buf, ".\n", 2)) break; + strretchomp(buf); + if (!folders) folders = g_ptr_array_new(); + item = folder_find_item_from_identifier(buf); + if (item) + g_ptr_array_add(folders, item); + else + g_warning("no such folder: %s\n", buf); + } + + return folders; +} + +static gboolean lock_socket_input_cb(GIOChannel *source, GIOCondition condition, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + gint fd, sock; + gchar buf[BUFFSIZE]; + +#if USE_THREADS + gdk_threads_enter(); +#endif + + fd = g_io_channel_unix_get_fd(source); + sock = fd_accept(fd); + if (fd_gets(sock, buf, sizeof(buf)) <= 0) { + fd_close(sock); +#if USE_THREADS + gdk_threads_leave(); +#endif + return TRUE; + } + + if (!strncmp(buf, "popup", 5)) { +#ifdef G_OS_WIN32 + HWND hwnd; + + hwnd = (HWND)gdk_win32_drawable_get_handle + (GDK_DRAWABLE(mainwin->window->window)); + fd_write(sock, (gchar *)&hwnd, sizeof(hwnd)); + if (mainwin->window_hidden) + main_window_popup(mainwin); +#else + main_window_popup(mainwin); +#endif + } else if (!strncmp(buf, "receive_all", 11)) { + main_window_popup(mainwin); + if (!gtkut_window_modal_exist()) + inc_all_account_mail(mainwin, FALSE); + } else if (!strncmp(buf, "receive", 7)) { + main_window_popup(mainwin); + if (!gtkut_window_modal_exist()) + inc_mail(mainwin); + } else if (!strncmp(buf, "compose_attach", 14)) { + GPtrArray *files; + gchar *mailto; + + mailto = g_strdup(buf + strlen("compose_attach") + 1); + files = g_ptr_array_new(); + while (fd_gets(sock, buf, sizeof(buf)) > 0) { + if (buf[0] == '.' && buf[1] == '\n') break; + strretchomp(buf); + g_ptr_array_add(files, g_strdup(buf)); + } + open_compose_new(mailto, files); + ptr_array_free_strings(files); + g_ptr_array_free(files, TRUE); + g_free(mailto); + } else if (!strncmp(buf, "compose", 7)) { + open_compose_new(buf + strlen("compose") + 1, NULL); + } else if (!strncmp(buf, "send", 4)) { + send_queue(); + } else if (!strncmp(buf, "status-full", 11) || + !strncmp(buf, "status", 6)) { + gchar *status; + GPtrArray *folders; + + folders = get_folder_item_list(sock); + status = folder_get_status + (folders, !strncmp(buf, "status-full", 11)); + fd_write_all(sock, status, strlen(status)); + fd_write_all(sock, ".\n", 2); + g_free(status); + if (folders) g_ptr_array_free(folders, TRUE); + } else if (!strncmp(buf, "open", 4)) { + strretchomp(buf); + if (strlen(buf) < 6 || buf[4] != ' ') { + fd_close(sock); +#if USE_THREADS + gdk_threads_leave(); +#endif + return TRUE; + } + open_message(buf + 5); + } else if (!strncmp(buf, "exit", 4)) { + fd_close(sock); + app_will_exit(TRUE); + } + + fd_close(sock); + +#if USE_THREADS + gdk_threads_leave(); +#endif + + return TRUE; +} + +static void remote_command_exec(void) +{ + MainWindow *mainwin; + + mainwin = main_window_get(); + + if (prefs_common.open_inbox_on_startup) { + FolderItem *item; + PrefsAccount *ac; + + ac = account_get_default(); + if (!ac) + ac = cur_account; + item = ac && ac->inbox + ? folder_find_item_from_identifier(ac->inbox) + : folder_get_default_inbox(); + folderview_select(mainwin->folderview, item); + } + + if (!gtkut_window_modal_exist()) { + if (cmd.compose) + open_compose_new(cmd.compose_mailto, cmd.attach_files); + + if (cmd.open_msg) + open_message(cmd.open_msg); + + if (cmd.receive_all) + inc_all_account_mail(mainwin, FALSE); + else if (prefs_common.chk_on_startup) + inc_all_account_mail(mainwin, TRUE); + else if (cmd.receive) + inc_mail(mainwin); + + if (cmd.send) + send_queue(); + } + + if (cmd.attach_files) { + ptr_array_free_strings(cmd.attach_files); + g_ptr_array_free(cmd.attach_files, TRUE); + cmd.attach_files = NULL; + } + if (cmd.status_folders) { + g_ptr_array_free(cmd.status_folders, TRUE); + cmd.status_folders = NULL; + } + if (cmd.status_full_folders) { + g_ptr_array_free(cmd.status_full_folders, TRUE); + cmd.status_full_folders = NULL; + } + if (cmd.open_msg) { + g_free(cmd.open_msg); + cmd.open_msg = NULL; + } + if (cmd.exit) { + app_will_exit(TRUE); + } +} + +#if !defined(G_OS_WIN32) && !defined(__APPLE__) +static void migrate_old_config(void) +{ + GDir *dir; + const gchar *dir_name; + GPatternSpec *pspec; + + if (alertpanel(_("Migration of configuration"), + _("The previous version of configuration found.\n" + "Do you want to migrate it?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL) != G_ALERTDEFAULT) + return; + + debug_print("Migrating old configuration...\n"); + +#define COPY_FILE(rcfile) \ + if (is_file_exist(OLD_RC_DIR G_DIR_SEPARATOR_S rcfile)) { \ + conv_copy_file(OLD_RC_DIR G_DIR_SEPARATOR_S rcfile, \ + RC_DIR G_DIR_SEPARATOR_S rcfile, \ + conv_get_locale_charset_str()); \ + } + + COPY_FILE(ACCOUNT_RC); + COPY_FILE(ACTIONS_RC); + COPY_FILE(COMMON_RC); + COPY_FILE(CUSTOM_HEADER_RC); + COPY_FILE(DISPLAY_HEADER_RC); + COPY_FILE(FILTER_HEADER_RC); + COPY_FILE(COMMAND_HISTORY); + +#undef COPY_FILE + + if (is_file_exist(OLD_RC_DIR G_DIR_SEPARATOR_S FILTER_LIST)) + copy_file(OLD_RC_DIR G_DIR_SEPARATOR_S FILTER_LIST, + RC_DIR G_DIR_SEPARATOR_S FILTER_LIST, FALSE); + if (is_file_exist(OLD_RC_DIR G_DIR_SEPARATOR_S FOLDER_LIST)) + copy_file(OLD_RC_DIR G_DIR_SEPARATOR_S FOLDER_LIST, + RC_DIR G_DIR_SEPARATOR_S FOLDER_LIST, FALSE); + if (is_file_exist(OLD_RC_DIR G_DIR_SEPARATOR_S "mime.types")) + copy_file(OLD_RC_DIR G_DIR_SEPARATOR_S "mime.types", + RC_DIR G_DIR_SEPARATOR_S "mime.types", FALSE); + + if (is_dir_exist(OLD_RC_DIR G_DIR_SEPARATOR_S TEMPLATE_DIR)) + conv_copy_dir(OLD_RC_DIR G_DIR_SEPARATOR_S TEMPLATE_DIR, + RC_DIR G_DIR_SEPARATOR_S TEMPLATE_DIR, + conv_get_locale_charset_str()); + if (is_dir_exist(OLD_RC_DIR G_DIR_SEPARATOR_S UIDL_DIR)) + copy_dir(OLD_RC_DIR G_DIR_SEPARATOR_S UIDL_DIR, + RC_DIR G_DIR_SEPARATOR_S UIDL_DIR); + + if (!is_file_exist(OLD_RC_DIR G_DIR_SEPARATOR_S ADDRESSBOOK_INDEX_FILE)) + return; + + if ((dir = g_dir_open(OLD_RC_DIR, 0, NULL)) == NULL) { + g_warning("failed to open directory: %s\n", OLD_RC_DIR); + return; + } + + pspec = g_pattern_spec_new("addrbook-*.xml"); + + while ((dir_name = g_dir_read_name(dir)) != NULL) { + if (g_pattern_match_string(pspec, dir_name)) { + gchar *old_file; + gchar *new_file; + + old_file = g_strconcat(OLD_RC_DIR G_DIR_SEPARATOR_S, + dir_name, NULL); + new_file = g_strconcat(RC_DIR G_DIR_SEPARATOR_S, + dir_name, NULL); + copy_file(old_file, new_file, FALSE); + g_free(new_file); + g_free(old_file); + } + } + + g_pattern_spec_free(pspec); + g_dir_close(dir); +} +#endif /* !G_OS_WIN32 && !__APPLE__ */ + +static void open_compose_new(const gchar *address, GPtrArray *attach_files) +{ + gchar *utf8addr = NULL; +#ifdef G_OS_WIN32 + GPtrArray *utf8files = NULL; +#endif + + if (gtkut_window_modal_exist()) + return; + + if (address) { + utf8addr = g_locale_to_utf8(address, -1, NULL, NULL, NULL); + if (utf8addr) + g_strstrip(utf8addr); + debug_print("open compose: %s\n", utf8addr ? utf8addr : ""); + } + +#ifdef G_OS_WIN32 + if (attach_files) { + gint i; + gchar *file, *utf8file; + + utf8files = g_ptr_array_new(); + for (i = 0; i < attach_files->len; i++) { + file = g_ptr_array_index(attach_files, i); + utf8file = g_locale_to_utf8(file, -1, NULL, NULL, NULL); + if (utf8file) + g_ptr_array_add(utf8files, utf8file); + } + } + + compose_new(NULL, NULL, utf8addr, utf8files); + if (utf8files) { + ptr_array_free_strings(utf8files); + g_ptr_array_free(utf8files, TRUE); + } +#else + compose_new(NULL, NULL, utf8addr, attach_files); +#endif + + g_free(utf8addr); +} + +static void open_message_file(const gchar *file) +{ + MsgInfo *msginfo; + MsgFlags flags = {0}; + MessageView *msgview; + + g_return_if_fail(file != NULL); + + debug_print("open message file: %s\n", file); + + if (!is_file_exist(file) || get_file_size(file) <= 0) { + debug_print("file not found: %s\n", file); + return; + } + + msginfo = procheader_parse_file(file, flags, FALSE); + if (msginfo) { + msginfo->file_path = g_strdup(file); + msgview = messageview_create_with_new_window(); + messageview_show(msgview, msginfo, FALSE); + procmsg_msginfo_free(msginfo); + } else + debug_print("cannot open message: %s\n", file); +} + +static void open_message(const gchar *path) +{ + gchar *fid; + gchar *msg; + gint num; + FolderItem *item; + MsgInfo *msginfo; + MessageView *msgview; + gchar *file; + + g_return_if_fail(path != NULL); + + if (gtkut_window_modal_exist()) + return; + + debug_print("open message: %s\n", path); + + if (!strncmp(path, "file:", 5)) { + file = g_filename_from_uri(path, NULL, NULL); + open_message_file(file); + g_free(file); + return; + } else if (g_path_is_absolute(path)) { + open_message_file(path); + return; + } + + /* relative path, or folder identifier */ + + fid = g_path_get_dirname(path); + msg = g_path_get_basename(path); + num = to_number(msg); + item = folder_find_item_from_identifier(fid); + + if (num > 0 && item) { + debug_print("open folder id: %s (msg %d)\n", fid, num); + msginfo = folder_item_get_msginfo(item, num); + if (msginfo) { + msgview = messageview_create_with_new_window(); + messageview_show(msgview, msginfo, FALSE); + procmsg_msginfo_free(msginfo); + g_free(msg); + g_free(fid); + return; + } else + debug_print("message %d not found\n", num); + } + + g_free(msg); + g_free(fid); + + /* relative path */ + + file = g_strconcat(get_startup_dir(), G_DIR_SEPARATOR_S, path, NULL); + open_message_file(file); + g_free(file); +} + +static void send_queue(void) +{ + GList *list; + + if (gtkut_window_modal_exist()) + return; + if (!main_window_toggle_online_if_offline(main_window_get())) + return; + + for (list = folder_get_list(); list != NULL; list = list->next) { + Folder *folder = list->data; + + if (folder->queue) { + gint ret; + + ret = send_message_queue_all(folder->queue, + prefs_common.savemsg, + prefs_common.filter_sent); + statusbar_pop_all(); + if (ret > 0) + folder_item_scan(folder->queue); + } + } + + folderview_update_all_updated(TRUE); + main_window_set_menu_sensitive(main_window_get()); + main_window_set_toolbar_sensitive(main_window_get()); +} diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..9ad54c9 --- /dev/null +++ b/src/main.h @@ -0,0 +1,30 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2006 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __MAIN_H__ +#define __MAIN_H__ + +#include + +extern gchar *prog_version; + +void app_will_exit (gboolean force); +void app_will_restart (gboolean force); + +#endif /* __MAIN_H__ */ diff --git a/src/mainwindow.c b/src/mainwindow.c new file mode 100644 index 0000000..bc7e713 --- /dev/null +++ b/src/mainwindow.c @@ -0,0 +1,4276 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2017 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "mainwindow.h" +#include "folderview.h" +#include "foldersel.h" +#include "summaryview.h" +#include "quick_search.h" +#include "query_search.h" +#include "messageview.h" +#include "mimeview.h" +#include "message_search.h" +#include "headerview.h" +#include "menu.h" +#include "stock_pixmap.h" +#include "folder.h" +#include "inc.h" +#include "rpop3.h" +#include "compose.h" +#include "procmsg.h" +#include "send_message.h" +#include "import.h" +#include "export.h" +#include "prefs_common.h" +#include "prefs_common_dialog.h" +#include "prefs_filter.h" +#include "prefs_actions.h" +#include "prefs_account.h" +#include "prefs_summary_column.h" +#include "prefs_template.h" +#include "prefs_search_folder.h" +#include "prefs_toolbar.h" +#include "plugin_manager.h" +#include "action.h" +#include "account.h" +#include "account_dialog.h" +#include "addressbook.h" +#include "logwindow.h" +#include "manage_window.h" +#include "alertpanel.h" +#include "statusbar.h" +#include "inputdialog.h" +#include "trayicon.h" +#include "printing.h" +#include "utils.h" +#include "gtkutils.h" +#include "codeconv.h" +#include "about.h" +#include "manual.h" +#include "update_check.h" +#include "setup.h" +#include "plugin.h" +#include "version.h" + +#define AC_LABEL_WIDTH 240 + +#define STATUSBAR_PUSH(mainwin, str) \ +{ \ + gtk_statusbar_push(GTK_STATUSBAR(mainwin->statusbar), \ + mainwin->mainwin_cid, str); \ + gtkut_widget_draw_now(mainwin->statusbar); \ +} + +#define STATUSBAR_POP(mainwin) \ +{ \ + gtk_statusbar_pop(GTK_STATUSBAR(mainwin->statusbar), \ + mainwin->mainwin_cid); \ +} + +/* list of all instantiated MainWindow */ +static GList *mainwin_list = NULL; + +static GdkCursor *watch_cursor; + +static void main_window_menu_callback_block (MainWindow *mainwin); +static void main_window_menu_callback_unblock (MainWindow *mainwin); + +static void main_window_show_cur_account (MainWindow *mainwin); + +static void main_window_set_toolbar_button_visibility + (MainWindow *mainwin); + +static void main_window_set_widgets (MainWindow *mainwin, + LayoutType layout, + SeparateType type); +static GtkWidget *main_window_toolbar_create (MainWindow *mainwin); +static GtkWidget *main_window_toolbar_create_from_list + (MainWindow *mainwin, + GList *item_list); +static void main_window_toolbar_toggle_menu_set_active + (MainWindow *mainwin, + ToolbarStyle style); + +/* callback functions */ +static void toolbar_inc_cb (GtkWidget *widget, + gpointer data); +static void toolbar_inc_all_cb (GtkWidget *widget, + gpointer data); +static void toolbar_rpop3_cb (GtkWidget *widget, + gpointer data); +static void toolbar_send_cb (GtkWidget *widget, + gpointer data); + +static void toolbar_compose_cb (GtkWidget *widget, + gpointer data); +static void toolbar_reply_cb (GtkWidget *widget, + gpointer data); +static void toolbar_reply_to_all_cb (GtkWidget *widget, + gpointer data); +static void toolbar_forward_cb (GtkWidget *widget, + gpointer data); + +static void toolbar_delete_cb (GtkWidget *widget, + gpointer data); +static void toolbar_junk_cb (GtkWidget *widget, + gpointer data); +static void toolbar_notjunk_cb (GtkWidget *widget, + gpointer data); +static void toolbar_exec_cb (GtkWidget *widget, + gpointer data); + +static void toolbar_next_unread_cb (GtkWidget *widget, + gpointer data); +static void toolbar_prev_unread_cb (GtkWidget *widget, + gpointer data); +static void toolbar_address_cb (GtkWidget *widget, + gpointer data); +static void toolbar_search_cb (GtkWidget *widget, + gpointer data); +static void toolbar_print_cb (GtkWidget *widget, + gpointer data); +static void toolbar_stop_cb (GtkWidget *widget, + gpointer data); +static void toolbar_prefs_common_cb (GtkWidget *widget, + gpointer data); +static void toolbar_prefs_account_cb (GtkWidget *widget, + gpointer data); + +static gboolean toolbar_button_pressed (GtkWidget *widget, + GdkEventButton *event, + gpointer data); + +static void online_switch_clicked (GtkWidget *widget, + gpointer data); +static gboolean ac_label_button_pressed (GtkWidget *widget, + GdkEventButton *event, + gpointer data); +static void ac_menu_popup_closed (GtkMenuShell *menu_shell, + gpointer data); + +static gboolean main_window_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); + +static gint main_window_close_cb (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gint folder_window_close_cb (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gint message_window_close_cb (GtkWidget *widget, + GdkEventAny *event, + gpointer data); + +static void main_window_size_allocate_cb (GtkWidget *widget, + GtkAllocation *allocation, + gpointer data); +static void folder_window_size_allocate_cb (GtkWidget *widget, + GtkAllocation *allocation, + gpointer data); +static void message_window_size_allocate_cb (GtkWidget *widget, + GtkAllocation *allocation, + gpointer data); + +static gboolean main_window_window_state_cb (GtkWidget *widget, + GdkEventWindowState *event, + gpointer data); +static gboolean main_window_visibility_notify_cb(GtkWidget *widget, + GdkEventVisibility *event, + gpointer data); + +static void new_folder_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void rename_folder_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void move_folder_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void delete_folder_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void update_folderview_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void add_mailbox_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void remove_mailbox_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void rebuild_tree_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void import_mail_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void export_mail_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void empty_trash_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void save_as_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +#if GTK_CHECK_VERSION(2, 10, 0) +static void page_setup_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +#endif +static void print_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void toggle_offline_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void app_exit_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void search_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void toggle_folder_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void toggle_message_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void toggle_toolbar_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void toggle_searchbar_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void toggle_statusbar_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void toolbar_customize_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void change_layout_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void separate_widget_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void addressbook_open_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void log_window_show_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void inc_mail_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void inc_all_account_mail_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void inc_stop_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void rpop3_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void send_queue_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void compose_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void reply_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void open_msg_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void view_source_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void show_all_header_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void move_to_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void copy_to_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void delete_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void mark_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void unmark_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void mark_as_unread_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void mark_as_read_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void mark_thread_as_read_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void mark_all_read_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void junk_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void reedit_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void add_address_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void set_charset_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void thread_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void expand_threads_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void collapse_threads_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void set_display_item_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void sort_summary_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void sort_summary_type_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void attract_by_subject_cb(MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void delete_duplicated_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void concat_partial_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void filter_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void filter_junk_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void execute_summary_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void update_summary_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void open_config_folder_cb(MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void open_attachments_folder_cb + (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void prev_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void next_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void prev_unread_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void next_unread_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void prev_new_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void next_new_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void prev_marked_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void next_marked_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void prev_labeled_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void next_labeled_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void goto_folder_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void copy_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void allsel_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void select_thread_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void create_filter_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void prefs_common_open_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void prefs_filter_open_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void prefs_template_open_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void plugin_manager_open_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +#ifndef G_OS_WIN32 +static void prefs_actions_open_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +#endif +static void prefs_account_open_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void new_account_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); + +static void account_selector_menu_cb (GtkMenuItem *menuitem, + gpointer data); +static void account_receive_menu_cb (GtkMenuItem *menuitem, + gpointer data); + +static void manual_open_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void faq_open_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +static void help_cmdline_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +#if USE_UPDATE_CHECK +static void update_check_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); +#ifdef USE_UPDATE_CHECK_PLUGIN +static void update_check_plugin_cb(MainWindow *mainwin, guint action, + GtkWidget *widget); +#endif +#endif + +static void scan_tree_func (Folder *folder, + FolderItem *item, + gpointer data); + +static GtkItemFactoryEntry mainwin_entries[] = +{ + {N_("/_File"), NULL, NULL, 0, ""}, + {N_("/_File/_Folder"), NULL, NULL, 0, ""}, + {N_("/_File/_Folder/Create _new folder..."), + NULL, new_folder_cb, 0, NULL}, + {N_("/_File/_Folder/_Rename folder..."),NULL, rename_folder_cb, 0, NULL}, + {N_("/_File/_Folder/_Move folder..."), NULL, move_folder_cb, 0, NULL}, + {N_("/_File/_Folder/_Delete folder"), NULL, delete_folder_cb, 0, NULL}, + {N_("/_File/_Mailbox"), NULL, NULL, 0, ""}, + {N_("/_File/_Mailbox/Add _mailbox..."), NULL, add_mailbox_cb, 0, NULL}, + {N_("/_File/_Mailbox/_Remove mailbox"), NULL, remove_mailbox_cb, 0, NULL}, + {N_("/_File/_Mailbox/---"), NULL, NULL, 0, ""}, + {N_("/_File/_Mailbox/_Check for new messages"), + NULL, update_folderview_cb, 0, NULL}, + {N_("/_File/_Mailbox/Check for new messages in _all mailboxes"), + NULL, update_folderview_cb, 1, NULL}, + {N_("/_File/_Mailbox/---"), NULL, NULL, 0, ""}, + {N_("/_File/_Mailbox/R_ebuild folder tree"), + NULL, rebuild_tree_cb, 0, NULL}, + {N_("/_File/---"), NULL, NULL, 0, ""}, + {N_("/_File/_Import mail data..."), NULL, import_mail_cb, 0, NULL}, + {N_("/_File/_Export mail data..."), NULL, export_mail_cb, 0, NULL}, + {N_("/_File/---"), NULL, NULL, 0, ""}, + {N_("/_File/Empty all _trash"), NULL, empty_trash_cb, 0, NULL}, + {N_("/_File/---"), NULL, NULL, 0, ""}, + {N_("/_File/_Save as..."), "S", save_as_cb, 0, NULL}, + {N_("/_File/---"), NULL, NULL, 0, ""}, +#if GTK_CHECK_VERSION(2, 10, 0) + {N_("/_File/Page set_up..."), NULL, page_setup_cb, 0, NULL}, +#endif + {N_("/_File/_Print..."), NULL, print_cb, 0, NULL}, + {N_("/_File/---"), NULL, NULL, 0, ""}, + {N_("/_File/_Work offline"), NULL, toggle_offline_cb, 0, ""}, + {N_("/_File/---"), NULL, NULL, 0, ""}, + /* {N_("/_File/_Close"), "W", app_exit_cb, 0, NULL}, */ + {N_("/_File/E_xit"), "Q", app_exit_cb, 0, NULL}, + + {N_("/_Edit"), NULL, NULL, 0, ""}, + {N_("/_Edit/_Copy"), "C", copy_cb, 0, NULL}, + {N_("/_Edit/Select _all"), "A", allsel_cb, 0, NULL}, + {N_("/_Edit/Select _thread"), NULL, select_thread_cb, 0, NULL}, + {N_("/_Edit/---"), NULL, NULL, 0, ""}, + {N_("/_Edit/_Find in current message..."), + "F", search_cb, 0, NULL}, + {N_("/_Edit/_Search messages..."), "F", search_cb, 1, NULL}, + {N_("/_Edit/_Quick search"), "S", search_cb, 2, NULL}, + + {N_("/_View"), NULL, NULL, 0, ""}, + {N_("/_View/Show or hi_de"), NULL, NULL, 0, ""}, + {N_("/_View/Show or hi_de/_Folder tree"), + NULL, toggle_folder_cb, 0, ""}, + {N_("/_View/Show or hi_de/_Message view"), + "V", toggle_message_cb, 0, ""}, + {N_("/_View/Show or hi_de/_Toolbar"), + NULL, NULL, 0, ""}, + {N_("/_View/Show or hi_de/_Toolbar/Icon _and text"), + NULL, toggle_toolbar_cb, TOOLBAR_BOTH, ""}, + {N_("/_View/Show or hi_de/_Toolbar/Text at the _right of icon"), + NULL, toggle_toolbar_cb, TOOLBAR_BOTH_HORIZ, "/View/Show or hide/Toolbar/Icon and text"}, + {N_("/_View/Show or hi_de/_Toolbar/_Icon"), + NULL, toggle_toolbar_cb, TOOLBAR_ICON, "/View/Show or hide/Toolbar/Icon and text"}, + {N_("/_View/Show or hi_de/_Toolbar/_Text"), + NULL, toggle_toolbar_cb, TOOLBAR_TEXT, "/View/Show or hide/Toolbar/Icon and text"}, + {N_("/_View/Show or hi_de/_Toolbar/_None"), + NULL, toggle_toolbar_cb, TOOLBAR_NONE, "/View/Show or hide/Toolbar/Icon and text"}, + {N_("/_View/Show or hi_de/_Search bar"), + NULL, toggle_searchbar_cb, 0, ""}, + {N_("/_View/Show or hi_de/Status _bar"), + NULL, toggle_statusbar_cb, 0, ""}, + {N_("/_View/_Customize toolbar..."), NULL, toolbar_customize_cb, 0, NULL}, + {N_("/_View/---"), NULL, NULL, 0, ""}, + {N_("/_View/Layou_t"), NULL, NULL, 0, ""}, + {N_("/_View/Layou_t/_Normal"), NULL, change_layout_cb, LAYOUT_NORMAL, ""}, + {N_("/_View/Layou_t/_Vertical"), NULL, change_layout_cb, LAYOUT_VERTICAL, "/View/Layout/Normal"}, + {N_("/_View/Separate f_older tree"), NULL, separate_widget_cb, SEPARATE_FOLDER, ""}, + {N_("/_View/Separate _message view"), NULL, separate_widget_cb, SEPARATE_MESSAGE, ""}, + {N_("/_View/---"), NULL, NULL, 0, ""}, + {N_("/_View/_Sort"), NULL, NULL, 0, ""}, + {N_("/_View/_Sort/by _number"), NULL, sort_summary_cb, SORT_BY_NUMBER, ""}, + {N_("/_View/_Sort/by s_ize"), NULL, sort_summary_cb, SORT_BY_SIZE, "/View/Sort/by number"}, + {N_("/_View/_Sort/by _date"), NULL, sort_summary_cb, SORT_BY_DATE, "/View/Sort/by number"}, + {N_("/_View/_Sort/by t_hread date"), NULL, sort_summary_cb, SORT_BY_TDATE, "/View/Sort/by number"}, + {N_("/_View/_Sort/by _from"), NULL, sort_summary_cb, SORT_BY_FROM, "/View/Sort/by number"}, + {N_("/_View/_Sort/by _recipient"), NULL, sort_summary_cb, SORT_BY_TO, "/View/Sort/by number"}, + {N_("/_View/_Sort/by _subject"), NULL, sort_summary_cb, SORT_BY_SUBJECT, "/View/Sort/by number"}, + {N_("/_View/_Sort/by _color label"), + NULL, sort_summary_cb, SORT_BY_LABEL, "/View/Sort/by number"}, + {N_("/_View/_Sort/by _mark"), NULL, sort_summary_cb, SORT_BY_MARK, "/View/Sort/by number"}, + {N_("/_View/_Sort/by _unread"), NULL, sort_summary_cb, SORT_BY_UNREAD, "/View/Sort/by number"}, + {N_("/_View/_Sort/by a_ttachment"), + NULL, sort_summary_cb, SORT_BY_MIME, "/View/Sort/by number"}, + {N_("/_View/_Sort/D_on't sort"), NULL, sort_summary_cb, SORT_BY_NONE, "/View/Sort/by number"}, + {N_("/_View/_Sort/---"), NULL, NULL, 0, ""}, + {N_("/_View/_Sort/Ascending"), NULL, sort_summary_type_cb, SORT_ASCENDING, ""}, + {N_("/_View/_Sort/Descending"), NULL, sort_summary_type_cb, SORT_DESCENDING, "/View/Sort/Ascending"}, + {N_("/_View/_Sort/---"), NULL, NULL, 0, ""}, + {N_("/_View/_Sort/_Attract by subject"), + NULL, attract_by_subject_cb, 0, NULL}, + {N_("/_View/Th_read view"), "T", thread_cb, 0, ""}, + {N_("/_View/E_xpand all threads"), NULL, expand_threads_cb, 0, NULL}, + {N_("/_View/Co_llapse all threads"), NULL, collapse_threads_cb, 0, NULL}, + {N_("/_View/Set display _item..."), NULL, set_display_item_cb, 0, NULL}, + + {N_("/_View/---"), NULL, NULL, 0, ""}, + {N_("/_View/_Go to"), NULL, NULL, 0, ""}, + {N_("/_View/_Go to/_Prev message"), "P", prev_cb, 0, NULL}, + {N_("/_View/_Go to/_Next message"), "N", next_cb, 0, NULL}, + {N_("/_View/_Go to/---"), NULL, NULL, 0, ""}, + {N_("/_View/_Go to/P_rev unread message"), + "P", prev_unread_cb, 0, NULL}, + {N_("/_View/_Go to/N_ext unread message"), + "N", next_unread_cb, 0, NULL}, + {N_("/_View/_Go to/---"), NULL, NULL, 0, ""}, + {N_("/_View/_Go to/Prev ne_w message"), NULL, prev_new_cb, 0, NULL}, + {N_("/_View/_Go to/Ne_xt new message"), NULL, next_new_cb, 0, NULL}, + {N_("/_View/_Go to/---"), NULL, NULL, 0, ""}, + {N_("/_View/_Go to/Prev _marked message"), + NULL, prev_marked_cb, 0, NULL}, + {N_("/_View/_Go to/Next m_arked message"), + NULL, next_marked_cb, 0, NULL}, + {N_("/_View/_Go to/---"), NULL, NULL, 0, ""}, + {N_("/_View/_Go to/Prev _labeled message"), + NULL, prev_labeled_cb, 0, NULL}, + {N_("/_View/_Go to/Next la_beled message"), + NULL, next_labeled_cb, 0, NULL}, + {N_("/_View/_Go to/---"), NULL, NULL, 0, ""}, + {N_("/_View/_Go to/Other _folder..."), "G", goto_folder_cb, 0, NULL}, + {N_("/_View/---"), NULL, NULL, 0, ""}, + +#define ENC_SEPARATOR \ + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""} +#define ENC_ACTION(action) \ + NULL, set_charset_cb, action, "/View/Character encoding/Auto detect" + + {N_("/_View/Character _encoding"), NULL, NULL, 0, ""}, + {N_("/_View/Character _encoding/_Auto detect"), + NULL, set_charset_cb, C_AUTO, ""}, + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""}, + {N_("/_View/Character _encoding/7bit ascii (US-ASC_II)"), + ENC_ACTION(C_US_ASCII)}, + {N_("/_View/Character _encoding/Unicode (_UTF-8)"), + ENC_ACTION(C_UTF_8)}, + ENC_SEPARATOR, + + {N_("/_View/Character _encoding/Western European (ISO-8859-_1)"), + ENC_ACTION(C_ISO_8859_1)}, + {N_("/_View/Character _encoding/Western European (ISO-8859-15)"), + ENC_ACTION(C_ISO_8859_15)}, + {N_("/_View/Character _encoding/Western European (Windows-1252)"), + ENC_ACTION(C_WINDOWS_1252)}, + ENC_SEPARATOR, + + {N_("/_View/Character _encoding/Central European (ISO-8859-_2)"), + ENC_ACTION(C_ISO_8859_2)}, + ENC_SEPARATOR, + + {N_("/_View/Character _encoding/_Baltic (ISO-8859-13)"), + ENC_ACTION(C_ISO_8859_13)}, + {N_("/_View/Character _encoding/Baltic (ISO-8859-_4)"), + ENC_ACTION(C_ISO_8859_4)}, + {N_("/_View/Character _encoding/Baltic (Windows-1257)"), + ENC_ACTION(C_WINDOWS_1257)}, + ENC_SEPARATOR, + + {N_("/_View/Character _encoding/Greek (ISO-8859-_7)"), + ENC_ACTION(C_ISO_8859_7)}, + ENC_SEPARATOR, + + {N_("/_View/Character _encoding/Arabic (ISO-8859-_6)"), + ENC_ACTION(C_ISO_8859_6)}, + {N_("/_View/Character _encoding/Arabic (Windows-1256)"), + ENC_ACTION(C_CP1256)}, + ENC_SEPARATOR, + + {N_("/_View/Character _encoding/Hebrew (ISO-8859-_8)"), + ENC_ACTION(C_ISO_8859_8)}, + {N_("/_View/Character _encoding/Hebrew (Windows-1255)"), + ENC_ACTION(C_CP1255)}, + ENC_SEPARATOR, + + {N_("/_View/Character _encoding/Turkish (ISO-8859-_9)"), + ENC_ACTION(C_ISO_8859_9)}, + ENC_SEPARATOR, + + {N_("/_View/Character _encoding/Cyrillic (ISO-8859-_5)"), + ENC_ACTION(C_ISO_8859_5)}, + {N_("/_View/Character _encoding/Cyrillic (KOI8-_R)"), + ENC_ACTION(C_KOI8_R)}, + {N_("/_View/Character _encoding/Cyrillic (KOI8-U)"), + ENC_ACTION(C_KOI8_U)}, + {N_("/_View/Character _encoding/Cyrillic (Windows-1251)"), + ENC_ACTION(C_CP1251)}, + ENC_SEPARATOR, + + {N_("/_View/Character _encoding/Japanese (ISO-2022-_JP)"), + ENC_ACTION(C_ISO_2022_JP)}, + {N_("/_View/Character _encoding/Japanese (ISO-2022-JP-2)"), + ENC_ACTION(C_ISO_2022_JP_2)}, + {N_("/_View/Character _encoding/Japanese (_EUC-JP)"), + ENC_ACTION(C_EUC_JP)}, + {N_("/_View/Character _encoding/Japanese (_Shift__JIS)"), + ENC_ACTION(C_SHIFT_JIS)}, + ENC_SEPARATOR, + + {N_("/_View/Character _encoding/Simplified Chinese (_GB2312)"), + ENC_ACTION(C_GB2312)}, + {N_("/_View/Character _encoding/Simplified Chinese (GBK)"), + ENC_ACTION(C_GBK)}, + {N_("/_View/Character _encoding/Traditional Chinese (_Big5)"), + ENC_ACTION(C_BIG5)}, + {N_("/_View/Character _encoding/Traditional Chinese (EUC-_TW)"), + ENC_ACTION(C_EUC_TW)}, + {N_("/_View/Character _encoding/Chinese (ISO-2022-_CN)"), + ENC_ACTION(C_ISO_2022_CN)}, + ENC_SEPARATOR, + + {N_("/_View/Character _encoding/Korean (EUC-_KR)"), + ENC_ACTION(C_EUC_KR)}, + {N_("/_View/Character _encoding/Korean (ISO-2022-KR)"), + ENC_ACTION(C_ISO_2022_KR)}, + ENC_SEPARATOR, + + {N_("/_View/Character _encoding/Thai (TIS-620)"), + ENC_ACTION(C_TIS_620)}, + {N_("/_View/Character _encoding/Thai (Windows-874)"), + ENC_ACTION(C_WINDOWS_874)}, + +#undef CODESET_SEPARATOR +#undef CODESET_ACTION + + {N_("/_View/---"), NULL, NULL, 0, ""}, + {N_("/_View/Open in new _window"), "N", open_msg_cb, 0, NULL}, + {N_("/_View/Mess_age source"), "U", view_source_cb, 0, NULL}, + {N_("/_View/All _headers"), "H", show_all_header_cb, 0, ""}, + {N_("/_View/---"), NULL, NULL, 0, ""}, + {N_("/_View/_Update summary"), "U", update_summary_cb, 0, NULL}, + + {N_("/_Message"), NULL, NULL, 0, ""}, + {N_("/_Message/Recei_ve"), NULL, NULL, 0, ""}, + {N_("/_Message/Recei_ve/Get from _current account"), + "I", inc_mail_cb, 0, NULL}, + {N_("/_Message/Recei_ve/Get from _all accounts"), + "I", inc_all_account_mail_cb, 0, NULL}, + {N_("/_Message/Recei_ve/Stop receivin_g"), + NULL, inc_stop_cb, 0, NULL}, + {N_("/_Message/Recei_ve/_Remote mailbox..."), + NULL, rpop3_cb, 0, NULL}, + {N_("/_Message/Recei_ve/---"), NULL, NULL, 0, ""}, + {N_("/_Message/_Send queued messages"), NULL, send_queue_cb, 0, NULL}, + {N_("/_Message/---"), NULL, NULL, 0, ""}, + {N_("/_Message/Compose _new message"), "M", compose_cb, 0, NULL}, + {N_("/_Message/---"), NULL, NULL, 0, ""}, + {N_("/_Message/_Reply"), "R", reply_cb, COMPOSE_REPLY, NULL}, + {N_("/_Message/Repl_y to"), NULL, NULL, 0, ""}, + {N_("/_Message/Repl_y to/_all"), "R", reply_cb, COMPOSE_REPLY_TO_ALL, NULL}, + {N_("/_Message/Repl_y to/_sender"), NULL, reply_cb, COMPOSE_REPLY_TO_SENDER, NULL}, + {N_("/_Message/Repl_y to/mailing _list"), + "L", reply_cb, COMPOSE_REPLY_TO_LIST, NULL}, + {N_("/_Message/---"), NULL, NULL, 0, ""}, + {N_("/_Message/_Forward"), "F", reply_cb, COMPOSE_FORWARD, NULL}, + {N_("/_Message/For_ward as attachment"), + "F", reply_cb, COMPOSE_FORWARD_AS_ATTACH, NULL}, + {N_("/_Message/Redirec_t"), NULL, reply_cb, COMPOSE_REDIRECT, NULL}, + {N_("/_Message/---"), NULL, NULL, 0, ""}, + {N_("/_Message/M_ove..."), "O", move_to_cb, 0, NULL}, + {N_("/_Message/_Copy..."), "O", copy_to_cb, 0, NULL}, + {N_("/_Message/---"), NULL, NULL, 0, ""}, + {N_("/_Message/_Mark"), NULL, NULL, 0, ""}, + {N_("/_Message/_Mark/Set _flag"), "asterisk", mark_cb, 0, NULL}, + {N_("/_Message/_Mark/_Unset flag"), "U", unmark_cb, 0, NULL}, + {N_("/_Message/_Mark/---"), NULL, NULL, 0, ""}, + {N_("/_Message/_Mark/Mark as unr_ead"), "exclam", mark_as_unread_cb, 0, NULL}, + {N_("/_Message/_Mark/Mark as rea_d"), + NULL, mark_as_read_cb, 0, NULL}, + {N_("/_Message/_Mark/Mark _thread as read"), + NULL, mark_thread_as_read_cb, 0, NULL}, + {N_("/_Message/_Mark/Mark all _read"), NULL, mark_all_read_cb, 0, NULL}, + {N_("/_Message/---"), NULL, NULL, 0, ""}, + {N_("/_Message/_Delete"), "D", delete_cb, 0, NULL}, + {N_("/_Message/---"), NULL, NULL, 0, ""}, + {N_("/_Message/Set as _junk mail"), "J", junk_cb, 0, NULL}, + {N_("/_Message/Set as not j_unk mail"), "J", junk_cb, 1, NULL}, + {N_("/_Message/---"), NULL, NULL, 0, ""}, + {N_("/_Message/Re-_edit"), NULL, reedit_cb, 0, NULL}, + + {N_("/_Tools"), NULL, NULL, 0, ""}, + {N_("/_Tools/_Address book"), "A", addressbook_open_cb, 0, NULL}, + {N_("/_Tools/Add sender to address boo_k..."), + NULL, add_address_cb, 0, NULL}, + {N_("/_Tools/---"), NULL, NULL, 0, ""}, + {N_("/_Tools/_Filter all messages in folder"), + NULL, filter_cb, 0, NULL}, + {N_("/_Tools/Filter _selected messages"), + NULL, filter_cb, 1, NULL}, + {N_("/_Tools/_Create filter rule"), NULL, NULL, 0, ""}, + {N_("/_Tools/_Create filter rule/_Automatically"), + NULL, create_filter_cb, FLT_BY_AUTO, NULL}, + {N_("/_Tools/_Create filter rule/by _From"), + NULL, create_filter_cb, FLT_BY_FROM, NULL}, + {N_("/_Tools/_Create filter rule/by _To"), + NULL, create_filter_cb, FLT_BY_TO, NULL}, + {N_("/_Tools/_Create filter rule/by _Subject"), + NULL, create_filter_cb, FLT_BY_SUBJECT, NULL}, + {N_("/_Tools/---"), NULL, NULL, 0, ""}, + {N_("/_Tools/Filter _junk mails in folder"), + NULL, filter_junk_cb, 0, NULL}, + {N_("/_Tools/Filter junk _mails in selected messages"), + NULL, filter_junk_cb, 1, NULL}, + {N_("/_Tools/---"), NULL, NULL, 0, ""}, +#ifndef G_OS_WIN32 + {N_("/_Tools/Actio_ns"), NULL, NULL, 0, ""}, + {N_("/_Tools/---"), NULL, NULL, 0, ""}, +#endif + {N_("/_Tools/Delete du_plicated messages"), + NULL, delete_duplicated_cb, 0, NULL}, + {N_("/_Tools/C_oncatenate separated messages"), + NULL, concat_partial_cb, 0, NULL}, + {N_("/_Tools/---"), NULL, NULL, 0, ""}, + {N_("/_Tools/E_xecute marked process"), "X", execute_summary_cb, 0, NULL}, + {N_("/_Tools/---"), NULL, NULL, 0, ""}, + {N_("/_Tools/Op_en configuration folder"), + NULL, open_config_folder_cb, 0, NULL}, + {N_("/_Tools/Open a_ttachments folder"),NULL, open_attachments_folder_cb, 0, NULL}, + {N_("/_Tools/---"), NULL, NULL, 0, ""}, + {N_("/_Tools/_Log window"), "L", log_window_show_cb, 0, NULL}, + + {N_("/_Configuration"), NULL, NULL, 0, ""}, + {N_("/_Configuration/_Common preferences..."), + NULL, prefs_common_open_cb, 0, NULL}, + {N_("/_Configuration/_Filter settings..."), + NULL, prefs_filter_open_cb, 0, NULL}, + {N_("/_Configuration/_Template..."), NULL, prefs_template_open_cb, 0, NULL}, +#ifndef G_OS_WIN32 + {N_("/_Configuration/_Actions..."), NULL, prefs_actions_open_cb, 0, NULL}, +#endif + {N_("/_Configuration/Plug-in _manager..."), + NULL, plugin_manager_open_cb, 0, NULL}, + {N_("/_Configuration/---"), NULL, NULL, 0, ""}, + {N_("/_Configuration/_Preferences for current account..."), + NULL, prefs_account_open_cb, 0, NULL}, + {N_("/_Configuration/Create _new account..."), + NULL, new_account_cb, 0, NULL}, + {N_("/_Configuration/_Edit accounts..."), + NULL, account_edit_open, 0, NULL}, + {N_("/_Configuration/C_hange current account"), + NULL, NULL, 0, ""}, + + {N_("/_Help"), NULL, NULL, 0, ""}, + {N_("/_Help/_Manual"), NULL, NULL, 0, ""}, + {N_("/_Help/_Manual/_English"), NULL, manual_open_cb, MANUAL_LANG_EN, NULL}, + {N_("/_Help/_Manual/_Japanese"), NULL, manual_open_cb, MANUAL_LANG_JA, NULL}, + {N_("/_Help/_FAQ"), NULL, NULL, 0, ""}, + {N_("/_Help/_FAQ/_English"), NULL, faq_open_cb, MANUAL_LANG_EN, NULL}, + {N_("/_Help/_FAQ/_German"), NULL, faq_open_cb, MANUAL_LANG_DE, NULL}, + {N_("/_Help/_FAQ/_Spanish"), NULL, faq_open_cb, MANUAL_LANG_ES, NULL}, + {N_("/_Help/_FAQ/_French"), NULL, faq_open_cb, MANUAL_LANG_FR, NULL}, + {N_("/_Help/_FAQ/_Italian"), NULL, faq_open_cb, MANUAL_LANG_IT, NULL}, + {N_("/_Help/_Command line options"), NULL, help_cmdline_cb, 0, NULL}, +#if USE_UPDATE_CHECK + {N_("/_Help/---"), NULL, NULL, 0, ""}, + {N_("/_Help/_Update check..."), NULL, update_check_cb, 0, NULL}, +#ifdef USE_UPDATE_CHECK_PLUGIN + {N_("/_Help/Update check of _plug-ins..."), + NULL, update_check_plugin_cb, 0, NULL}, +#endif +#endif + {N_("/_Help/---"), NULL, NULL, 0, ""}, + {N_("/_Help/_About"), NULL, about_show, 0, NULL} +}; + +MainWindow *main_window_create(SeparateType type) +{ + MainWindow *mainwin; + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *menubar; + GtkWidget *toolbar; + GtkWidget *hbox_spc; + GtkWidget *vbox_body; + GtkWidget *statusbar; + GtkWidget *progressbar; + GtkWidget *statuslabel; + GtkWidget *online_hbox; + GtkWidget *online_switch; + GtkWidget *online_pixmap; + GtkWidget *offline_pixmap; + GtkTooltips *online_tip; +#if !GTK_CHECK_VERSION(2, 6, 0) + GtkWidget *spacer_hbox; +#endif + GtkWidget *ac_button; + GtkWidget *ac_label; + + TrayIcon *tray_icon; + + FolderView *folderview; + SummaryView *summaryview; + MessageView *messageview; + GdkColormap *colormap; + GdkColor color[3]; + gboolean success[3]; + guint n_menu_entries; + GtkItemFactory *ifactory; + GtkWidget *ac_menu; + GtkWidget *menuitem; + gint i; + + static GdkGeometry geometry; + + debug_print(_("Creating main window...\n")); + mainwin = g_new0(MainWindow, 1); + + /* main window */ + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), PROG_VERSION); + gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE); + gtk_window_set_wmclass(GTK_WINDOW(window), "main_window", "Sylpheed"); + gtk_widget_add_events(window, GDK_VISIBILITY_NOTIFY_MASK); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(main_window_key_pressed), mainwin); + + if (!geometry.min_height) { + geometry.min_width = 320; + geometry.min_height = 200; + } + gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry, + GDK_HINT_MIN_SIZE); + + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(main_window_close_cb), mainwin); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + gtk_widget_realize(window); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_widget_show(vbox); + gtk_container_add(GTK_CONTAINER(window), vbox); + + /* menu bar */ + n_menu_entries = sizeof(mainwin_entries) / sizeof(mainwin_entries[0]); + menubar = menubar_create(window, mainwin_entries, + n_menu_entries, "
", mainwin); + gtk_widget_show(menubar); + gtk_widget_set_size_request(menubar, 300, -1); + gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0); + ifactory = gtk_item_factory_from_widget(menubar); + + /* toolbar */ + mainwin->toolbar_tip = gtk_tooltips_new(); + toolbar = main_window_toolbar_create(mainwin); + gtk_widget_set_size_request(toolbar, 300, -1); + gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); + + hbox_spc = gtk_hbox_new(FALSE, 0); + gtk_widget_show(hbox_spc); + gtk_widget_set_size_request(hbox_spc, -1, BORDER_WIDTH); + gtk_box_pack_start(GTK_BOX(vbox), hbox_spc, FALSE, FALSE, 0); + + /* vbox that contains body */ + vbox_body = gtk_vbox_new(FALSE, BORDER_WIDTH); + gtk_widget_show(vbox_body); + gtk_container_set_border_width(GTK_CONTAINER(vbox_body), 0); + gtk_box_pack_start(GTK_BOX(vbox), vbox_body, TRUE, TRUE, 0); + + statusbar = statusbar_create(); + gtk_box_pack_end(GTK_BOX(vbox_body), statusbar, FALSE, FALSE, 0); + + progressbar = gtk_progress_bar_new(); + gtk_widget_set_size_request(progressbar, 120, 1); + gtk_box_pack_start(GTK_BOX(statusbar), progressbar, FALSE, FALSE, 0); + + statuslabel = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(statusbar), statuslabel, FALSE, FALSE, 0); + + online_hbox = gtk_hbox_new(FALSE, 0); + + online_pixmap = stock_pixbuf_widget(statusbar, STOCK_PIXMAP_ONLINE); + offline_pixmap = stock_pixbuf_widget(statusbar, STOCK_PIXMAP_OFFLINE); + gtk_box_pack_start(GTK_BOX(online_hbox), online_pixmap, + FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(online_hbox), offline_pixmap, + FALSE, FALSE, 0); + + online_switch = gtk_button_new(); + gtk_button_set_relief(GTK_BUTTON(online_switch), GTK_RELIEF_NONE); + GTK_WIDGET_UNSET_FLAGS(online_switch, GTK_CAN_FOCUS); +#ifdef G_OS_WIN32 + gtk_widget_set_size_request(online_switch, 34, 20); +#else + gtk_widget_set_size_request(online_switch, 34, 24); +#endif + gtk_container_add(GTK_CONTAINER(online_switch), online_hbox); + g_signal_connect(G_OBJECT(online_switch), "clicked", + G_CALLBACK(online_switch_clicked), mainwin); + gtk_box_pack_start(GTK_BOX(statusbar), online_switch, FALSE, FALSE, 0); + + online_tip = gtk_tooltips_new(); + +#if !GTK_CHECK_VERSION(2, 6, 0) + spacer_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(statusbar), spacer_hbox, FALSE, FALSE, 0); +#endif + + ac_button = gtk_button_new(); + gtk_button_set_relief(GTK_BUTTON(ac_button), GTK_RELIEF_NONE); + GTK_WIDGET_UNSET_FLAGS(ac_button, GTK_CAN_FOCUS); + gtk_widget_set_size_request(ac_button, -1, 1); + gtk_box_pack_end(GTK_BOX(statusbar), ac_button, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(ac_button), "button_press_event", + G_CALLBACK(ac_label_button_pressed), mainwin); + + ac_label = gtk_label_new(""); + gtk_container_add(GTK_CONTAINER(ac_button), ac_label); + + gtk_widget_show_all(statusbar); + + tray_icon = trayicon_create(mainwin); + if (tray_icon && prefs_common.show_trayicon) + trayicon_show(tray_icon); + + /* create views */ + mainwin->folderview = folderview = folderview_create(); + mainwin->summaryview = summaryview = summary_create(); + mainwin->messageview = messageview = messageview_create(); + mainwin->logwin = log_window_create(); + + quick_search_create(summaryview); + + folderview->mainwin = mainwin; + folderview->summaryview = summaryview; + + summaryview->mainwin = mainwin; + summaryview->folderview = folderview; + summaryview->messageview = messageview; + summaryview->window = window; + + messageview->statusbar = statusbar; + messageview->mainwin = mainwin; + + mainwin->window = window; + mainwin->vbox = vbox; + mainwin->menubar = menubar; + mainwin->menu_factory = ifactory; + mainwin->toolbar = toolbar; + mainwin->vbox_body = vbox_body; + mainwin->statusbar = statusbar; + mainwin->progressbar = progressbar; + mainwin->statuslabel = statuslabel; + mainwin->online_switch = online_switch; + mainwin->online_pixmap = online_pixmap; + mainwin->offline_pixmap = offline_pixmap; + mainwin->online_tip = online_tip; + mainwin->ac_button = ac_button; + mainwin->ac_label = ac_label; + + mainwin->tray_icon = tray_icon; + + /* set context IDs for status bar */ + mainwin->mainwin_cid = gtk_statusbar_get_context_id + (GTK_STATUSBAR(statusbar), "Main Window"); + mainwin->folderview_cid = gtk_statusbar_get_context_id + (GTK_STATUSBAR(statusbar), "Folder View"); + mainwin->summaryview_cid = gtk_statusbar_get_context_id + (GTK_STATUSBAR(statusbar), "Summary View"); + mainwin->messageview_cid = gtk_statusbar_get_context_id + (GTK_STATUSBAR(statusbar), "Message View"); + + messageview->statusbar_cid = mainwin->messageview_cid; + + /* allocate colors for summary view and folder view */ + summaryview->color_marked.red = summaryview->color_marked.green = 0; + summaryview->color_marked.blue = (guint16)65535; + + summaryview->color_dim.red = summaryview->color_dim.green = + summaryview->color_dim.blue = COLOR_DIM; + + folderview->color_new.red = (guint16)55000; + folderview->color_new.green = folderview->color_new.blue = 15000; + + folderview->color_noselect.red = folderview->color_noselect.green = + folderview->color_noselect.blue = COLOR_DIM; + + color[0] = summaryview->color_marked; + color[1] = summaryview->color_dim; + color[2] = folderview->color_new; + + colormap = gdk_window_get_colormap(window->window); + gdk_colormap_alloc_colors(colormap, color, 3, FALSE, TRUE, success); + for (i = 0; i < 3; i++) { + if (success[i] == FALSE) + g_warning(_("MainWindow: color allocation %d failed\n"), i); + } + + if (prefs_common.layout_type == LAYOUT_VERTICAL) + messageview->visible = TRUE; + else + messageview->visible = prefs_common.msgview_visible; + + main_window_set_widgets(mainwin, prefs_common.layout_type, type); + + if (prefs_common.mainwin_maximized) + gtk_window_maximize(GTK_WINDOW(window)); + + g_signal_connect(G_OBJECT(window), "size_allocate", + G_CALLBACK(main_window_size_allocate_cb), mainwin); + g_signal_connect(G_OBJECT(window), "window_state_event", + G_CALLBACK(main_window_window_state_cb), mainwin); + g_signal_connect(G_OBJECT(window), "visibility_notify_event", + G_CALLBACK(main_window_visibility_notify_cb), mainwin); + + /* set menu items */ + menuitem = gtk_item_factory_get_item + (ifactory, "/View/Character encoding/Auto detect"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE); + + main_window_toolbar_toggle_menu_set_active + (mainwin, prefs_common.toolbar_style); + + gtk_widget_hide(GTK_WIDGET_PTR(summaryview->qsearch)); + menuitem = gtk_item_factory_get_item + (ifactory, "/View/Show or hide/Search bar"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), + prefs_common.show_searchbar); + + gtk_widget_hide(mainwin->statusbar); + menuitem = gtk_item_factory_get_item + (ifactory, "/View/Show or hide/Status bar"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), + prefs_common.show_statusbar); + + /* set account selection menu */ + ac_menu = gtk_item_factory_get_widget + (ifactory, "/Configuration/Change current account"); + g_signal_connect(G_OBJECT(ac_menu), "selection_done", + G_CALLBACK(ac_menu_popup_closed), mainwin); + mainwin->ac_menu = ac_menu; + + main_window_set_toolbar_sensitive(mainwin); + main_window_set_toolbar_button_visibility(mainwin); + + /* create actions menu */ +#ifndef G_OS_WIN32 + action_update_mainwin_menu(ifactory, mainwin); +#endif + + /* initialize online switch */ + if (prefs_common.startup_online_mode == 1) + prefs_common.online_mode = TRUE; + else if (prefs_common.startup_online_mode == 0) + prefs_common.online_mode = FALSE; + prefs_common.online_mode = !prefs_common.online_mode; + online_switch_clicked(online_switch, mainwin); + + /* show main window */ + gtk_widget_show(mainwin->window); + +#if !GTK_CHECK_VERSION(2, 6, 0) + { + gint w, h; + gdk_drawable_get_size + (GDK_DRAWABLE(GTK_STATUSBAR(statusbar)->grip_window), + &w, &h); + gtk_widget_set_size_request(spacer_hbox, w, -1); + } +#endif + + /* initialize views */ + folderview_init(folderview); + summary_init(summaryview); + messageview_init(messageview); + log_window_init(mainwin->logwin); + + mainwin->lock_count = 0; + mainwin->menu_lock_count = 0; + mainwin->cursor_count = 0; + + mainwin->window_hidden = FALSE; + mainwin->window_obscured = FALSE; + + if (!watch_cursor) + watch_cursor = gdk_cursor_new(GDK_WATCH); + + mainwin_list = g_list_append(mainwin_list, mainwin); + + debug_print(_("done.\n")); + + return mainwin; +} + +void main_window_cursor_wait(MainWindow *mainwin) +{ + + if (mainwin->cursor_count == 0) + gdk_window_set_cursor(mainwin->window->window, watch_cursor); + + mainwin->cursor_count++; + + gdk_flush(); +} + +void main_window_cursor_normal(MainWindow *mainwin) +{ + if (mainwin->cursor_count) + mainwin->cursor_count--; + + if (mainwin->cursor_count == 0) + gdk_window_set_cursor(mainwin->window->window, NULL); + + gdk_flush(); +} + +/* lock / unlock the user-interface */ +void main_window_lock(MainWindow *mainwin) +{ + if (mainwin->lock_count == 0) + gtk_widget_set_sensitive(mainwin->ac_button, FALSE); + + mainwin->lock_count++; + + main_window_set_menu_sensitive(mainwin); + main_window_set_toolbar_sensitive(mainwin); +} + +void main_window_unlock(MainWindow *mainwin) +{ + if (mainwin->lock_count) + mainwin->lock_count--; + + main_window_set_menu_sensitive(mainwin); + main_window_set_toolbar_sensitive(mainwin); + + if (mainwin->lock_count == 0) + gtk_widget_set_sensitive(mainwin->ac_button, TRUE); +} + +static void main_window_menu_callback_block(MainWindow *mainwin) +{ + mainwin->menu_lock_count++; +} + +static void main_window_menu_callback_unblock(MainWindow *mainwin) +{ + if (mainwin->menu_lock_count) + mainwin->menu_lock_count--; +} + +void main_window_reflect_prefs_all(void) +{ + MainWindow *mainwin; + + mainwin = main_window_get(); + + main_window_show_cur_account(mainwin); + main_window_set_menu_sensitive(mainwin); + main_window_set_toolbar_sensitive(mainwin); + main_window_set_toolbar_button_visibility(mainwin); + + if (mainwin->tray_icon) { + if (prefs_common.show_trayicon) + trayicon_show(mainwin->tray_icon); + else { + /* trayicon is automatically replaced by new one */ + trayicon_hide(mainwin->tray_icon); + } + } + + folderview_reflect_prefs(mainwin->folderview); + + messageview_reflect_prefs(mainwin->messageview); + + headerview_set_visibility(mainwin->messageview->headerview, + prefs_common.display_header_pane); + + textview_reflect_prefs(mainwin->messageview->textview); + textview_reflect_prefs(mainwin->messageview->mimeview->textview); + + summary_redisplay_msg(mainwin->summaryview); +} + +void main_window_set_summary_column(void) +{ + summary_set_column_order(main_window_get()->summaryview); +} + +static void main_window_set_account_selector_menu(MainWindow *mainwin, + GList *account_list) +{ + GList *cur_ac, *cur_item; + GtkWidget *menuitem; + PrefsAccount *ac_prefs; + + /* destroy all previous menu item */ + cur_item = GTK_MENU_SHELL(mainwin->ac_menu)->children; + while (cur_item != NULL) { + GList *next = cur_item->next; + gtk_widget_destroy(GTK_WIDGET(cur_item->data)); + cur_item = next; + } + + for (cur_ac = account_list; cur_ac != NULL; cur_ac = cur_ac->next) { + ac_prefs = (PrefsAccount *)cur_ac->data; + + menuitem = gtk_menu_item_new_with_label + (ac_prefs->account_name + ? ac_prefs->account_name : _("Untitled")); + gtk_widget_show(menuitem); + gtk_menu_append(GTK_MENU(mainwin->ac_menu), menuitem); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(account_selector_menu_cb), + ac_prefs); + } +} + +static void main_window_set_account_receive_menu(MainWindow *mainwin, + GList *account_list) +{ + GList *cur_ac, *cur_item; + GtkWidget *menu; + GtkWidget *menuitem; + PrefsAccount *ac_prefs; + + menu = gtk_item_factory_get_widget(mainwin->menu_factory, + "/Message/Receive"); + + /* search for separator */ + for (cur_item = GTK_MENU_SHELL(menu)->children; cur_item != NULL; + cur_item = cur_item->next) { + if (GTK_BIN(cur_item->data)->child == NULL) { + cur_item = cur_item->next; + break; + } + } + + /* destroy all previous menu item */ + while (cur_item != NULL) { + GList *next = cur_item->next; + gtk_widget_destroy(GTK_WIDGET(cur_item->data)); + cur_item = next; + } + + for (cur_ac = account_list; cur_ac != NULL; cur_ac = cur_ac->next) { + ac_prefs = (PrefsAccount *)cur_ac->data; + + menuitem = gtk_menu_item_new_with_label + (ac_prefs->account_name ? ac_prefs->account_name + : _("Untitled")); + gtk_widget_show(menuitem); + gtk_menu_append(GTK_MENU(menu), menuitem); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(account_receive_menu_cb), + ac_prefs); + } +} + +void main_window_set_account_menu(GList *account_list) +{ + MainWindow *mainwin; + + mainwin = main_window_get(); + main_window_set_account_selector_menu(mainwin, account_list); + main_window_set_account_receive_menu(mainwin, account_list); +} + +static void main_window_show_cur_account(MainWindow *mainwin) +{ + gchar *buf; + gchar *ac_name; + + ac_name = g_strdup(cur_account + ? (cur_account->account_name + ? cur_account->account_name : _("Untitled")) + : _("none")); + + if (cur_account) + buf = g_strdup_printf("%s - %s", ac_name, PROG_VERSION); + else + buf = g_strdup(PROG_VERSION); + gtk_window_set_title(GTK_WINDOW(mainwin->window), buf); + g_free(buf); + + gtk_label_set_text(GTK_LABEL(mainwin->ac_label), ac_name); + gtk_widget_queue_resize(mainwin->ac_button); + + g_free(ac_name); +} + +void main_window_change_cur_account(void) +{ + MainWindow *mainwin; + + mainwin = main_window_get(); + main_window_show_cur_account(mainwin); + main_window_set_menu_sensitive(mainwin); + main_window_set_toolbar_sensitive(mainwin); +} + +MainWindow *main_window_get(void) +{ + if (mainwin_list) + return (MainWindow *)mainwin_list->data; + else + return NULL; +} + +GtkWidget *main_window_get_folder_window(MainWindow *mainwin) +{ + switch (mainwin->type) { + case SEPARATE_FOLDER: + return mainwin->win.sep_folder.folderwin; + case SEPARATE_BOTH: + return mainwin->win.sep_both.folderwin; + default: + return NULL; + } +} + +GtkWidget *main_window_get_message_window(MainWindow *mainwin) +{ + switch (mainwin->type) { + case SEPARATE_MESSAGE: + return mainwin->win.sep_message.messagewin; + case SEPARATE_BOTH: + return mainwin->win.sep_both.messagewin; + default: + return NULL; + } +} + +void main_window_hide(MainWindow *mainwin) +{ + GtkWidget *folder_wid = GTK_WIDGET_PTR(mainwin->folderview); + GtkWidget *summary_wid = GTK_WIDGET_PTR(mainwin->summaryview); + GtkWidget *message_wid = GTK_WIDGET_PTR(mainwin->messageview); + GtkWidget *qsearch_wid = GTK_WIDGET_PTR(mainwin->summaryview->qsearch); + GtkWidget *vbox_summary = qsearch_wid->parent; + + /* remove widgets from those containers */ + gtkut_container_remove + (GTK_CONTAINER(folder_wid->parent), folder_wid); + gtkut_container_remove + (GTK_CONTAINER(summary_wid->parent), summary_wid); + gtkut_container_remove + (GTK_CONTAINER(message_wid->parent), message_wid); + gtkut_container_remove + (GTK_CONTAINER(qsearch_wid->parent), qsearch_wid); + + /* clean containers */ + switch (mainwin->type) { + case SEPARATE_NONE: + if (!mainwin->win.sep_none.vpaned->parent) + gtk_widget_destroy(mainwin->win.sep_none.vpaned); + gtk_widget_destroy(mainwin->win.sep_none.hpaned); + mainwin->win.sep_none.hpaned = NULL; + mainwin->win.sep_none.vpaned = NULL; + break; + case SEPARATE_FOLDER: + gtk_widget_destroy(mainwin->win.sep_folder.folderwin); + if (!mainwin->win.sep_folder.vpaned->parent) + gtk_widget_destroy(mainwin->win.sep_folder.vpaned); + gtk_widget_destroy(vbox_summary); + mainwin->win.sep_folder.folderwin = NULL; + mainwin->win.sep_folder.vpaned = NULL; + break; + case SEPARATE_MESSAGE: + gtk_widget_destroy(mainwin->win.sep_message.messagewin); + gtk_widget_destroy(mainwin->win.sep_message.hpaned); + mainwin->win.sep_message.messagewin = NULL; + mainwin->win.sep_message.hpaned = NULL; + break; + case SEPARATE_BOTH: + gtk_widget_destroy(vbox_summary); + gtk_widget_destroy(mainwin->win.sep_both.folderwin); + gtk_widget_destroy(mainwin->win.sep_both.messagewin); + mainwin->win.sep_both.folderwin = NULL; + mainwin->win.sep_both.messagewin = NULL; + break; + } + + gtk_widget_hide(mainwin->window); +} + +void main_window_change_layout(MainWindow *mainwin, LayoutType layout, + SeparateType type) +{ + GtkWidget *folder_wid = GTK_WIDGET_PTR(mainwin->folderview); + GtkWidget *summary_wid = GTK_WIDGET_PTR(mainwin->summaryview); + GtkWidget *message_wid = GTK_WIDGET_PTR(mainwin->messageview); + GtkWidget *qsearch_wid = GTK_WIDGET_PTR(mainwin->summaryview->qsearch); + GtkWidget *focus_widget; + + debug_print("Changing window layout type (layout: %d -> %d, separation: %d -> %d)\n", prefs_common.layout_type, layout, mainwin->type, type); + + if (prefs_common.layout_type == layout && mainwin->type == type) + return; + + /* keep previous focus */ + focus_widget = gtk_window_get_focus(GTK_WINDOW(mainwin->window)); + + gtk_widget_ref(folder_wid); + gtk_widget_ref(summary_wid); + gtk_widget_ref(message_wid); + gtk_widget_ref(qsearch_wid); + + main_window_hide(mainwin); + main_window_set_widgets(mainwin, layout, type); + gtk_widget_show(mainwin->window); + if (focus_widget) + gtk_widget_grab_focus(focus_widget); + + gtk_widget_unref(folder_wid); + gtk_widget_unref(summary_wid); + gtk_widget_unref(message_wid); + gtk_widget_unref(qsearch_wid); +} + +void main_window_toggle_message_view(MainWindow *mainwin) +{ + SummaryView *summaryview = mainwin->summaryview; + union CompositeWin *cwin = &mainwin->win; + GtkWidget *vpaned = NULL; + GtkWidget *container = NULL; + GtkWidget *msgwin = NULL; + gboolean use_vlayout = (prefs_common.layout_type == LAYOUT_VERTICAL); + + switch (mainwin->type) { + case SEPARATE_NONE: + vpaned = cwin->sep_none.vpaned; + container = GTK_WIDGET_PTR(summaryview->qsearch)->parent; + break; + case SEPARATE_FOLDER: + vpaned = cwin->sep_folder.vpaned; + container = GTK_WIDGET_PTR(summaryview->qsearch)->parent; + break; + case SEPARATE_MESSAGE: + msgwin = mainwin->win.sep_message.messagewin; + break; + case SEPARATE_BOTH: + msgwin = mainwin->win.sep_both.messagewin; + break; + } + + if (msgwin) { + /* separate message view */ + if (GTK_WIDGET_VISIBLE(msgwin)) { + gtk_widget_hide(msgwin); + mainwin->messageview->visible = FALSE; + summaryview->displayed = NULL; + } else { + gtk_widget_show(msgwin); + mainwin->messageview->visible = TRUE; + } + } else if (vpaned->parent != NULL) { + /* hide message view */ + mainwin->messageview->visible = FALSE; + summaryview->displayed = NULL; + gtk_widget_ref(vpaned); + gtkut_container_remove(GTK_CONTAINER(container), vpaned); + gtk_widget_reparent(GTK_WIDGET_PTR(summaryview), container); + if (!use_vlayout) + gtk_widget_hide(summaryview->hseparator); + } else { + /* show message view */ + mainwin->messageview->visible = TRUE; + gtk_widget_reparent(GTK_WIDGET_PTR(summaryview), vpaned); + gtk_container_add(GTK_CONTAINER(container), vpaned); + gtk_widget_unref(vpaned); + if (!use_vlayout) + gtk_widget_show(summaryview->hseparator); + } + + if (messageview_is_visible(mainwin->messageview)) + gtk_arrow_set(GTK_ARROW(mainwin->summaryview->toggle_arrow), + use_vlayout ? GTK_ARROW_RIGHT : GTK_ARROW_DOWN, + GTK_SHADOW_OUT); + else + gtk_arrow_set(GTK_ARROW(mainwin->summaryview->toggle_arrow), + use_vlayout ? GTK_ARROW_LEFT : GTK_ARROW_UP, + GTK_SHADOW_OUT); + + if (mainwin->messageview->visible == FALSE) + messageview_clear(mainwin->messageview); + + main_window_set_menu_sensitive(mainwin); + + prefs_common.msgview_visible = mainwin->messageview->visible; + + gtk_widget_grab_focus(summaryview->treeview); +} + +void main_window_get_size(MainWindow *mainwin) +{ + GtkAllocation *allocation; + gboolean vlayout = (prefs_common.layout_type == LAYOUT_VERTICAL); + + allocation = &(GTK_WIDGET_PTR(mainwin->summaryview)->allocation); + if (allocation->width > 1 && allocation->height > 1) { + if (vlayout) { + if (!(mainwin->type & SEPARATE_MESSAGE) && + messageview_is_visible(mainwin->messageview)) + prefs_common.summaryview_vwidth = allocation->width; + prefs_common.summaryview_vheight = allocation->height; + } else { + if (!prefs_common.mainwin_maximized) { + prefs_common.summaryview_width = allocation->width; + prefs_common.mainview_width = allocation->width; + } + if ((mainwin->type == SEPARATE_NONE || + mainwin->type == SEPARATE_FOLDER) && + messageview_is_visible(mainwin->messageview)) + prefs_common.summaryview_height = allocation->height; + } + } + + if (prefs_common.mainwin_maximized) { + allocation = &(GTK_WIDGET_PTR(mainwin->folderview)->allocation); + if (allocation->width > 1 && allocation->height > 1) + prefs_common.folderview_width = allocation->width; + return; + } + + allocation = &mainwin->window->allocation; + if (allocation->width > 1 && allocation->height > 1) { + prefs_common.mainview_height = allocation->height; + prefs_common.mainwin_width = allocation->width; + prefs_common.mainwin_height = allocation->height; + } + + allocation = &(GTK_WIDGET_PTR(mainwin->folderview)->allocation); + if (allocation->width > 1 && allocation->height > 1) { + prefs_common.folderview_width = allocation->width; + prefs_common.folderview_height = allocation->height; + } + + allocation = &(GTK_WIDGET_PTR(mainwin->messageview)->allocation); + if (allocation->width > 1 && allocation->height > 1) { + if (vlayout) { + prefs_common.msgview_vwidth = allocation->width; + prefs_common.msgview_vheight = allocation->height; + } else { + prefs_common.msgview_width = allocation->width; + prefs_common.msgview_height = allocation->height; + } + } + +#if 0 + debug_print("summaryview size: %d x %d\n", + prefs_common.summaryview_width, + prefs_common.summaryview_height); + debug_print("mainwin size: %d x %d\n", + prefs_common.mainwin_width, + prefs_common.mainwin_height); + debug_print("folderview size: %d x %d\n", + prefs_common.folderview_width, + prefs_common.folderview_height); + debug_print("messageview size: %d x %d\n", + prefs_common.msgview_width, + prefs_common.msgview_height); +#endif +} + +void main_window_get_position(MainWindow *mainwin) +{ + gint x, y; + GtkWidget *window; + + if (prefs_common.mainwin_maximized || mainwin->window_hidden) + return; + + gtkut_widget_get_uposition(mainwin->window, &x, &y); + + prefs_common.mainview_x = x; + prefs_common.mainview_y = y; + prefs_common.mainwin_x = x; + prefs_common.mainwin_y = y; + + debug_print("main window position: %d, %d\n", x, y); + + window = main_window_get_folder_window(mainwin); + if (window) { + gtkut_widget_get_uposition(window, &x, &y); + prefs_common.folderwin_x = x; + prefs_common.folderwin_y = y; + debug_print("folder window position: %d, %d\n", x, y); + } + window = main_window_get_message_window(mainwin); + if (window) { + gtkut_widget_get_uposition(window, &x, &y); + prefs_common.main_msgwin_x = x; + prefs_common.main_msgwin_y = y; + debug_print("message window position: %d, %d\n", x, y); + } +} + +void main_window_progress_on(MainWindow *mainwin) +{ + gtk_progress_set_show_text(GTK_PROGRESS(mainwin->progressbar), TRUE); + gtk_progress_set_format_string(GTK_PROGRESS(mainwin->progressbar), ""); +} + +void main_window_progress_off(MainWindow *mainwin) +{ + gtk_progress_set_show_text(GTK_PROGRESS(mainwin->progressbar), FALSE); + gtk_progress_bar_update(GTK_PROGRESS_BAR(mainwin->progressbar), 0.0); + gtk_progress_set_format_string(GTK_PROGRESS(mainwin->progressbar), ""); +} + +void main_window_progress_set(MainWindow *mainwin, gint cur, gint total) +{ + gchar buf[32]; + + g_snprintf(buf, sizeof(buf), "%d / %d", cur, total); + gtk_progress_set_format_string(GTK_PROGRESS(mainwin->progressbar), buf); + gtk_progress_bar_update(GTK_PROGRESS_BAR(mainwin->progressbar), + (cur == 0 && total == 0) ? 0 : + (gfloat)cur / (gfloat)total); +} + +void main_window_progress_show(gint cur, gint total) +{ + MainWindow *mainwin; + + mainwin = main_window_get(); + + if (total > 0) { + gtk_progress_set_show_text(GTK_PROGRESS(mainwin->progressbar), + TRUE); + main_window_progress_set(mainwin, cur, total); + } else + main_window_progress_off(mainwin); +} + +void main_window_toggle_online(MainWindow *mainwin, gboolean online) +{ + if (prefs_common.online_mode != online) + online_switch_clicked(mainwin->online_switch, mainwin); +} + +gboolean main_window_toggle_online_if_offline(MainWindow *mainwin) +{ + if (!prefs_common.online_mode) { + if (alertpanel(_("Offline"), + _("You are offline. Go online?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL) + == G_ALERTDEFAULT) + main_window_toggle_online(mainwin, TRUE); + } + + return prefs_common.online_mode; +} + +void main_window_empty_trash(MainWindow *mainwin, gboolean confirm) +{ + GList *list; + + if (!procmsg_trash_messages_exist()) + return; + + if (confirm) { + if (alertpanel(_("Empty all trash"), + _("Delete all messages in trash folders?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL) + != G_ALERTDEFAULT) + return; + manage_window_focus_in(mainwin->window, NULL, NULL); + } + + procmsg_empty_all_trash(); + statusbar_pop_all(); + trayicon_set_tooltip(NULL); + trayicon_set_notify(FALSE); + + for (list = folder_get_list(); list != NULL; list = list->next) { + Folder *folder; + + folder = list->data; + if (folder->trash) + folderview_update_item(folder->trash, TRUE); + } + + if (mainwin->summaryview->folder_item && + mainwin->summaryview->folder_item->stype == F_TRASH) + gtk_widget_grab_focus(mainwin->folderview->treeview); +} + +void main_window_add_mailbox(MainWindow *mainwin) +{ + gchar *path; + Folder *folder; + + path = input_dialog_with_filesel + (_("Add mailbox"), + _("Specify the location of mailbox.\n" + "If the existing mailbox is specified, it will be\n" + "scanned automatically."), + "Mail", GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); + if (!path) return; + if (folder_find_from_path(path)) { + alertpanel_error(_("The mailbox `%s' already exists."), path); + g_free(path); + return; + } + if (!strcmp(path, "Mail")) + folder = folder_new(F_MH, _("Mailbox"), path); + else + folder = folder_new(F_MH, g_basename(path), path); + g_free(path); + + if (folder->klass->create_tree(folder) < 0) { + alertpanel_error(_("Creation of the mailbox failed.\n" + "Maybe some files already exist, or you don't have the permission to write there.")); + folder_destroy(folder); + return; + } + + folder_add(folder); + folder_set_ui_func(folder, scan_tree_func, mainwin); + folder->klass->scan_tree(folder); + folder_set_ui_func(folder, NULL, NULL); + + folderview_set(mainwin->folderview); +} + +void main_window_send_queue(MainWindow *mainwin) +{ + GList *list; + + if (inc_is_active()) + return; + if (!main_window_toggle_online_if_offline(mainwin)) + return; + + for (list = folder_get_list(); list != NULL; list = list->next) { + Folder *folder = list->data; + + if (folder->queue) { + gint ret; + + ret = send_message_queue_all(folder->queue, + prefs_common.savemsg, + prefs_common.filter_sent); + statusbar_pop_all(); + if (ret > 0) + folder_item_scan(folder->queue); + } + } + + folderview_update_all_updated(TRUE); + main_window_set_menu_sensitive(mainwin); + main_window_set_toolbar_sensitive(mainwin); +} + +typedef enum +{ + M_UNLOCKED = 1 << 0, + M_MSG_EXIST = 1 << 1, + M_TARGET_EXIST = 1 << 2, + M_SINGLE_TARGET_EXIST = 1 << 3, + M_EXEC = 1 << 4, + M_ALLOW_REEDIT = 1 << 5, + M_HAVE_ACCOUNT = 1 << 6, + M_THREADED = 1 << 7, + M_UNTHREADED = 1 << 8, + M_ALLOW_DELETE = 1 << 9, + M_INC_ACTIVE = 1 << 10, + M_ENABLE_JUNK = 1 << 11, + M_HAVE_QUEUED_MSG = 1 << 12, + M_POP3_ACCOUNT = 1 << 13, + + M_FOLDER_NEWOK = 1 << 17, + M_FOLDER_RENOK = 1 << 18, + M_FOLDER_MOVEOK = 1 << 19, + M_FOLDER_DELOK = 1 << 20, + M_MBOX_ADDOK = 1 << 21, + M_MBOX_RMOK = 1 << 22, + M_MBOX_CHKOK = 1 << 23, + M_MBOX_CHKALLOK = 1 << 24, + M_MBOX_REBUILDOK = 1 << 25 +} SensitiveCond; + +static SensitiveCond main_window_get_current_state(MainWindow *mainwin) +{ + SensitiveCond state = 0; + SummarySelection selection; + GList *list; + FolderItem *item = mainwin->summaryview->folder_item; + + selection = summary_get_selection_type(mainwin->summaryview); + + if (mainwin->lock_count == 0) + state |= M_UNLOCKED; + if (selection != SUMMARY_NONE) + state |= M_MSG_EXIST; + if (item && item->path && item->parent && !item->no_select) { + state |= M_EXEC; + if (item->threaded) + state |= M_THREADED; + else + state |= M_UNTHREADED; + if (FOLDER_TYPE(item->folder) != F_NEWS) + state |= M_ALLOW_DELETE; + } + if (selection == SUMMARY_SELECTED_SINGLE || + selection == SUMMARY_SELECTED_MULTIPLE) + state |= M_TARGET_EXIST; + if (selection == SUMMARY_SELECTED_SINGLE) + state |= M_SINGLE_TARGET_EXIST; + if (selection == SUMMARY_SELECTED_SINGLE && + FOLDER_ITEM_IS_SENT_FOLDER(item)) + state |= M_ALLOW_REEDIT; + if (cur_account) { + state |= M_HAVE_ACCOUNT; + if (cur_account->protocol == A_POP3) + state |= M_POP3_ACCOUNT; + } + + if (inc_is_active()) + state |= M_INC_ACTIVE; + + if (prefs_common.enable_junk) + state |= M_ENABLE_JUNK; + + for (list = folder_get_list(); list != NULL; list = list->next) { + Folder *folder = list->data; + if (folder->queue && folder->queue->total > 0) { + state |= M_HAVE_QUEUED_MSG; + break; + } + } + + item = folderview_get_selected_item(mainwin->folderview); + if (item) { + state |= M_FOLDER_NEWOK; + if (item->parent == NULL) { + state |= M_MBOX_RMOK; + state |= M_MBOX_CHKOK; + } + if (FOLDER_IS_LOCAL(item->folder) || + FOLDER_TYPE(item->folder) == F_IMAP) { + if (item->parent == NULL) + state |= M_MBOX_REBUILDOK; + else if (item->stype == F_NORMAL) { + state |= M_FOLDER_RENOK; + state |= M_FOLDER_DELOK; + if (item->folder->klass->move_folder) + state |= M_FOLDER_MOVEOK; + } + } else if (FOLDER_TYPE(item->folder) == F_NEWS) { + if (item->parent != NULL) + state |= M_FOLDER_DELOK; + } + } + state |= M_MBOX_ADDOK; + state |= M_MBOX_CHKALLOK; + + return state; +} + +void main_window_set_toolbar_sensitive(MainWindow *mainwin) +{ + SensitiveCond state; + gboolean sensitive, prev_sensitive; + gint n; + gint i = 0; + + struct { + GtkWidget *widget; + SensitiveCond cond; + } entry[20]; + +#define SET_WIDGET_COND(w, c) \ +{ \ + entry[i].widget = w; \ + entry[i].cond = c; \ + i++; \ +} + + SET_WIDGET_COND(mainwin->get_btn, M_HAVE_ACCOUNT|M_UNLOCKED); + SET_WIDGET_COND(mainwin->getall_btn, M_HAVE_ACCOUNT|M_UNLOCKED); + SET_WIDGET_COND(mainwin->rpop3_btn, + M_HAVE_ACCOUNT|M_UNLOCKED|M_POP3_ACCOUNT); + SET_WIDGET_COND(mainwin->send_btn, + M_HAVE_ACCOUNT|M_UNLOCKED|M_HAVE_QUEUED_MSG); + SET_WIDGET_COND(mainwin->compose_btn, M_HAVE_ACCOUNT); + SET_WIDGET_COND(mainwin->reply_btn, + M_HAVE_ACCOUNT|M_SINGLE_TARGET_EXIST); + SET_WIDGET_COND(mainwin->reply_combo ? + GTK_WIDGET_PTR(mainwin->reply_combo) : NULL, + M_HAVE_ACCOUNT|M_SINGLE_TARGET_EXIST); + SET_WIDGET_COND(mainwin->replyall_btn, + M_HAVE_ACCOUNT|M_SINGLE_TARGET_EXIST); + SET_WIDGET_COND(mainwin->fwd_btn, M_HAVE_ACCOUNT|M_TARGET_EXIST); + SET_WIDGET_COND(mainwin->fwd_combo ? GTK_WIDGET_PTR(mainwin->fwd_combo) + : NULL, + M_HAVE_ACCOUNT|M_TARGET_EXIST); + SET_WIDGET_COND(mainwin->delete_btn, + M_TARGET_EXIST|M_ALLOW_DELETE); + SET_WIDGET_COND(mainwin->junk_btn, + M_TARGET_EXIST|M_ALLOW_DELETE|M_ENABLE_JUNK); + SET_WIDGET_COND(mainwin->notjunk_btn, + M_TARGET_EXIST|M_ALLOW_DELETE|M_ENABLE_JUNK); + SET_WIDGET_COND(mainwin->exec_btn, M_MSG_EXIST|M_EXEC); + SET_WIDGET_COND(mainwin->next_btn, M_MSG_EXIST); + SET_WIDGET_COND(mainwin->prev_btn, M_MSG_EXIST); + SET_WIDGET_COND(mainwin->print_btn, M_TARGET_EXIST); + SET_WIDGET_COND(mainwin->stop_btn, M_INC_ACTIVE); + SET_WIDGET_COND(mainwin->prefs_common_btn, M_UNLOCKED); + SET_WIDGET_COND(mainwin->prefs_account_btn, M_HAVE_ACCOUNT|M_UNLOCKED); + +#undef SET_WIDGET_COND + + state = main_window_get_current_state(mainwin); + + n = sizeof(entry) / sizeof(entry[0]); + for (i = 0; i < n; i++) { + if (entry[i].widget) { + prev_sensitive = + GTK_WIDGET_IS_SENSITIVE(entry[i].widget); + sensitive = ((entry[i].cond & state) == entry[i].cond); + if (prev_sensitive != sensitive) { + /* workaround for GTK+ bug (#56070) */ + if (!prev_sensitive) + gtk_widget_hide(entry[i].widget); + gtk_widget_set_sensitive(entry[i].widget, + sensitive); + if (!prev_sensitive) + gtk_widget_show(entry[i].widget); + } + } + } +} + +static void main_window_set_toolbar_button_visibility(MainWindow *mainwin) +{ + GtkToolbarStyle style = GTK_TOOLBAR_BOTH_HORIZ; + + if (prefs_common.toolbar_style == TOOLBAR_NONE) + style = -1; + else if (prefs_common.toolbar_style == TOOLBAR_ICON) + style = GTK_TOOLBAR_ICONS; + else if (prefs_common.toolbar_style == TOOLBAR_TEXT) + style = GTK_TOOLBAR_TEXT; + else if (prefs_common.toolbar_style == TOOLBAR_BOTH) + style = GTK_TOOLBAR_BOTH; + else if (prefs_common.toolbar_style == TOOLBAR_BOTH_HORIZ) + style = GTK_TOOLBAR_BOTH_HORIZ; + + if (style != -1) { + gtk_toolbar_set_style(GTK_TOOLBAR(mainwin->toolbar), style); + gtk_widget_show(mainwin->toolbar); + gtk_widget_queue_resize(mainwin->toolbar); + } else + gtk_widget_hide(mainwin->toolbar); + +#if 0 + if (mainwin->junk_btn) { + if (prefs_common.enable_junk) + gtk_widget_show(mainwin->junk_btn); + else + gtk_widget_hide(mainwin->junk_btn); + } + + if (mainwin->exec_btn) { + if (prefs_common.immediate_exec) + gtk_widget_hide(mainwin->exec_btn); + else + gtk_widget_show(mainwin->exec_btn); + } +#endif +} + +void main_window_set_menu_sensitive(MainWindow *mainwin) +{ + GtkItemFactory *ifactory = mainwin->menu_factory; + SensitiveCond state; + gboolean sensitive; + GtkWidget *menu; + GtkWidget *menuitem; + FolderItem *item; + gchar *menu_path; + gint i; + GList *cur_item; + + static const struct { + gchar *const entry; + SensitiveCond cond; + } entry[] = { + {"/File/Folder/Create new folder...", M_UNLOCKED|M_FOLDER_NEWOK}, + {"/File/Folder/Rename folder..." , M_UNLOCKED|M_FOLDER_RENOK}, + {"/File/Folder/Move folder..." , M_UNLOCKED|M_FOLDER_MOVEOK}, + {"/File/Folder/Delete folder" , M_UNLOCKED|M_FOLDER_DELOK}, + {"/File/Mailbox/Add mailbox..." , M_UNLOCKED|M_MBOX_ADDOK}, + {"/File/Mailbox/Remove mailbox" , M_UNLOCKED|M_MBOX_RMOK}, + {"/File/Mailbox/Check for new messages" + , M_UNLOCKED|M_MBOX_CHKOK}, + {"/File/Mailbox/Check for new messages in all mailboxes" + , M_UNLOCKED|M_MBOX_CHKALLOK}, + {"/File/Mailbox/Rebuild folder tree", M_UNLOCKED|M_MBOX_REBUILDOK}, + {"/File/Import mail data..." , M_UNLOCKED}, + {"/File/Export mail data..." , M_UNLOCKED}, + {"/File/Empty all trash" , M_UNLOCKED}, + + {"/File/Save as..." , M_SINGLE_TARGET_EXIST}, + {"/File/Print..." , M_TARGET_EXIST}, + {"/File/Work offline", M_UNLOCKED}, + /* {"/File/Close" , M_UNLOCKED}, */ + {"/File/Exit" , M_UNLOCKED}, + + {"/Edit/Select thread" , M_SINGLE_TARGET_EXIST}, + + {"/View/Sort" , M_EXEC}, + {"/View/Thread view" , M_EXEC}, + {"/View/Expand all threads" , M_MSG_EXIST}, + {"/View/Collapse all threads" , M_MSG_EXIST}, + {"/View/Go to/Prev message" , M_MSG_EXIST}, + {"/View/Go to/Next message" , M_MSG_EXIST}, + {"/View/Go to/Prev unread message" , M_MSG_EXIST}, + {"/View/Go to/Next unread message" , M_MSG_EXIST}, + {"/View/Go to/Prev new message" , M_MSG_EXIST}, + {"/View/Go to/Next new message" , M_MSG_EXIST}, + {"/View/Go to/Prev marked message" , M_MSG_EXIST}, + {"/View/Go to/Next marked message" , M_MSG_EXIST}, + {"/View/Go to/Prev labeled message", M_MSG_EXIST}, + {"/View/Go to/Next labeled message", M_MSG_EXIST}, + {"/View/Open in new window" , M_SINGLE_TARGET_EXIST}, + {"/View/All headers" , M_SINGLE_TARGET_EXIST}, + {"/View/Message source" , M_SINGLE_TARGET_EXIST}, + + {"/Message/Receive/Get from current account" + , M_HAVE_ACCOUNT|M_UNLOCKED}, + {"/Message/Receive/Get from all accounts" + , M_HAVE_ACCOUNT|M_UNLOCKED}, + {"/Message/Receive/Stop receiving" + , M_INC_ACTIVE}, + {"/Message/Receive/Remote mailbox..." + , M_HAVE_ACCOUNT|M_UNLOCKED|M_POP3_ACCOUNT}, + {"/Message/Send queued messages" , M_HAVE_ACCOUNT|M_UNLOCKED|M_HAVE_QUEUED_MSG}, + + {"/Message/Compose new message" , M_HAVE_ACCOUNT}, + {"/Message/Reply" , M_HAVE_ACCOUNT|M_SINGLE_TARGET_EXIST}, + {"/Message/Reply to" , M_HAVE_ACCOUNT|M_SINGLE_TARGET_EXIST}, + {"/Message/Forward" , M_HAVE_ACCOUNT|M_TARGET_EXIST}, + {"/Message/Forward as attachment", M_HAVE_ACCOUNT|M_TARGET_EXIST}, + {"/Message/Redirect" , M_HAVE_ACCOUNT|M_SINGLE_TARGET_EXIST}, + {"/Message/Move..." , M_TARGET_EXIST|M_ALLOW_DELETE}, + {"/Message/Copy..." , M_TARGET_EXIST|M_EXEC}, + {"/Message/Delete" , M_TARGET_EXIST|M_ALLOW_DELETE}, + {"/Message/Mark" , M_TARGET_EXIST}, + {"/Message/Set as junk mail" , M_TARGET_EXIST|M_ALLOW_DELETE|M_ENABLE_JUNK}, + {"/Message/Set as not junk mail" , M_TARGET_EXIST|M_ALLOW_DELETE|M_ENABLE_JUNK}, + {"/Message/Re-edit" , M_HAVE_ACCOUNT|M_ALLOW_REEDIT}, + + {"/Tools/Add sender to address book...", M_SINGLE_TARGET_EXIST}, + {"/Tools/Filter all messages in folder", M_MSG_EXIST|M_EXEC}, + {"/Tools/Filter selected messages" , M_TARGET_EXIST|M_EXEC}, + {"/Tools/Create filter rule" , M_SINGLE_TARGET_EXIST|M_UNLOCKED}, + {"/Tools/Filter junk mails in folder" , M_MSG_EXIST|M_EXEC|M_ENABLE_JUNK}, + {"/Tools/Filter junk mails in selected messages", M_TARGET_EXIST|M_EXEC|M_ENABLE_JUNK}, +#ifndef G_OS_WIN32 + {"/Tools/Actions" , M_TARGET_EXIST|M_UNLOCKED}, +#endif + {"/Tools/Execute marked process" , M_MSG_EXIST|M_EXEC}, + {"/Tools/Delete duplicated messages" , M_MSG_EXIST|M_ALLOW_DELETE}, + {"/Tools/Concatenate separated messages" + , M_TARGET_EXIST|M_UNLOCKED|M_ALLOW_DELETE}, + + {"/Configuration/Common preferences...", M_UNLOCKED}, + {"/Configuration/Filter settings...", M_UNLOCKED}, + {"/Configuration/Preferences for current account...", M_UNLOCKED}, + {"/Configuration/Create new account...", M_UNLOCKED}, + {"/Configuration/Edit accounts...", M_UNLOCKED}, + {"/Configuration/Change current account", M_UNLOCKED}, + + {NULL, 0} + }; + + state = main_window_get_current_state(mainwin); + + for (i = 0; entry[i].entry != NULL; i++) { + sensitive = ((entry[i].cond & state) == entry[i].cond); + menu_set_sensitive(ifactory, entry[i].entry, sensitive); + } + + menu = gtk_item_factory_get_widget(ifactory, "/Message/Receive"); + + /* search for separator */ + for (cur_item = GTK_MENU_SHELL(menu)->children; cur_item != NULL; + cur_item = cur_item->next) { + if (GTK_BIN(cur_item->data)->child == NULL) { + cur_item = cur_item->next; + break; + } + } + + for (; cur_item != NULL; cur_item = cur_item->next) { + gtk_widget_set_sensitive(GTK_WIDGET(cur_item->data), + (M_UNLOCKED & state) != 0); + } + + main_window_menu_callback_block(mainwin); + +#define SET_CHECK_MENU_ACTIVE(path, active) \ +{ \ + menuitem = gtk_item_factory_get_widget(ifactory, path); \ + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), active); \ +} + + SET_CHECK_MENU_ACTIVE("/View/Show or hide/Message view", + messageview_is_visible(mainwin->messageview)); + + item = mainwin->summaryview->folder_item; + menu_path = "/View/Sort/Don't sort"; + if (item) { + switch (item->sort_key) { + case SORT_BY_NUMBER: + menu_path = "/View/Sort/by number"; break; + case SORT_BY_SIZE: + menu_path = "/View/Sort/by size"; break; + case SORT_BY_DATE: + menu_path = "/View/Sort/by date"; break; + case SORT_BY_TDATE: + menu_path = "/View/Sort/by thread date"; break; + case SORT_BY_FROM: + menu_path = "/View/Sort/by from"; break; + case SORT_BY_TO: + menu_path = "/View/Sort/by recipient"; break; + case SORT_BY_SUBJECT: + menu_path = "/View/Sort/by subject"; break; + case SORT_BY_LABEL: + menu_path = "/View/Sort/by color label"; break; + case SORT_BY_MARK: + menu_path = "/View/Sort/by mark"; break; + case SORT_BY_UNREAD: + menu_path = "/View/Sort/by unread"; break; + case SORT_BY_MIME: + menu_path = "/View/Sort/by attachment"; break; + case SORT_BY_NONE: + default: + menu_path = "/View/Sort/Don't sort"; break; + } + } + SET_CHECK_MENU_ACTIVE(menu_path, TRUE); + + if (!item || item->sort_type == SORT_ASCENDING) { + SET_CHECK_MENU_ACTIVE("/View/Sort/Ascending", TRUE); + } else { + SET_CHECK_MENU_ACTIVE("/View/Sort/Descending", TRUE); + } + + if (item && item->sort_key != SORT_BY_NONE) { + menu_set_sensitive(ifactory, "/View/Sort/Ascending", TRUE); + menu_set_sensitive(ifactory, "/View/Sort/Descending", TRUE); + menu_set_sensitive(ifactory, "/View/Sort/Attract by subject", + FALSE); + } else { + menu_set_sensitive(ifactory, "/View/Sort/Ascending", FALSE); + menu_set_sensitive(ifactory, "/View/Sort/Descending", FALSE); + menu_set_sensitive(ifactory, "/View/Sort/Attract by subject", + (item != NULL)); + } + + SET_CHECK_MENU_ACTIVE("/View/All headers", + mainwin->messageview->textview->show_all_headers); + SET_CHECK_MENU_ACTIVE("/View/Thread view", (state & M_THREADED) != 0); + +#undef SET_CHECK_MENU_ACTIVE + + main_window_menu_callback_unblock(mainwin); +} + +void main_window_popup(MainWindow *mainwin) +{ + gtkut_window_popup(mainwin->window); + + switch (mainwin->type) { + case SEPARATE_FOLDER: + if (prefs_common.folderview_visible) + gtkut_window_popup(mainwin->win.sep_folder.folderwin); + break; + case SEPARATE_MESSAGE: + if (messageview_is_visible(mainwin->messageview)) + gtkut_window_popup(mainwin->win.sep_message.messagewin); + break; + case SEPARATE_BOTH: + if (prefs_common.folderview_visible) + gtkut_window_popup(mainwin->win.sep_both.folderwin); + if (messageview_is_visible(mainwin->messageview)) + gtkut_window_popup(mainwin->win.sep_both.messagewin); + break; + default: + break; + } +} + +static void main_window_set_widgets(MainWindow *mainwin, LayoutType layout, + SeparateType type) +{ + GtkWidget *folderwin = NULL; + GtkWidget *messagewin = NULL; + GtkWidget *hpaned; + GtkWidget *vpaned; + GtkWidget *vbox_body = mainwin->vbox_body; + GtkWidget *vbox_summary; + GtkItemFactory *ifactory = mainwin->menu_factory; + GtkWidget *menuitem; + gboolean use_vlayout = (layout == LAYOUT_VERTICAL); + + debug_print("Setting main window widgets...\n"); + + gtk_widget_set_size_request(GTK_WIDGET_PTR(mainwin->folderview), + prefs_common.folderview_width, + prefs_common.folderview_height); + if (use_vlayout) { + gtk_widget_set_size_request + (GTK_WIDGET_PTR(mainwin->summaryview), + prefs_common.summaryview_vwidth, + prefs_common.summaryview_vheight); + gtk_widget_set_size_request + (GTK_WIDGET_PTR(mainwin->messageview), + prefs_common.msgview_vwidth, + prefs_common.msgview_vheight); + } else { + gtk_widget_set_size_request + (GTK_WIDGET_PTR(mainwin->summaryview), + prefs_common.summaryview_width, + prefs_common.summaryview_height); + gtk_widget_set_size_request + (GTK_WIDGET_PTR(mainwin->messageview), + prefs_common.msgview_width, + prefs_common.msgview_height); + } + + /* create separated window(s) if needed */ + if (type & SEPARATE_FOLDER) { + folderwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(folderwin), + _("Sylpheed - Folder View")); + gtk_window_set_wmclass(GTK_WINDOW(folderwin), + "folder_view", "Sylpheed"); + gtk_window_set_policy(GTK_WINDOW(folderwin), + TRUE, TRUE, FALSE); + gtkut_window_move(GTK_WINDOW(folderwin), + prefs_common.folderwin_x, + prefs_common.folderwin_y); + gtk_container_set_border_width(GTK_CONTAINER(folderwin), 0); + g_signal_connect(G_OBJECT(folderwin), "delete_event", + G_CALLBACK(folder_window_close_cb), mainwin); + gtk_container_add(GTK_CONTAINER(folderwin), + GTK_WIDGET_PTR(mainwin->folderview)); + gtk_widget_realize(folderwin); + if (prefs_common.folderview_visible) + gtk_widget_show(folderwin); + } + if (type & SEPARATE_MESSAGE) { + messagewin = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(messagewin), + _("Sylpheed - Message View")); + gtk_window_set_wmclass(GTK_WINDOW(messagewin), + "message_view", "Sylpheed"); + gtk_window_set_policy(GTK_WINDOW(messagewin), + TRUE, TRUE, FALSE); + gtkut_window_move(GTK_WINDOW(messagewin), + prefs_common.main_msgwin_x, + prefs_common.main_msgwin_y); + gtk_container_set_border_width(GTK_CONTAINER(messagewin), 0); + g_signal_connect(G_OBJECT(messagewin), "delete_event", + G_CALLBACK(message_window_close_cb), mainwin); + gtk_container_add(GTK_CONTAINER(messagewin), + GTK_WIDGET_PTR(mainwin->messageview)); + gtk_widget_realize(messagewin); + if (messageview_is_visible(mainwin->messageview)) + gtk_widget_show(messagewin); + } + + vbox_summary = gtk_vbox_new(FALSE, 1); + gtk_box_pack_start(GTK_BOX(vbox_summary), + GTK_WIDGET_PTR(mainwin->summaryview->qsearch), + FALSE, FALSE, 0); + gtk_widget_show(vbox_summary); + + switch (type) { + case SEPARATE_NONE: + hpaned = gtk_hpaned_new(); + gtk_box_pack_start(GTK_BOX(vbox_body), hpaned, TRUE, TRUE, 0); + gtk_paned_add1(GTK_PANED(hpaned), + GTK_WIDGET_PTR(mainwin->folderview)); + gtk_paned_add2(GTK_PANED(hpaned), vbox_summary); + gtk_widget_show(hpaned); + gtk_widget_queue_resize(hpaned); + + if (use_vlayout) { + vpaned = gtk_hpaned_new(); + gtk_widget_hide(mainwin->summaryview->hseparator); + } else + vpaned = gtk_vpaned_new(); + if (messageview_is_visible(mainwin->messageview)) { + gtk_paned_add1(GTK_PANED(vpaned), + GTK_WIDGET_PTR(mainwin->summaryview)); + gtk_box_pack_start(GTK_BOX(vbox_summary), vpaned, + TRUE, TRUE, 0); + if (!use_vlayout) + gtk_widget_show + (mainwin->summaryview->hseparator); + } else { + gtk_box_pack_start(GTK_BOX(vbox_summary), + GTK_WIDGET_PTR(mainwin->summaryview), + TRUE, TRUE, 0); + gtk_widget_ref(vpaned); + gtk_widget_hide(mainwin->summaryview->hseparator); + } + gtk_paned_add2(GTK_PANED(vpaned), + GTK_WIDGET_PTR(mainwin->messageview)); + gtk_widget_show(vpaned); + gtk_widget_queue_resize(vpaned); + + mainwin->win.sep_none.hpaned = hpaned; + mainwin->win.sep_none.vpaned = vpaned; + + break; + case SEPARATE_FOLDER: + gtk_box_pack_start(GTK_BOX(vbox_body), vbox_summary, + TRUE, TRUE, 0); + + if (use_vlayout) { + vpaned = gtk_hpaned_new(); + gtk_widget_hide(mainwin->summaryview->hseparator); + } else + vpaned = gtk_vpaned_new(); + if (messageview_is_visible(mainwin->messageview)) { + gtk_paned_add1(GTK_PANED(vpaned), + GTK_WIDGET_PTR(mainwin->summaryview)); + gtk_box_pack_start(GTK_BOX(vbox_summary), vpaned, + TRUE, TRUE, 0); + if (!use_vlayout) + gtk_widget_show + (mainwin->summaryview->hseparator); + } else { + gtk_box_pack_start(GTK_BOX(vbox_summary), + GTK_WIDGET_PTR(mainwin->summaryview), + TRUE, TRUE, 0); + gtk_widget_ref(vpaned); + gtk_widget_hide(mainwin->summaryview->hseparator); + } + gtk_paned_add2(GTK_PANED(vpaned), + GTK_WIDGET_PTR(mainwin->messageview)); + gtk_widget_show(vpaned); + gtk_widget_queue_resize(vpaned); + + mainwin->win.sep_folder.folderwin = folderwin; + mainwin->win.sep_folder.vpaned = vpaned; + + break; + case SEPARATE_MESSAGE: + hpaned = gtk_hpaned_new(); + gtk_box_pack_start(GTK_BOX(vbox_body), hpaned, TRUE, TRUE, 0); + gtk_paned_add1(GTK_PANED(hpaned), + GTK_WIDGET_PTR(mainwin->folderview)); + gtk_paned_add2(GTK_PANED(hpaned), vbox_summary); + gtk_box_pack_start(GTK_BOX(vbox_summary), + GTK_WIDGET_PTR(mainwin->summaryview), + TRUE, TRUE, 0); + gtk_widget_hide(mainwin->summaryview->hseparator); + gtk_widget_show(hpaned); + gtk_widget_queue_resize(hpaned); + + mainwin->win.sep_message.messagewin = messagewin; + mainwin->win.sep_message.hpaned = hpaned; + + break; + case SEPARATE_BOTH: + gtk_box_pack_start(GTK_BOX(vbox_body), vbox_summary, + TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox_summary), + GTK_WIDGET_PTR(mainwin->summaryview), + TRUE, TRUE, 0); + gtk_widget_hide(mainwin->summaryview->hseparator); + + mainwin->win.sep_both.folderwin = folderwin; + mainwin->win.sep_both.messagewin = messagewin; + + break; + } + + if (messageview_is_visible(mainwin->messageview)) + gtk_arrow_set(GTK_ARROW(mainwin->summaryview->toggle_arrow), + use_vlayout ? GTK_ARROW_RIGHT : GTK_ARROW_DOWN, + GTK_SHADOW_OUT); + else + gtk_arrow_set(GTK_ARROW(mainwin->summaryview->toggle_arrow), + use_vlayout ? GTK_ARROW_LEFT : GTK_ARROW_UP, + GTK_SHADOW_OUT); + + gtkut_window_move(GTK_WINDOW(mainwin->window), + prefs_common.mainwin_x, prefs_common.mainwin_y); + + gtk_widget_queue_resize(vbox_body); + gtk_widget_queue_resize(mainwin->vbox); + gtk_widget_queue_resize(mainwin->window); + + mainwin->type = type; + prefs_common.layout_type = layout; + + /* toggle menu state */ + menuitem = gtk_item_factory_get_item + (ifactory, "/View/Show or hide/Folder tree"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), + (type & SEPARATE_FOLDER) == 0 ? TRUE : + prefs_common.folderview_visible); + gtk_widget_set_sensitive(menuitem, ((type & SEPARATE_FOLDER) != 0)); + menuitem = gtk_item_factory_get_item + (ifactory, "/View/Show or hide/Message view"); + gtk_check_menu_item_set_active + (GTK_CHECK_MENU_ITEM(menuitem), + messageview_is_visible(mainwin->messageview)); + + if (layout == LAYOUT_NORMAL) { + menuitem = gtk_item_factory_get_item + (ifactory, "/View/Layout/Normal"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), + TRUE); + } else if (layout == LAYOUT_VERTICAL) { + menuitem = gtk_item_factory_get_item + (ifactory, "/View/Layout/Vertical"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), + TRUE); + } + + menuitem = gtk_item_factory_get_item + (ifactory, "/View/Separate folder tree"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), + ((type & SEPARATE_FOLDER) != 0)); + menuitem = gtk_item_factory_get_item + (ifactory, "/View/Separate message view"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), + ((type & SEPARATE_MESSAGE) != 0)); + + if (folderwin) { + g_signal_connect + (G_OBJECT(folderwin), "size_allocate", + G_CALLBACK(folder_window_size_allocate_cb), mainwin); + } + if (messagewin) { + g_signal_connect + (G_OBJECT(messagewin), "size_allocate", + G_CALLBACK(message_window_size_allocate_cb), mainwin); + } + + debug_print("done.\n"); +} + +static GtkItemFactoryEntry reply_entries[] = +{ + {N_("/_Reply"), NULL, reply_cb, COMPOSE_REPLY, NULL}, + {N_("/Reply to _all"), NULL, reply_cb, COMPOSE_REPLY_TO_ALL, NULL}, + {N_("/Reply to _sender"), NULL, reply_cb, COMPOSE_REPLY_TO_SENDER, NULL}, + {N_("/Reply to mailing _list"), NULL, reply_cb, COMPOSE_REPLY_TO_LIST, NULL} +}; + +static GtkItemFactoryEntry forward_entries[] = +{ + {N_("/_Forward"), NULL, reply_cb, COMPOSE_FORWARD, NULL}, + {N_("/For_ward as attachment"), NULL, reply_cb, COMPOSE_FORWARD_AS_ATTACH, NULL}, + {N_("/Redirec_t"), NULL, reply_cb, COMPOSE_REDIRECT, NULL} +}; + +static PrefsToolbarItem items[] = +{ + {T_GET, TRUE, toolbar_inc_cb}, + {T_GET_ALL, TRUE, toolbar_inc_all_cb}, + {T_SEND_QUEUE, TRUE, toolbar_send_cb}, + {T_COMPOSE, TRUE, toolbar_compose_cb}, + {T_REPLY, TRUE, toolbar_reply_cb}, + {T_REPLY_ALL, TRUE, toolbar_reply_to_all_cb}, + {T_FORWARD, TRUE, toolbar_forward_cb}, + {T_DELETE, FALSE, toolbar_delete_cb}, + {T_JUNK, TRUE, toolbar_junk_cb}, + {T_NOTJUNK, FALSE, toolbar_notjunk_cb}, + {T_NEXT, FALSE, toolbar_next_unread_cb}, + {T_PREV, FALSE, toolbar_prev_unread_cb}, + {T_SEARCH, FALSE, toolbar_search_cb}, + {T_PRINT, FALSE, toolbar_print_cb}, + {T_STOP, FALSE, toolbar_stop_cb}, + {T_ADDRESS_BOOK, FALSE, toolbar_address_cb}, + {T_EXECUTE, FALSE, toolbar_exec_cb}, + {T_COMMON_PREFS, FALSE, toolbar_prefs_common_cb}, + {T_ACCOUNT_PREFS, FALSE, toolbar_prefs_account_cb}, + {T_REMOTE_MAILBOX, FALSE, toolbar_rpop3_cb}, + + {-1, FALSE, NULL} +}; + +static GtkWidget *main_window_toolbar_create(MainWindow *mainwin) +{ + GtkWidget *toolbar; + const gchar *setting; + GList *item_list; + + if (prefs_common.main_toolbar_setting && + *prefs_common.main_toolbar_setting != '\0') + setting = prefs_common.main_toolbar_setting; + else + setting = prefs_toolbar_get_default_main_setting_name_list(); + + item_list = prefs_toolbar_get_item_list_from_name_list(setting); + toolbar = main_window_toolbar_create_from_list(mainwin, item_list); + g_list_free(item_list); + + return toolbar; +} + +static GtkWidget *main_window_toolbar_create_from_list(MainWindow *mainwin, + GList *item_list) +{ + GtkWidget *toolbar; + GtkWidget *icon_wid; + GtkToolItem *toolitem; + GtkToolItem *comboitem; + ComboButton *combo; + gint n_entries; + gint i; + GList *cur; + + toolbar = gtk_toolbar_new(); + gtk_toolbar_set_orientation(GTK_TOOLBAR(toolbar), + GTK_ORIENTATION_HORIZONTAL); + gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH); + g_signal_connect(G_OBJECT(toolbar), "button_press_event", + G_CALLBACK(toolbar_button_pressed), mainwin); + + items[0].data = &mainwin->get_btn; + items[1].data = &mainwin->getall_btn; + items[2].data = &mainwin->send_btn; + items[3].data = &mainwin->compose_btn; + items[4].data = &mainwin->reply_btn; + items[5].data = &mainwin->replyall_btn; + items[6].data = &mainwin->fwd_btn; + items[7].data = &mainwin->delete_btn; + items[8].data = &mainwin->junk_btn; + items[9].data = &mainwin->notjunk_btn; + items[10].data = &mainwin->next_btn; + items[11].data = &mainwin->prev_btn; + items[12].data = &mainwin->search_btn; + items[13].data = &mainwin->print_btn; + items[14].data = &mainwin->stop_btn; + items[15].data = &mainwin->address_btn; + items[16].data = &mainwin->exec_btn; + items[17].data = &mainwin->prefs_common_btn; + items[18].data = &mainwin->prefs_account_btn; + items[19].data = &mainwin->rpop3_btn; + for (i = 0; i <= 19; i++) + *(GtkWidget **)items[i].data = NULL; + mainwin->reply_combo = NULL; + mainwin->fwd_combo = NULL; + + for (cur = item_list; cur != NULL; cur = cur->next) { + const PrefsDisplayItem *ditem = cur->data; + PrefsToolbarItem *item; + gint width; + + if (ditem->id == T_SEPARATOR) { + toolitem = gtk_separator_tool_item_new(); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); + continue; + } + + for (item = items; item->id != -1; item++) { + if (ditem->id == item->id) + break; + } + if (item->id == -1) + continue; + + icon_wid = stock_pixbuf_widget_for_toolbar(ditem->icon); + toolitem = gtk_tool_button_new(icon_wid, gettext(ditem->label)); + if (ditem->description) { + gtk_tool_item_set_tooltip(toolitem, + mainwin->toolbar_tip, + gettext(ditem->description), + ditem->name); + } + + gtkut_get_str_size(GTK_WIDGET(toolitem), gettext(ditem->label), + &width, NULL); + gtk_tool_item_set_homogeneous + (toolitem, width < 52 ? TRUE : FALSE); + gtk_tool_item_set_is_important(toolitem, item->is_important); + + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); + + g_signal_connect(G_OBJECT(toolitem), "clicked", + G_CALLBACK(item->callback), mainwin); + g_signal_connect(G_OBJECT(GTK_BIN(toolitem)->child), + "button_press_event", + G_CALLBACK(toolbar_button_pressed), mainwin); + + if (ditem->id == T_REPLY) { + n_entries = sizeof(reply_entries) / + sizeof(reply_entries[0]); + combo = gtkut_combo_button_create + (GTK_WIDGET(toolitem), + reply_entries, n_entries, "", mainwin); + gtk_button_set_relief(GTK_BUTTON(combo->arrow), + GTK_RELIEF_NONE); + + comboitem = gtk_tool_item_new(); + gtk_tool_item_set_homogeneous(comboitem, FALSE); + gtk_container_add(GTK_CONTAINER(comboitem), + GTK_WIDGET_PTR(combo)); + if (ditem->description) { + gtk_tool_item_set_tooltip + (comboitem, mainwin->toolbar_tip, + gettext(ditem->description), + ditem->name); + } + + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), comboitem, -1); + + mainwin->reply_combo = combo; + } else if (ditem->id == T_FORWARD) { + n_entries = sizeof(forward_entries) / + sizeof(forward_entries[0]); + combo = gtkut_combo_button_create + (GTK_WIDGET(toolitem), + forward_entries, n_entries, "", + mainwin); + gtk_button_set_relief(GTK_BUTTON(combo->arrow), + GTK_RELIEF_NONE); + + comboitem = gtk_tool_item_new(); + gtk_tool_item_set_homogeneous(comboitem, FALSE); + gtk_container_add(GTK_CONTAINER(comboitem), + GTK_WIDGET_PTR(combo)); + if (ditem->description) { + gtk_tool_item_set_tooltip + (comboitem, mainwin->toolbar_tip, + gettext(ditem->description), + ditem->name); + } + + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), comboitem, -1); + + mainwin->fwd_combo = combo; + } + + *(GtkWidget **)item->data = GTK_WIDGET(toolitem); + } + + gtk_widget_show_all(toolbar); + + return toolbar; +} + +static void main_window_toolbar_toggle_menu_set_active(MainWindow *mainwin, + ToolbarStyle style) +{ + GtkWidget *menuitem = NULL; + GtkItemFactory *ifactory = mainwin->menu_factory; + + switch (style) { + case TOOLBAR_NONE: + menuitem = gtk_item_factory_get_item + (ifactory, "/View/Show or hide/Toolbar/None"); + break; + case TOOLBAR_ICON: + menuitem = gtk_item_factory_get_item + (ifactory, "/View/Show or hide/Toolbar/Icon"); + break; + case TOOLBAR_TEXT: + menuitem = gtk_item_factory_get_item + (ifactory, "/View/Show or hide/Toolbar/Text"); + break; + case TOOLBAR_BOTH: + menuitem = gtk_item_factory_get_item + (ifactory, "/View/Show or hide/Toolbar/Icon and text"); + break; + case TOOLBAR_BOTH_HORIZ: + menuitem = gtk_item_factory_get_item + (ifactory, "/View/Show or hide/Toolbar/Text at the right of icon"); + break; + } + + if (menuitem) + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), + TRUE); +} + +/* callback functions */ + +static void toolbar_inc_cb (GtkWidget *widget, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + inc_mail_cb(mainwin, 0, NULL); +} + +static void toolbar_inc_all_cb (GtkWidget *widget, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + inc_all_account_mail_cb(mainwin, 0, NULL); +} + +static void toolbar_rpop3_cb (GtkWidget *widget, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + rpop3_cb(mainwin, 0, NULL); +} + +static void toolbar_send_cb (GtkWidget *widget, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + send_queue_cb(mainwin, 0, NULL); +} + +static void toolbar_compose_cb (GtkWidget *widget, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + compose_cb(mainwin, 0, NULL); +} + +static void toolbar_reply_cb (GtkWidget *widget, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + if (prefs_common.default_reply_list) + reply_cb(mainwin, COMPOSE_REPLY_TO_LIST, NULL); + else + reply_cb(mainwin, COMPOSE_REPLY, NULL); +} + +static void toolbar_reply_to_all_cb (GtkWidget *widget, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + reply_cb(mainwin, COMPOSE_REPLY_TO_ALL, NULL); +} + +static void toolbar_forward_cb (GtkWidget *widget, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + reply_cb(mainwin, COMPOSE_FORWARD, NULL); +} + +static void toolbar_delete_cb (GtkWidget *widget, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + summary_delete(mainwin->summaryview); +} + +static void toolbar_junk_cb (GtkWidget *widget, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + summary_junk(mainwin->summaryview); +} + +static void toolbar_notjunk_cb (GtkWidget *widget, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + summary_not_junk(mainwin->summaryview); +} + +static void toolbar_exec_cb (GtkWidget *widget, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + summary_execute(mainwin->summaryview); +} + +static void toolbar_next_unread_cb (GtkWidget *widget, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + next_unread_cb(mainwin, 0, NULL); +} + +static void toolbar_prev_unread_cb(GtkWidget *widget, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + prev_unread_cb(mainwin, 0, NULL); +} + +static void toolbar_address_cb(GtkWidget *widget, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + addressbook_open_cb(mainwin, 0, NULL); +} + +static void toolbar_search_cb(GtkWidget *widget, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + search_cb(mainwin, 1, NULL); +} + +static void toolbar_print_cb(GtkWidget *widget, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + print_cb(mainwin, 0, NULL); +} + +static void toolbar_stop_cb(GtkWidget *widget, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + inc_stop_cb(mainwin, 0, NULL); +} + +static void toolbar_prefs_common_cb(GtkWidget *widget, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + prefs_common_open_cb(mainwin, 0, NULL); +} + +static void toolbar_prefs_account_cb(GtkWidget *widget, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + prefs_account_open_cb(mainwin, 0, NULL); +} + +static void toolbar_toggle(GtkWidget *widget, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + ToolbarStyle style; + + if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) + return; + + style = (ToolbarStyle)g_object_get_data(G_OBJECT(widget), MENU_VAL_ID); + main_window_toolbar_toggle_menu_set_active(mainwin, style); +} + +static void toolbar_customize(GtkWidget *widget, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + gint *visible_items; + GList *item_list = NULL; + GtkWidget *toolbar; + gint ret; + const gchar *setting; + + if (prefs_common.main_toolbar_setting && + *prefs_common.main_toolbar_setting != '\0') + setting = prefs_common.main_toolbar_setting; + else + setting = prefs_toolbar_get_default_main_setting_name_list(); + visible_items = prefs_toolbar_get_id_list_from_name_list(setting); + ret = prefs_toolbar_open(TOOLBAR_MAIN, visible_items, &item_list); + g_free(visible_items); + + if (ret == 0) { + gtk_widget_destroy(mainwin->toolbar); + toolbar = main_window_toolbar_create_from_list(mainwin, + item_list); + gtk_widget_set_size_request(toolbar, 300, -1); + gtk_box_pack_start(GTK_BOX(mainwin->vbox), toolbar, + FALSE, FALSE, 0); + gtk_box_reorder_child(GTK_BOX(mainwin->vbox), toolbar, 1); + mainwin->toolbar = toolbar; + main_window_set_toolbar_sensitive(mainwin); + main_window_set_toolbar_button_visibility(mainwin); + g_free(prefs_common.main_toolbar_setting); + prefs_common.main_toolbar_setting = + prefs_toolbar_get_name_list_from_item_list(item_list); + g_list_free(item_list); + prefs_common_write_config(); + + syl_plugin_signal_emit("main-window-toolbar-changed"); + } +} + +static gboolean toolbar_button_pressed(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + GtkWidget *menu; + GtkWidget *menuitem; + + if (!event) return FALSE; + if (event->button != 3) return FALSE; + + menu = gtk_menu_new(); + gtk_widget_show(menu); + +#define SET_TOOLBAR_MENU(text, style, widget) \ +{ \ + MENUITEM_ADD_RADIO(menu, menuitem, widget, text, style); \ + if (prefs_common.toolbar_style == style) \ + gtk_check_menu_item_set_active \ + (GTK_CHECK_MENU_ITEM(menuitem), TRUE); \ + g_signal_connect(G_OBJECT(menuitem), "activate", \ + G_CALLBACK(toolbar_toggle), mainwin); \ +} + + SET_TOOLBAR_MENU(_("Icon _and text"), TOOLBAR_BOTH, NULL); + SET_TOOLBAR_MENU(_("Text at the _right of icon"), TOOLBAR_BOTH_HORIZ, + menuitem); + SET_TOOLBAR_MENU(_("_Icon"), TOOLBAR_ICON, menuitem); + SET_TOOLBAR_MENU(_("_Text"), TOOLBAR_TEXT, menuitem); + SET_TOOLBAR_MENU(_("_None"), TOOLBAR_NONE, menuitem); + MENUITEM_ADD(menu, menuitem, NULL, NULL); + MENUITEM_ADD_WITH_MNEMONIC(menu, menuitem, _("_Customize toolbar..."), + 0); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(toolbar_customize), mainwin); + + g_signal_connect(G_OBJECT(menu), "selection_done", + G_CALLBACK(gtk_widget_destroy), NULL); + + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, + event->button, event->time); + + return TRUE; +} + +static void online_switch_clicked(GtkWidget *widget, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + GtkWidget *menuitem; + + debug_print("Toggle online mode: %d -> %d\n", prefs_common.online_mode, + !prefs_common.online_mode); + + menuitem = gtk_item_factory_get_item(mainwin->menu_factory, + "/File/Work offline"); + + if (prefs_common.online_mode == TRUE) { + if (folder_remote_folder_active_session_exist()) { + debug_print("Active session exist. Cancelling online switch.\n"); + return; + } + + prefs_common.online_mode = FALSE; + gtk_widget_hide(mainwin->online_pixmap); + gtk_widget_show(mainwin->offline_pixmap); + gtk_tooltips_set_tip + (mainwin->online_tip, mainwin->online_switch, + _("You are offline. Click the icon to go online."), + NULL); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), + TRUE); + inc_autocheck_timer_remove(); + folder_remote_folder_destroy_all_sessions(); + } else { + prefs_common.online_mode = TRUE; + gtk_widget_hide(mainwin->offline_pixmap); + gtk_widget_show(mainwin->online_pixmap); + gtk_tooltips_set_tip + (mainwin->online_tip, mainwin->online_switch, + _("You are online. Click the icon to go offline."), + NULL); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), + FALSE); + inc_autocheck_timer_set(); + } +} + +static gboolean ac_label_button_pressed(GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + if (!event) return FALSE; + + gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NORMAL); + g_object_set_data(G_OBJECT(mainwin->ac_menu), "menu_button", widget); + + gtk_menu_popup(GTK_MENU(mainwin->ac_menu), NULL, NULL, + menu_button_position, widget, + event->button, event->time); + + return TRUE; +} + +static void ac_menu_popup_closed(GtkMenuShell *menu_shell, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + GtkWidget *button; + + button = g_object_get_data(G_OBJECT(menu_shell), "menu_button"); + if (!button) return; + gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + g_object_set_data(G_OBJECT(mainwin->ac_menu), "menu_button", NULL); + manage_window_focus_in(mainwin->window, NULL, NULL); +} + +static gboolean main_window_key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + if (!mainwin) + return FALSE; + + if (!GTK_WIDGET_HAS_FOCUS(mainwin->summaryview->qsearch->entry)) + return FALSE; + + /* g_print("keyval: %d, state: %d\n", event->keyval, event->state); */ + if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) + return FALSE; + if (event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab || + event->keyval == GDK_ISO_Left_Tab) + return FALSE; + + gtk_window_propagate_key_event(GTK_WINDOW(widget), event); + + return TRUE; +} + +static gint main_window_close_cb(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + if (mainwin->lock_count == 0) + app_exit_cb(data, 0, widget); + + return TRUE; +} + +static gint folder_window_close_cb(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + GtkWidget *menuitem; + + menuitem = gtk_item_factory_get_item + (mainwin->menu_factory, "/View/Show or hide/Folder tree"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), FALSE); + + return TRUE; +} + +static gint message_window_close_cb(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + GtkWidget *menuitem; + + menuitem = gtk_item_factory_get_item + (mainwin->menu_factory, "/View/Show or hide/Message view"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), FALSE); + + return TRUE; +} + +static void main_window_size_allocate_cb(GtkWidget *widget, + GtkAllocation *allocation, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + main_window_get_size(mainwin); +} + +static void folder_window_size_allocate_cb(GtkWidget *widget, + GtkAllocation *allocation, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + main_window_get_size(mainwin); +} + +static void message_window_size_allocate_cb(GtkWidget *widget, + GtkAllocation *allocation, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + main_window_get_size(mainwin); +} + +static gboolean main_window_window_state_cb(GtkWidget *widget, + GdkEventWindowState *event, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + gboolean minimized = FALSE; + + debug_print("main_window_window_state_cb\n"); + + if ((event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) != 0) { + if ((event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0) { + debug_print("main_window_window_state_cb: maximized\n"); + prefs_common.mainwin_maximized = TRUE; + } else { + debug_print("main_window_window_state_cb: unmaximized\n"); +#ifdef G_OS_WIN32 + if ((event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) != 0 && + (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) != 0) { + debug_print("main_window_window_state_cb: unmaximized by minimize\n"); + /* don't change mainwin_maximized */ + } else { + prefs_common.mainwin_maximized = FALSE; + } +#else + prefs_common.mainwin_maximized = FALSE; +#endif + } + } + if ((event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) != 0) { + if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) != 0) { + debug_print("main_window_window_state_cb: iconified\n"); + minimized = TRUE; + mainwin->window_hidden = TRUE; + } else { + debug_print("main_window_window_state_cb: deiconified\n"); + mainwin->window_hidden = FALSE; + } + } + if ((event->changed_mask & GDK_WINDOW_STATE_WITHDRAWN) != 0) { + if ((event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) != 0) { + debug_print("main_window_window_state_cb: withdrawn\n"); + mainwin->window_hidden = TRUE; + } else { + debug_print("main_window_window_state_cb: unwithdrawn\n"); + mainwin->window_hidden = FALSE; + } + } + +#ifdef G_OS_WIN32 + if (minimized && + prefs_common.show_trayicon && prefs_common.minimize_to_tray) { + gtk_window_set_skip_taskbar_hint(GTK_WINDOW(widget), TRUE); + debug_print("main_window_window_state_cb: hide window\n"); + gtk_widget_hide(widget); +#else + if (mainwin->window_hidden && + prefs_common.show_trayicon && prefs_common.minimize_to_tray) { + gtk_window_set_skip_taskbar_hint(GTK_WINDOW(widget), TRUE); +#endif + } else if (!mainwin->window_hidden) { + gtk_window_set_skip_taskbar_hint(GTK_WINDOW(widget), FALSE); +#ifdef G_OS_WIN32 + if (prefs_common.mainwin_maximized) + gtk_window_maximize(GTK_WINDOW(widget)); +#endif + } + + return FALSE; +} + +static gboolean main_window_visibility_notify_cb(GtkWidget *widget, + GdkEventVisibility *event, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + mainwin->window_obscured = + (event->state == GDK_VISIBILITY_FULLY_OBSCURED || + event->state == GDK_VISIBILITY_PARTIAL) ? TRUE : FALSE; + + return FALSE; +} + +static void new_folder_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + folderview_new_folder(mainwin->folderview); +} + +static void rename_folder_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + folderview_rename_folder(mainwin->folderview); +} + +static void move_folder_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + folderview_move_folder(mainwin->folderview); +} + +static void delete_folder_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + folderview_delete_folder(mainwin->folderview); +} + +static void add_mailbox_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + main_window_add_mailbox(mainwin); +} + +static void remove_mailbox_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + folderview_remove_mailbox(mainwin->folderview); +} + +static void update_folderview_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + if (action == 0) + folderview_check_new_selected(mainwin->folderview); + else + folderview_check_new_all(); +} + +static void rebuild_tree_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + folderview_rebuild_tree(mainwin->folderview); +} + +static void import_mail_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + import_mail(mainwin->summaryview->folder_item); + main_window_popup(mainwin); +} + +static void export_mail_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + export_mail(mainwin->summaryview->folder_item); + main_window_popup(mainwin); +} + +static void empty_trash_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + main_window_empty_trash(mainwin, TRUE); +} + +static void save_as_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + MessageView *messageview = mainwin->messageview; + + if (messageview_get_selected_mime_part(messageview) && + GTK_WIDGET_HAS_FOCUS(messageview->mimeview->treeview)) + mimeview_save_as(messageview->mimeview); + else + summary_save_as(mainwin->summaryview); +} + +#if GTK_CHECK_VERSION(2, 10, 0) +static void page_setup_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + printing_page_setup_gtk(); +} +#endif + +static void print_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + summary_print(mainwin->summaryview); +} + +static void toggle_offline_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + if (GTK_CHECK_MENU_ITEM(widget)->active && + folder_remote_folder_active_session_exist()) { + debug_print("Active session exist. Cancelling online switch.\n"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), + FALSE); + return; + } + + main_window_toggle_online + (mainwin, !GTK_CHECK_MENU_ITEM(widget)->active); +} + +static void app_exit_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + if (prefs_common.confirm_on_exit) { + if (alertpanel(_("Exit"), _("Exit this program?"), + GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL) + != G_ALERTDEFAULT) + return; + manage_window_focus_in(mainwin->window, NULL, NULL); + } + + app_will_exit(FALSE); +} + +static void search_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + if (action == 1) { + FolderItem *item; + + item = folderview_get_selected_item(mainwin->folderview); + if (item && item->stype == F_VIRTUAL) + prefs_search_folder_open(item); + else + query_search(item); + } else if (action == 2) { + if (!prefs_common.show_searchbar) { + GtkWidget *menuitem; + + menuitem = gtk_item_factory_get_item + (mainwin->menu_factory, + "/View/Show or hide/Search bar"); + gtk_check_menu_item_set_active + (GTK_CHECK_MENU_ITEM(menuitem), TRUE); + } + gtk_widget_grab_focus(mainwin->summaryview->qsearch->entry); + } else + message_search(mainwin->messageview); +} + +static void toggle_folder_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + gboolean active; + + active = GTK_CHECK_MENU_ITEM(widget)->active; + + switch (mainwin->type) { + case SEPARATE_NONE: + case SEPARATE_MESSAGE: +#if 0 + if (active) + gtk_widget_show(GTK_WIDGET_PTR(mainwin->folderview)); + else + gtk_widget_hide(GTK_WIDGET_PTR(mainwin->folderview)); +#endif + break; + case SEPARATE_FOLDER: + if (active) + gtk_widget_show(mainwin->win.sep_folder.folderwin); + else + gtk_widget_hide(mainwin->win.sep_folder.folderwin); + break; + case SEPARATE_BOTH: + if (active) + gtk_widget_show(mainwin->win.sep_both.folderwin); + else + gtk_widget_hide(mainwin->win.sep_both.folderwin); + break; + } + + prefs_common.folderview_visible = active; +} + +static void toggle_message_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + gboolean active; + + active = GTK_CHECK_MENU_ITEM(widget)->active; + + if (active != messageview_is_visible(mainwin->messageview)) + summary_toggle_view(mainwin->summaryview); +} + +static void toggle_toolbar_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + mainwin->toolbar_style = (ToolbarStyle)action; + prefs_common.toolbar_style = (ToolbarStyle)action; + + main_window_set_toolbar_button_visibility(mainwin); +} + +static void toggle_searchbar_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + if (GTK_CHECK_MENU_ITEM(widget)->active) { + gtk_widget_show(mainwin->summaryview->qsearch->hbox); + prefs_common.show_searchbar = TRUE; + } else { + gtk_widget_hide(mainwin->summaryview->qsearch->hbox); + summary_qsearch_reset(mainwin->summaryview); + prefs_common.show_searchbar = FALSE; + } +} + +static void toggle_statusbar_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + if (GTK_CHECK_MENU_ITEM(widget)->active) { + gtk_widget_show(mainwin->statusbar); + prefs_common.show_statusbar = TRUE; + } else { + gtk_widget_hide(mainwin->statusbar); + prefs_common.show_statusbar = FALSE; + } +} + +static void toolbar_customize_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + toolbar_customize(widget, mainwin); +} + +static void change_layout_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + LayoutType type = action; + + if (GTK_CHECK_MENU_ITEM(widget)->active) + main_window_change_layout(mainwin, type, mainwin->type); +} + +static void separate_widget_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + SeparateType type; + + if (GTK_CHECK_MENU_ITEM(widget)->active) + type = mainwin->type | action; + else + type = mainwin->type & ~action; + + main_window_change_layout(mainwin, prefs_common.layout_type, type); + + prefs_common.sep_folder = (type & SEPARATE_FOLDER) != 0; + prefs_common.sep_msg = (type & SEPARATE_MESSAGE) != 0; +} + +static void addressbook_open_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + addressbook_open(NULL); +} + +static void log_window_show_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + log_window_show(mainwin->logwin); +} + +static void inc_mail_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + inc_mail(mainwin); +} + +static void inc_all_account_mail_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + inc_all_account_mail(mainwin, FALSE); +} + +static void inc_stop_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + inc_cancel_all(); +} + +static void rpop3_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + rpop3_account(cur_account); +} + +static void send_queue_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + main_window_send_queue(mainwin); +} + +static void compose_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + PrefsAccount *ac = NULL; + FolderItem *item = mainwin->summaryview->folder_item; + + if (item) { + ac = account_find_from_item(item); + if (ac && ac->protocol == A_NNTP && + FOLDER_TYPE(item->folder) == F_NEWS) { + compose_new(ac, item, item->path, NULL); + return; + } + } + + compose_new(ac, item, NULL, NULL); +} + +static void reply_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + summary_reply(mainwin->summaryview, (ComposeMode)action); +} + +static void move_to_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + summary_move_to(mainwin->summaryview); +} + +static void copy_to_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + summary_copy_to(mainwin->summaryview); +} + +static void delete_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + summary_delete(mainwin->summaryview); +} + +static void open_msg_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + summary_open_msg(mainwin->summaryview); +} + +static void view_source_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_view_source(mainwin->summaryview); +} + +static void show_all_header_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + if (mainwin->menu_lock_count) return; + summary_display_msg_selected(mainwin->summaryview, FALSE, + GTK_CHECK_MENU_ITEM(widget)->active); +} + +static void mark_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + summary_mark(mainwin->summaryview); +} + +static void unmark_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + summary_unmark(mainwin->summaryview); +} + +static void mark_as_unread_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_mark_as_unread(mainwin->summaryview); +} + +static void mark_as_read_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_mark_as_read(mainwin->summaryview); +} + +static void mark_thread_as_read_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_mark_thread_as_read(mainwin->summaryview); +} + +static void mark_all_read_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_mark_all_read(mainwin->summaryview); +} + +static void junk_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + if (action == 0) + summary_junk(mainwin->summaryview); + else + summary_not_junk(mainwin->summaryview); +} + +static void reedit_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + summary_reedit(mainwin->summaryview); +} + +static void add_address_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_add_address(mainwin->summaryview); +} + +static void set_charset_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + const gchar *str; + + if (GTK_CHECK_MENU_ITEM(widget)->active) { + str = conv_get_charset_str((CharSet)action); + g_free(prefs_common.force_charset); + prefs_common.force_charset = str ? g_strdup(str) : NULL; + + summary_redisplay_msg(mainwin->summaryview); + + debug_print("forced charset: %s\n", + str ? str : "Auto-Detect"); + } +} + +static void thread_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + if (mainwin->menu_lock_count) return; + if (!mainwin->summaryview->folder_item) return; + + if (GTK_CHECK_MENU_ITEM(widget)->active) + summary_thread_build(mainwin->summaryview); + else + summary_unthread(mainwin->summaryview); +} + +static void expand_threads_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_expand_threads(mainwin->summaryview); +} + +static void collapse_threads_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_collapse_threads(mainwin->summaryview); +} + +static void set_display_item_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + prefs_summary_column_open + (FOLDER_ITEM_IS_SENT_FOLDER(mainwin->summaryview->folder_item)); +} + +static void sort_summary_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + FolderItem *item = mainwin->summaryview->folder_item; + GtkWidget *menuitem; + + if (mainwin->menu_lock_count) return; + + if (GTK_CHECK_MENU_ITEM(widget)->active && item) { + menuitem = gtk_item_factory_get_item + (mainwin->menu_factory, "/View/Sort/Ascending"); + summary_sort(mainwin->summaryview, (FolderSortKey)action, + GTK_CHECK_MENU_ITEM(menuitem)->active + ? SORT_ASCENDING : SORT_DESCENDING); + } +} + +static void sort_summary_type_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + FolderItem *item = mainwin->summaryview->folder_item; + + if (mainwin->menu_lock_count) return; + + if (GTK_CHECK_MENU_ITEM(widget)->active && item) + summary_sort(mainwin->summaryview, + item->sort_key, (FolderSortType)action); +} + +static void attract_by_subject_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_attract_by_subject(mainwin->summaryview); +} + +static void delete_duplicated_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_delete_duplicated(mainwin->summaryview); +} + +static void concat_partial_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + GSList *mlist; + gchar *file; + FolderItem *item; + + if (summary_is_locked(mainwin->summaryview)) + return; + + item = mainwin->summaryview->folder_item; + if (!item) + return; + mlist = summary_get_selected_msg_list(mainwin->summaryview); + if (!mlist) + return; + + file = get_tmp_file(); + if (procmsg_concat_partial_messages(mlist, file) == 0) { + folder_item_add_msg(item, file, NULL, TRUE); + summary_show_queued_msgs(mainwin->summaryview); + } else { + alertpanel_error + (_("The selected messages could not be combined.")); + } + g_free(file); + + g_slist_free(mlist); +} + +static void filter_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + summary_filter(mainwin->summaryview, (gboolean)action); +} + +static void filter_junk_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + summary_filter_junk(mainwin->summaryview, (gboolean)action); +} + +static void execute_summary_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_execute(mainwin->summaryview); +} + +static void update_summary_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + if (!mainwin->summaryview->folder_item) return; + + summary_show(mainwin->summaryview, mainwin->summaryview->folder_item, + TRUE); +} + +static void open_config_folder_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + execute_open_file(get_rc_dir(), NULL); +} + +static void open_attachments_folder_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + execute_open_file(get_mime_tmp_dir(), NULL); +} + +static void prev_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + MessageView *messageview = mainwin->messageview; + + if (messageview_get_selected_mime_part(messageview) && + GTK_WIDGET_HAS_FOCUS(messageview->mimeview->treeview) && + mimeview_step(messageview->mimeview, GTK_SCROLL_STEP_BACKWARD)) + return; + + if (summary_step(mainwin->summaryview, GTK_SCROLL_STEP_BACKWARD)) + summary_mark_displayed_read(mainwin->summaryview, NULL); +} + +static void next_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + MessageView *messageview = mainwin->messageview; + + if (messageview_get_selected_mime_part(messageview) && + GTK_WIDGET_HAS_FOCUS(messageview->mimeview->treeview) && + mimeview_step(messageview->mimeview, GTK_SCROLL_STEP_FORWARD)) + return; + + if (summary_step(mainwin->summaryview, GTK_SCROLL_STEP_FORWARD)) + summary_mark_displayed_read(mainwin->summaryview, NULL); +} + +static void prev_unread_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_select_prev_unread(mainwin->summaryview); +} + +static void next_unread_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_select_next_unread(mainwin->summaryview); +} + +static void prev_new_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + summary_select_prev_new(mainwin->summaryview); +} + +static void next_new_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + summary_select_next_new(mainwin->summaryview); +} + +static void prev_marked_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_select_prev_marked(mainwin->summaryview); +} + +static void next_marked_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_select_next_marked(mainwin->summaryview); +} + +static void prev_labeled_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_select_prev_labeled(mainwin->summaryview); +} + +static void next_labeled_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_select_next_labeled(mainwin->summaryview); +} + +static void goto_folder_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + FolderItem *to_folder; + + to_folder = foldersel_folder_sel_full(NULL, FOLDER_SEL_ALL, NULL, + _("Select folder to open")); + + if (to_folder) + folderview_select(mainwin->folderview, to_folder); +} + +static void copy_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + messageview_copy_clipboard(mainwin->messageview); +} + +static void allsel_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + MessageView *msgview = mainwin->messageview; + + if (GTK_WIDGET_HAS_FOCUS(mainwin->summaryview->treeview)) + summary_select_all(mainwin->summaryview); + else if (GTK_WIDGET_HAS_FOCUS(mainwin->summaryview->qsearch->entry)) + gtk_editable_select_region + (GTK_EDITABLE(mainwin->summaryview->qsearch->entry), + 0, -1); + else if (messageview_is_visible(msgview) && + (GTK_WIDGET_HAS_FOCUS(msgview->textview->text) || + GTK_WIDGET_HAS_FOCUS(msgview->mimeview->textview->text))) + messageview_select_all(msgview); +} + +static void select_thread_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_select_thread(mainwin->summaryview); +} + +static void create_filter_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + summary_filter_open(mainwin->summaryview, (FilterCreateType)action); +} + +static void prefs_common_open_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + prefs_common_open(); +} + +static void prefs_filter_open_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + prefs_filter_open(NULL, NULL, NULL); +} + +static void prefs_template_open_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + prefs_template_open(); +} + +static void plugin_manager_open_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + plugin_manager_open(); +} + +#ifndef G_OS_WIN32 +static void prefs_actions_open_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + prefs_actions_open(mainwin); +} +#endif + +static void prefs_account_open_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + if (!cur_account) { + new_account_cb(mainwin, 0, widget); + } else { + account_open(cur_account); + } +} + +static void new_account_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + PrefsAccount *ac; + + if (compose_get_compose_list()) { + alertpanel_notice(_("Some composing windows are open.\n" + "Please close all the composing windows before editing the accounts.")); + return; + } + + if ((ac = setup_account())) { + account_set_menu(); + main_window_reflect_prefs_all(); + account_set_missing_folder(); + folderview_set(mainwin->folderview); + if (ac->folder) + folder_write_list(); + } +} + +static void account_selector_menu_cb(GtkMenuItem *menuitem, gpointer data) +{ + cur_account = (PrefsAccount *)data; + main_window_change_cur_account(); +} + +static void account_receive_menu_cb(GtkMenuItem *menuitem, gpointer data) +{ + PrefsAccount *account = (PrefsAccount *)data; + + inc_account_mail(main_window_get(), account); +} + +static void manual_open_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + manual_open((ManualLang)action); +} + +static void faq_open_cb(MainWindow *mainwin, guint action, GtkWidget *widget) +{ + faq_open((ManualLang)action); +} + +static GtkWidget *help_cmdline_window; + +static void help_cmdline_ok(GtkWidget *button) +{ + gtk_widget_destroy(gtk_widget_get_toplevel(button)); +} + +static gboolean help_cmdline_key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) { + gtk_widget_destroy(widget); + return TRUE; + } + return FALSE; +} + +static gboolean help_cmdline_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + return FALSE; +} + +static void help_cmdline_destroyed(GtkWidget *widget, gpointer data) +{ + help_cmdline_window = NULL; +} + +static void help_command_line_show(void) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *vbox2; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *hbbox; + GtkWidget *ok_btn; + + if (help_cmdline_window) { + gtk_window_present(GTK_WINDOW(help_cmdline_window)); + return; + } + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), _("Command line options")); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE); + help_cmdline_window = window; + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add(GTK_CONTAINER(window), vbox); + + vbox2 = gtk_vbox_new(FALSE, 8); + gtk_container_set_border_width(GTK_CONTAINER(vbox2), 8); + gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 0); + + label = gtk_label_new(_("Usage: sylpheed [OPTION]...")); + gtk_box_pack_start(GTK_BOX(vbox2), label, FALSE, FALSE, 0); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + hbox = gtk_hbox_new(FALSE, 16); + gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0); + + label = gtk_label_new(_("--compose [address]\n" + "--attach file1 [file2]...\n" + "--receive\n" + "--receive-all\n" + "--send\n" + "--status [folder]...\n" + "--status-full [folder]...\n" + "--open folderid/msgnum\n" + "--open \n" + "--configdir dirname\n" + "--exit\n" + "--debug\n" + "--safe-mode\n" + "--help\n" + "--version")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + label = gtk_label_new(_("open composition window\n" + "open composition window with specified files attached\n" + "receive new messages\n" + "receive new messages of all accounts\n" + "send all queued messages\n" + "show the total number of messages\n" + "show the status of each folder\n" + "open message in new window\n" + "open an rfc822 message file in a new window\n" + "specify directory which stores configuration files\n" + "exit Sylpheed\n" + "debug mode\n" + "safe mode\n" + "display this help and exit\n" + "output version information and exit")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + +#ifdef G_OS_WIN32 + label = gtk_label_new(_("Windows-only option:")); + gtk_box_pack_start(GTK_BOX(vbox2), label, FALSE, FALSE, 0); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + hbox = gtk_hbox_new(FALSE, 32); + gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0); + + label = gtk_label_new(_("--ipcport portnum")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + label = gtk_label_new(_("specify port for IPC remote commands")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); +#endif + + gtkut_stock_button_set_create(&hbbox, &ok_btn, GTK_STOCK_OK, + NULL, NULL, NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_btn); + + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(help_cmdline_ok), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(help_cmdline_key_pressed), NULL); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(help_cmdline_deleted), NULL); + g_signal_connect(G_OBJECT(window), "destroy", + G_CALLBACK(help_cmdline_destroyed), NULL); + + gtk_widget_show_all(window); +} + +static void help_cmdline_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + help_command_line_show(); +} + +#if USE_UPDATE_CHECK +static void update_check_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + update_check(TRUE); +} + +#ifdef USE_UPDATE_CHECK_PLUGIN +static void update_check_plugin_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + update_check_plugin(TRUE); +} +#endif +#endif + +static void scan_tree_func(Folder *folder, FolderItem *item, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + gchar *str; + + if (item->path) + str = g_strdup_printf(_("Scanning folder %s%c%s ..."), + LOCAL_FOLDER(folder)->rootpath, + G_DIR_SEPARATOR, + item->path); + else + str = g_strdup_printf(_("Scanning folder %s ..."), + LOCAL_FOLDER(folder)->rootpath); + + STATUSBAR_PUSH(mainwin, str); + STATUSBAR_POP(mainwin); + g_free(str); +} diff --git a/src/mainwindow.h b/src/mainwindow.h new file mode 100644 index 0000000..bc0107d --- /dev/null +++ b/src/mainwindow.h @@ -0,0 +1,192 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2012 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __MAINWINDOW_H__ +#define __MAINWINDOW_H__ + +#include +#include +#include +#include + +typedef struct _MainWindow MainWindow; + +#include "enums.h" +#include "folderview.h" +#include "summaryview.h" +#include "headerview.h" +#include "messageview.h" +#include "logwindow.h" +#include "trayicon.h" +#include "gtkutils.h" + +typedef enum +{ + SEPARATE_NONE = 0, + SEPARATE_FOLDER = 1 << 0, + SEPARATE_MESSAGE = 1 << 1, + SEPARATE_BOTH = (SEPARATE_FOLDER | SEPARATE_MESSAGE) +} SeparateType; + +struct _MainWindow +{ + SeparateType type; + + union CompositeWin { + struct + { + GtkWidget *hpaned; + GtkWidget *vpaned; + } sep_none; + struct { + GtkWidget *folderwin; + GtkWidget *vpaned; + } sep_folder; + struct { + GtkWidget *messagewin; + GtkWidget *hpaned; + } sep_message; + struct { + GtkWidget *folderwin; + GtkWidget *messagewin; + } sep_both; + } win; + + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *menubar; + + GtkItemFactory *menu_factory; + + /* toolbar */ + GtkWidget *toolbar; + GtkWidget *get_btn; + GtkWidget *getall_btn; + GtkWidget *rpop3_btn; + GtkWidget *send_btn; + GtkWidget *compose_btn; + GtkWidget *reply_btn; + ComboButton *reply_combo; + GtkWidget *replyall_btn; + GtkWidget *fwd_btn; + ComboButton *fwd_combo; + GtkWidget *delete_btn; + GtkWidget *junk_btn; + GtkWidget *notjunk_btn; + GtkWidget *exec_btn; + GtkWidget *next_btn; + GtkWidget *prev_btn; + GtkWidget *address_btn; + GtkWidget *search_btn; + GtkWidget *print_btn; + GtkWidget *stop_btn; + GtkWidget *prefs_common_btn; + GtkWidget *prefs_account_btn; + + /* body */ + GtkWidget *vbox_body; + GtkWidget *statusbar; + GtkWidget *progressbar; + GtkWidget *statuslabel; + GtkWidget *online_switch; + GtkWidget *online_pixmap; + GtkWidget *offline_pixmap; + GtkTooltips *online_tip; + GtkWidget *ac_button; + GtkWidget *ac_label; + GtkWidget *ac_menu; + + TrayIcon *tray_icon; + + /* context IDs for status bar */ + gint mainwin_cid; + gint folderview_cid; + gint summaryview_cid; + gint messageview_cid; + + ToolbarStyle toolbar_style; + + guint lock_count; + guint menu_lock_count; + guint cursor_count; + + gboolean window_hidden; + gboolean window_obscured; + + FolderView *folderview; + SummaryView *summaryview; + MessageView *messageview; + LogWindow *logwin; + + GtkTooltips *toolbar_tip; +}; + +MainWindow *main_window_create (SeparateType type); + +void main_window_cursor_wait (MainWindow *mainwin); +void main_window_cursor_normal (MainWindow *mainwin); + +void main_window_lock (MainWindow *mainwin); +void main_window_unlock (MainWindow *mainwin); + +void main_window_reflect_prefs_all (void); +void main_window_set_summary_column (void); +void main_window_set_account_menu (GList *account_list); + +void main_window_change_cur_account (void); + +MainWindow *main_window_get (void); + +GtkWidget *main_window_get_folder_window (MainWindow *mainwin); +GtkWidget *main_window_get_message_window (MainWindow *mainwin); + +void main_window_hide (MainWindow *mainwin); +void main_window_change_layout (MainWindow *mainwin, + LayoutType layout, + SeparateType type); + +void main_window_toggle_message_view (MainWindow *mainwin); + +void main_window_get_size (MainWindow *mainwin); +void main_window_get_position (MainWindow *mainwin); + +void main_window_progress_on (MainWindow *mainwin); +void main_window_progress_off (MainWindow *mainwin); +void main_window_progress_set (MainWindow *mainwin, + gint cur, + gint total); + +void main_window_progress_show (gint cur, + gint total); + +void main_window_toggle_online (MainWindow *mainwin, + gboolean online); +gboolean main_window_toggle_online_if_offline (MainWindow *mainwin); + +void main_window_empty_trash (MainWindow *mainwin, + gboolean confirm); +void main_window_add_mailbox (MainWindow *mainwin); +void main_window_send_queue (MainWindow *mainwin); + +void main_window_set_toolbar_sensitive (MainWindow *mainwin); +void main_window_set_menu_sensitive (MainWindow *mainwin); + +void main_window_popup (MainWindow *mainwin); + +#endif /* __MAINWINDOW_H__ */ diff --git a/src/manage_window.c b/src/manage_window.c new file mode 100644 index 0000000..b21a3b2 --- /dev/null +++ b/src/manage_window.c @@ -0,0 +1,103 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2002 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "manage_window.h" +#include "utils.h" +#include "gtkutils.h" + +static GtkWidget *focus_window; + +gint manage_window_focus_in(GtkWidget *widget, GdkEventFocus *event, + gpointer data) +{ + /* debug_print("Focus in event: window: %p\n", widget); */ + + focus_window = widget; + + return FALSE; +} + +gint manage_window_focus_out(GtkWidget *widget, GdkEventFocus *event, + gpointer data) +{ + /* debug_print("Focused window: %p\n", focus_window); */ + /* debug_print("Focus out event: window: %p\n", widget); */ + +#if 0 + if (focus_window == widget) + focus_window = NULL; +#endif + + return FALSE; +} + +gint manage_window_unmap(GtkWidget *widget, GdkEventAny *event, gpointer data) +{ + /* debug_print("unmap event: %p\n", widget); */ + + if (focus_window == widget) + focus_window = NULL; + + return FALSE; +} + +gint manage_window_delete(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + /* debug_print("delete event: %p\n", widget); */ + + if (focus_window == widget) + focus_window = NULL; + + return FALSE; +} + +void manage_window_destroy(GtkWidget *widget, gpointer data) +{ + /* debug_print("destroy event: %p\n", widget); */ + + if (focus_window == widget) + focus_window = NULL; +} + +void manage_window_set_transient(GtkWindow *window) +{ + /* debug_print("manage_window_set_transient(): window = %p, focus_window = %p\n", + window, focus_window); */ + + if (window && focus_window) { + if (!gtk_window_is_active(GTK_WINDOW(focus_window))) + gtkut_window_popup(focus_window); + gtk_window_set_transient_for(window, GTK_WINDOW(focus_window)); + } +} + +void manage_window_signals_connect(GtkWindow *window) +{ + MANAGE_WINDOW_SIGNALS_CONNECT(window); +} + +GtkWidget *manage_window_get_focus_window(void) +{ + return focus_window; +} diff --git a/src/manage_window.h b/src/manage_window.h new file mode 100644 index 0000000..61369d1 --- /dev/null +++ b/src/manage_window.h @@ -0,0 +1,59 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2002 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __MANAGE_WINDOW_H__ +#define __MANAGE_WINDOW_H__ + +#include +#include +#include + +#define MANAGE_WINDOW_SIGNALS_CONNECT(window) \ +{ \ + g_signal_connect(G_OBJECT(window), "focus_in_event", \ + G_CALLBACK(manage_window_focus_in), NULL); \ + g_signal_connect(G_OBJECT(window), "focus_out_event", \ + G_CALLBACK(manage_window_focus_out), NULL); \ + g_signal_connect(G_OBJECT(window), "unmap_event", \ + G_CALLBACK(manage_window_unmap), NULL); \ + g_signal_connect(G_OBJECT(window), "destroy", \ + G_CALLBACK(manage_window_destroy), NULL); \ +} + +gint manage_window_focus_in (GtkWidget *widget, + GdkEventFocus *event, + gpointer data); +gint manage_window_focus_out (GtkWidget *widget, + GdkEventFocus *event, + gpointer data); +gint manage_window_unmap (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +gint manage_window_delete (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +void manage_window_destroy (GtkWidget *widget, + gpointer data); + +void manage_window_set_transient (GtkWindow *window); +void manage_window_signals_connect (GtkWindow *window); + +GtkWidget *manage_window_get_focus_window (void); + +#endif /* __MANAGE_WINDOW_H__ */ diff --git a/src/manual.c b/src/manual.c new file mode 100644 index 0000000..4a54790 --- /dev/null +++ b/src/manual.c @@ -0,0 +1,112 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include + +#include "prefs_common.h" +#include "manual.h" +#include "utils.h" + +static gchar *get_lang_str(ManualLang lang); + +static gchar *get_lang_str(ManualLang lang) +{ + switch (lang) { + case MANUAL_LANG_DE: + return "de"; + case MANUAL_LANG_EN: + return "en"; + case MANUAL_LANG_ES: + return "es"; + case MANUAL_LANG_FR: + return "fr"; + case MANUAL_LANG_IT: + return "it"; + case MANUAL_LANG_JA: + return "ja"; + default: + return NULL; + } +} + +void manual_open(ManualLang lang) +{ + gchar *lang_str; + gchar *file_uri; + + lang_str = get_lang_str(lang); + if (!lang_str) return; + + file_uri = g_strconcat("file://", +#ifdef G_OS_WIN32 + get_startup_dir(), + G_DIR_SEPARATOR_S "doc" G_DIR_SEPARATOR_S + "manual", +#elif defined(__APPLE__) + get_startup_dir(), + G_DIR_SEPARATOR_S "Contents" G_DIR_SEPARATOR_S + "Resources" G_DIR_SEPARATOR_S + "share" G_DIR_SEPARATOR_S + "sylpheed" G_DIR_SEPARATOR_S + "manual", +#else + MANUALDIR, +#endif + G_DIR_SEPARATOR_S, lang_str, G_DIR_SEPARATOR_S, + MANUAL_HTML_INDEX, NULL); + debug_print("Opening manual: %s\n", file_uri); + open_uri(file_uri, prefs_common.uri_cmd); + g_free(file_uri); +} + +void faq_open(ManualLang lang) +{ + gchar *lang_str; + gchar *file_uri; + + lang_str = get_lang_str(lang); + if (!lang_str) return; + + file_uri = g_strconcat("file://", +#ifdef G_OS_WIN32 + get_startup_dir(), + G_DIR_SEPARATOR_S "doc" G_DIR_SEPARATOR_S "faq", +#elif defined(__APPLE__) + get_startup_dir(), + G_DIR_SEPARATOR_S "Contents" G_DIR_SEPARATOR_S + "Resources" G_DIR_SEPARATOR_S + "share" G_DIR_SEPARATOR_S + "sylpheed" G_DIR_SEPARATOR_S + "faq", +#else + FAQDIR, +#endif + G_DIR_SEPARATOR_S, lang_str, G_DIR_SEPARATOR_S, + FAQ_HTML_INDEX, NULL); + debug_print("Opening FAQ: %s\n", file_uri); + open_uri(file_uri, prefs_common.uri_cmd); + g_free(file_uri); +} diff --git a/src/manual.h b/src/manual.h new file mode 100644 index 0000000..6ae384f --- /dev/null +++ b/src/manual.h @@ -0,0 +1,36 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2002 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __MANUAL_H__ +#define __MANUAL_H__ + +typedef enum +{ + MANUAL_LANG_DE, + MANUAL_LANG_EN, + MANUAL_LANG_ES, + MANUAL_LANG_FR, + MANUAL_LANG_IT, + MANUAL_LANG_JA, +} ManualLang; + +void manual_open(ManualLang lang); +void faq_open (ManualLang lang); + +#endif /* __MANUAL_H__ */ diff --git a/src/menu.c b/src/menu.c new file mode 100644 index 0000000..2330b57 --- /dev/null +++ b/src/menu.c @@ -0,0 +1,300 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2012 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "menu.h" +#include "utils.h" + +static gchar *menu_translate(const gchar *path, gpointer data); + +GtkWidget *menubar_create(GtkWidget *window, GtkItemFactoryEntry *entries, + guint n_entries, const gchar *path, gpointer data) +{ + GtkItemFactory *factory; + + factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, path, NULL); + gtk_item_factory_set_translate_func(factory, menu_translate, + NULL, NULL); + gtk_item_factory_create_items(factory, n_entries, entries, data); + gtk_window_add_accel_group(GTK_WINDOW(window), factory->accel_group); + + return gtk_item_factory_get_widget(factory, path); +} + +GtkWidget *menu_create_items(GtkItemFactoryEntry *entries, + guint n_entries, const gchar *path, + GtkItemFactory **factory, gpointer data) +{ + *factory = gtk_item_factory_new(GTK_TYPE_MENU, path, NULL); + gtk_item_factory_set_translate_func(*factory, menu_translate, + NULL, NULL); + gtk_item_factory_create_items(*factory, n_entries, entries, data); + + return gtk_item_factory_get_widget(*factory, path); +} + +static gchar *menu_translate(const gchar *path, gpointer data) +{ + gchar *retval; + + retval = gettext(path); + + return retval; +} + +#if 0 +static void factory_print_func(gpointer data, gchar *string) +{ + GString *out_str = data; + + g_string_append(out_str, string); + g_string_append_c(out_str, '\n'); +} + +GString *menu_factory_get_rc(const gchar *path) +{ + GString *string; + GtkPatternSpec *pspec; + gchar pattern[256]; + + pspec = g_new(GtkPatternSpec, 1); + g_snprintf(pattern, sizeof(pattern), "%s*", path); + gtk_pattern_spec_init(pspec, pattern); + string = g_string_new(""); + gtk_item_factory_dump_items(pspec, FALSE, factory_print_func, + string); + gtk_pattern_spec_free_segs(pspec); + + return string; +} + +void menu_factory_clear_rc(const gchar *rc_str) +{ + GString *string; + gchar *p; + gchar *start, *end; + guint pos = 0; + + string = g_string_new(rc_str); + while ((p = strstr(string->str + pos, "(menu-path \"")) != NULL) { + pos = p + 12 - string->str; + p = strchr(p + 12, '"'); + if (!p) continue; + start = strchr(p + 1, '"'); + if (!start) continue; + end = strchr(start + 1, '"'); + if (!end) continue; + pos = start + 1 - string->str; + if (end > start + 1) + g_string_erase(string, pos, end - (start + 1)); + } + + gtk_item_factory_parse_rc_string(string->str); + g_string_free(string, TRUE); +} + +void menu_factory_copy_rc(const gchar *src_path, const gchar *dest_path) +{ + GString *string; + gint src_path_len; + gint dest_path_len; + gchar *p; + guint pos = 0; + + string = menu_factory_get_rc(src_path); + src_path_len = strlen(src_path); + dest_path_len = strlen(dest_path); + + while ((p = strstr(string->str + pos, src_path)) != NULL) { + pos = p - string->str; + g_string_erase(string, pos, src_path_len); + g_string_insert(string, pos, dest_path); + pos += dest_path_len; + } + + pos = 0; + while ((p = strchr(string->str + pos, ';')) != NULL) { + pos = p - string->str; + if (pos == 0 || *(p - 1) == '\n') + g_string_erase(string, pos, 1); + } + + menu_factory_clear_rc(string->str); + gtk_item_factory_parse_rc_string(string->str); + g_string_free(string, TRUE); +} +#endif + +void menu_set_sensitive(GtkItemFactory *ifactory, const gchar *path, + gboolean sensitive) +{ + GtkWidget *widget; + + g_return_if_fail(ifactory != NULL); + + widget = gtk_item_factory_get_item(ifactory, path); + gtk_widget_set_sensitive(widget, sensitive); +} + +void menu_set_sensitive_all(GtkMenuShell *menu_shell, gboolean sensitive) +{ + GList *cur; + + for (cur = menu_shell->children; cur != NULL; cur = cur->next) + gtk_widget_set_sensitive(GTK_WIDGET(cur->data), sensitive); +} + +void menu_set_active(GtkItemFactory *ifactory, const gchar *path, + gboolean is_active) +{ + GtkWidget *widget; + + g_return_if_fail(ifactory != NULL); + + widget = gtk_item_factory_get_item(ifactory, path); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), is_active); +} + +void menu_button_position(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, + gpointer user_data) +{ + GtkWidget *button; + GtkRequisition requisition; + gint button_xpos, button_ypos; + gint xpos, ypos; + gint width, height; + gint scr_width, scr_height; + + g_return_if_fail(x != NULL && y != NULL && push_in != NULL); + + button = GTK_WIDGET(user_data); + + gtk_widget_get_child_requisition(GTK_WIDGET(menu), &requisition); + width = requisition.width; + height = requisition.height; + gdk_window_get_origin(button->window, &button_xpos, &button_ypos); + + xpos = button_xpos + button->allocation.x; + ypos = button_ypos + button->allocation.y + button->allocation.height; + + scr_width = gdk_screen_width(); + scr_height = gdk_screen_height(); + + if (xpos + width > scr_width) + xpos -= (xpos + width) - scr_width; + if (ypos + height > scr_height) + ypos -= button->allocation.height + height; + if (xpos < 0) + xpos = 0; + if (ypos < 0) + ypos = 0; + + *x = xpos; + *y = ypos; + *push_in = FALSE; +} + +void menu_widget_position(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, + gpointer user_data) +{ + GtkWidget *widget; + GtkRequisition requisition; + gint w_xpos, w_ypos; + gint xpos, ypos; + gint width, height; + gint scr_width, scr_height; + + g_return_if_fail(x != NULL && y != NULL && push_in != NULL); + + widget = GTK_WIDGET(user_data); + + gtk_widget_get_child_requisition(GTK_WIDGET(menu), &requisition); + width = requisition.width; + height = requisition.height; + gdk_window_get_origin(widget->window, &w_xpos, &w_ypos); + + xpos = w_xpos; + ypos = w_ypos; + + scr_width = gdk_screen_width(); + scr_height = gdk_screen_height(); + + if (xpos + width > scr_width) + xpos -= (xpos + width) - scr_width; + if (ypos + height > scr_height) + ypos -= height; + if (xpos < 0) + xpos = 0; + if (ypos < 0) + ypos = 0; + + *x = xpos; + *y = ypos; + *push_in = FALSE; +} + +gint menu_find_option_menu_index(GtkOptionMenu *optmenu, gpointer data, + GCompareFunc func) +{ + GtkWidget *menu; + GtkWidget *menuitem; + gpointer menu_data; + GList *cur; + gint n; + + menu = gtk_option_menu_get_menu(optmenu); + + for (cur = GTK_MENU_SHELL(menu)->children, n = 0; + cur != NULL; cur = cur->next, n++) { + menuitem = GTK_WIDGET(cur->data); + menu_data = g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID); + if (func) { + if (func(menu_data, data) == 0) + return n; + } else if (menu_data == data) + return n; + } + + return -1; +} + +gint menu_get_option_menu_active_index(GtkOptionMenu *optmenu) +{ + GtkWidget *menu; + GtkWidget *menuitem; + + menu = gtk_option_menu_get_menu(optmenu); + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + + return GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID)); +} diff --git a/src/menu.h b/src/menu.h new file mode 100644 index 0000000..004131e --- /dev/null +++ b/src/menu.h @@ -0,0 +1,150 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2012 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __MENU_H__ +#define __MENU_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MENU_VAL_ID "Sylpheed::Menu::ValueID" + +#define MENUITEM_ADD(menu, menuitem, label, data) \ +{ \ + if (label) \ + menuitem = gtk_menu_item_new_with_label(label); \ + else { \ + menuitem = gtk_menu_item_new(); \ + gtk_widget_set_sensitive(menuitem, FALSE); \ + } \ + gtk_widget_show(menuitem); \ + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); \ + if (data) \ + g_object_set_data(G_OBJECT(menuitem), \ + MENU_VAL_ID, \ + GINT_TO_POINTER(data)); \ +} + +#define MENUITEM_ADD_WITH_MNEMONIC(menu, menuitem, label, data) \ +{ \ + if (label) \ + menuitem = gtk_menu_item_new_with_mnemonic(label); \ + else { \ + menuitem = gtk_menu_item_new(); \ + gtk_widget_set_sensitive(menuitem, FALSE); \ + } \ + gtk_widget_show(menuitem); \ + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); \ + if (data) \ + g_object_set_data(G_OBJECT(menuitem), \ + MENU_VAL_ID, \ + GINT_TO_POINTER(data)); \ +} + +#define MENUITEM_ADD_FROM_STOCK(menu, menuitem, label, data) \ +{ \ + if (label) \ + menuitem = gtk_image_menu_item_new_from_stock(label, NULL); \ + else { \ + menuitem = gtk_menu_item_new(); \ + gtk_widget_set_sensitive(menuitem, FALSE); \ + } \ + gtk_widget_show(menuitem); \ + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); \ + if (data) \ + g_object_set_data(G_OBJECT(menuitem), \ + MENU_VAL_ID, \ + GINT_TO_POINTER(data)); \ +} + +#define MENUITEM_ADD_RADIO(menu, menuitem, widget, label, data) \ +{ \ + if (label) { \ + if (widget) \ + menuitem = gtk_radio_menu_item_new_with_mnemonic_from_widget \ + (GTK_RADIO_MENU_ITEM(widget), label); \ + else \ + menuitem = gtk_radio_menu_item_new_with_mnemonic \ + (NULL, label); \ + } else { \ + menuitem = gtk_menu_item_new(); \ + gtk_widget_set_sensitive(menuitem, FALSE); \ + } \ + gtk_widget_show(menuitem); \ + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); \ + if (data) \ + g_object_set_data(G_OBJECT(menuitem), \ + MENU_VAL_ID, \ + GINT_TO_POINTER(data)); \ +} + +#define menu_set_insensitive_all(menu_shell) \ + menu_set_sensitive_all(menu_shell, FALSE); + +GtkWidget *menubar_create (GtkWidget *window, + GtkItemFactoryEntry *entries, + guint n_entries, + const gchar *path, + gpointer data); +GtkWidget *menu_create_items (GtkItemFactoryEntry *entries, + guint n_entries, + const gchar *path, + GtkItemFactory **factory, + gpointer data); + +GString *menu_factory_get_rc (const gchar *path); +void menu_factory_clear_rc (const gchar *rc_str); +void menu_factory_copy_rc (const gchar *src_path, + const gchar *dest_path); + +void menu_set_sensitive (GtkItemFactory *ifactory, + const gchar *path, + gboolean sensitive); +void menu_set_sensitive_all (GtkMenuShell *menu_shell, + gboolean sensitive); + +void menu_set_active (GtkItemFactory *ifactory, + const gchar *path, + gboolean is_active); + +void menu_button_position (GtkMenu *menu, + gint *x, + gint *y, + gboolean *push_in, + gpointer user_data); +void menu_widget_position (GtkMenu *menu, + gint *x, + gint *y, + gboolean *push_in, + gpointer user_data); + +gint menu_find_option_menu_index(GtkOptionMenu *optmenu, + gpointer data, + GCompareFunc func); + +gint menu_get_option_menu_active_index + (GtkOptionMenu *optmenu); + +#endif /* __MENU_H__ */ diff --git a/src/message_search.c b/src/message_search.c new file mode 100644 index 0000000..29d4737 --- /dev/null +++ b/src/message_search.c @@ -0,0 +1,283 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "message_search.h" +#include "messageview.h" +#include "utils.h" +#include "gtkutils.h" +#include "manage_window.h" +#include "alertpanel.h" + +static struct MessageSearchWindow { + GtkWidget *window; + GtkWidget *body_entry; + GtkWidget *case_checkbtn; + GtkWidget *next_btn; + GtkWidget *prev_btn; + GtkWidget *close_btn; + + MessageView *messageview; +} search_window; + +static void message_search_create (void); +static void message_search_execute (gboolean backward); +static void message_search_close (void); + +static void message_search_prev_clicked (GtkButton *button, + gpointer data); +static void message_search_next_clicked (GtkButton *button, + gpointer data); +static void close_clicked (GtkButton *button, + gpointer data); +static void body_activated (void); +static gboolean window_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); + +static void view_destroyed (GtkWidget *widget, + gpointer data); + +void message_search(MessageView *messageview) +{ + if (!search_window.window) + message_search_create(); + + search_window.messageview = messageview; + g_signal_handlers_disconnect_by_func(GTK_WIDGET_PTR(messageview), + view_destroyed, messageview); + g_signal_connect(G_OBJECT(GTK_WIDGET_PTR(messageview)), "destroy", + G_CALLBACK(view_destroyed), messageview); + + gtk_widget_grab_focus(search_window.next_btn); + gtk_widget_grab_focus(search_window.body_entry); + gtk_widget_show(search_window.window); + gtk_window_present(GTK_WINDOW(search_window.window)); +} + +static void message_search_create(void) +{ + GtkWidget *window; + + GtkWidget *vbox1; + GtkWidget *hbox1; + GtkWidget *body_label; + GtkWidget *body_entry; + + GtkWidget *checkbtn_hbox; + GtkWidget *case_checkbtn; + + GtkWidget *confirm_area; + GtkWidget *next_btn; + GtkWidget *prev_btn; + GtkWidget *close_btn; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (window), + _("Find in current message")); + gtk_widget_set_size_request (window, 450 * gtkut_get_dpi_multiplier(), -1); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, TRUE); + gtk_container_set_border_width (GTK_CONTAINER (window), 8); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(window_deleted), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + + vbox1 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (window), vbox1); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox1), hbox1, TRUE, TRUE, 0); + + body_label = gtk_label_new (_("Find text:")); + gtk_widget_show (body_label); + gtk_box_pack_start (GTK_BOX (hbox1), body_label, FALSE, FALSE, 0); + + body_entry = gtk_entry_new (); + gtk_widget_show (body_entry); + gtk_box_pack_start (GTK_BOX (hbox1), body_entry, TRUE, TRUE, 0); + g_signal_connect(G_OBJECT(body_entry), "activate", + G_CALLBACK(body_activated), NULL); + + checkbtn_hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (checkbtn_hbox); + gtk_box_pack_start (GTK_BOX (vbox1), checkbtn_hbox, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (checkbtn_hbox), 8); + + case_checkbtn = gtk_check_button_new_with_label (_("Case sensitive")); + gtk_widget_show (case_checkbtn); + gtk_box_pack_start (GTK_BOX (checkbtn_hbox), case_checkbtn, + FALSE, FALSE, 0); + + gtkut_stock_button_set_create(&confirm_area, + &next_btn, GTK_STOCK_GO_FORWARD, + &prev_btn, GTK_STOCK_GO_BACK, + &close_btn, GTK_STOCK_CLOSE); + gtkut_box_set_reverse_order(GTK_BOX(confirm_area), FALSE); + gtk_widget_show (confirm_area); + gtk_box_pack_start (GTK_BOX (vbox1), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default(next_btn); + + g_signal_connect(G_OBJECT(prev_btn), "clicked", + G_CALLBACK(message_search_prev_clicked), NULL); + g_signal_connect(G_OBJECT(next_btn), "clicked", + G_CALLBACK(message_search_next_clicked), NULL); + g_signal_connect(G_OBJECT(close_btn), "clicked", + G_CALLBACK(close_clicked), NULL); + + search_window.window = window; + search_window.body_entry = body_entry; + search_window.case_checkbtn = case_checkbtn; + search_window.next_btn = next_btn; + search_window.prev_btn = prev_btn; + search_window.close_btn = close_btn; +} + +static void message_search_execute(gboolean backward) +{ + MessageView *messageview = search_window.messageview; + gboolean case_sens; + gboolean all_searched = FALSE; + const gchar *body_str; + + body_str = gtk_entry_get_text(GTK_ENTRY(search_window.body_entry)); + if (*body_str == '\0') return; + + case_sens = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(search_window.case_checkbtn)); + + for (;;) { + gchar *str; + AlertValue val; + + if (backward) { + if (messageview_search_string_backward + (messageview, body_str, case_sens) == TRUE) + break; + } else { + if (messageview_search_string + (messageview, body_str, case_sens) == TRUE) + break; + } + + if (all_searched) { + alertpanel_message + (_("Search failed"), + _("Search string not found."), + ALERT_WARNING); + break; + } + + all_searched = TRUE; + + if (backward) + str = _("Beginning of message reached; " + "continue from end?"); + else + str = _("End of message reached; " + "continue from beginning?"); + + val = alertpanel(_("Search finished"), str, + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if (G_ALERTDEFAULT == val) { + manage_window_focus_in(search_window.window, + NULL, NULL); + messageview_set_position(messageview, + backward ? -1 : 0); + } else + break; + } +} + +static void message_search_close(void) +{ + search_window.messageview = NULL; + gtk_widget_hide(search_window.window); +} + +static void message_search_prev_clicked(GtkButton *button, gpointer data) +{ + message_search_execute(TRUE); +} + +static void message_search_next_clicked(GtkButton *button, gpointer data) +{ + message_search_execute(FALSE); +} + +static void close_clicked(GtkButton *button, gpointer data) +{ + message_search_close(); +} + +static void body_activated(void) +{ + gtk_button_clicked(GTK_BUTTON(search_window.next_btn)); +} + +static gboolean window_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + message_search_close(); + return TRUE; +} + +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + message_search_close(); + return FALSE; +} + +static void view_destroyed(GtkWidget *widget, gpointer data) +{ + if ((MessageView *)data == search_window.messageview) + message_search_close(); +} diff --git a/src/message_search.h b/src/message_search.h new file mode 100644 index 0000000..bbf28a5 --- /dev/null +++ b/src/message_search.h @@ -0,0 +1,29 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __MESSAGE_SEARCH_H__ +#define __MESSAGE_SEARCH_H__ + +#include + +#include "messageview.h" + +void message_search (MessageView *messageview); + +#endif /* __MESSAGE_SEARCH_H__ */ diff --git a/src/messageview.c b/src/messageview.c new file mode 100644 index 0000000..782bc80 --- /dev/null +++ b/src/messageview.c @@ -0,0 +1,1262 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2012 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "messageview.h" +#include "message_search.h" +#include "headerview.h" +#include "textview.h" +#include "imageview.h" +#include "mimeview.h" +#include "menu.h" +#include "about.h" +#include "filesel.h" +#include "sourcewindow.h" +#include "addressbook.h" +#include "alertpanel.h" +#include "inputdialog.h" +#include "manage_window.h" +#include "printing.h" +#include "procmsg.h" +#include "procheader.h" +#include "procmime.h" +#include "account.h" +#include "action.h" +#include "prefs_common.h" +#include "prefs_account.h" +#include "prefs_filter.h" +#include "filter.h" +#include "gtkutils.h" +#include "utils.h" +#include "rfc2015.h" +#include "plugin.h" + +static GList *messageview_list = NULL; + +static void messageview_set_mime_view_layout + (MessageView *messageview); +static void messageview_change_view_type(MessageView *messageview, + MessageType type); +static void messageview_set_tool_menu (MessageView *messageview, + MimeInfo *mimeinfo); +static void messageview_set_menu_state (MessageView *messageview); + +static void messageview_set_encoding_menu + (MessageView *messageview); + +static gint messageview_delete_cb (GtkWidget *widget, + GdkEventAny *event, + MessageView *messageview); +static void messageview_size_allocate_cb(GtkWidget *widget, + GtkAllocation *allocation); +static void messageview_toggle_attach_cb(GtkToggleButton *toggle, + MessageView *messageview); +static void messageview_switch_page_cb (GtkNotebook *notebook, + GtkNotebookPage *page, + guint page_num, + MessageView *messageview); +static gint messageview_menu_tool_btn_pressed + (GtkWidget *widget, + GdkEventButton *event, + MessageView *messageview); +static void messageview_open_file_cb (GtkWidget *widget, + MessageView *messageview); +static void messageview_save_all_cb (GtkWidget *widget, + MessageView *messageview); +static gboolean key_pressed (GtkWidget *widget, + GdkEventKey *event, + MessageView *messageview); + +static void save_as_cb (gpointer data, + guint action, + GtkWidget *widget); +#if GTK_CHECK_VERSION(2, 10, 0) +static void page_setup_cb (gpointer data, + guint action, + GtkWidget *widget); +#endif +static void print_cb (gpointer data, + guint action, + GtkWidget *widget); +static void close_cb (gpointer data, + guint action, + GtkWidget *widget); +static void copy_cb (gpointer data, + guint action, + GtkWidget *widget); +static void allsel_cb (gpointer data, + guint action, + GtkWidget *widget); +static void search_cb (gpointer data, + guint action, + GtkWidget *widget); + +static void set_charset_cb (gpointer data, + guint action, + GtkWidget *widget); +static void view_source_cb (gpointer data, + guint action, + GtkWidget *widget); +static void show_all_header_cb (gpointer data, + guint action, + GtkWidget *widget); + +static void compose_cb (gpointer data, + guint action, + GtkWidget *widget); +static void reply_cb (gpointer data, + guint action, + GtkWidget *widget); +static void reedit_cb (gpointer data, + guint action, + GtkWidget *widget); + +static void addressbook_open_cb (gpointer data, + guint action, + GtkWidget *widget); +static void add_address_cb (gpointer data, + guint action, + GtkWidget *widget); +static void create_filter_cb (gpointer data, + guint action, + GtkWidget *widget); + +static void about_cb (gpointer data, + guint action, + GtkWidget *widget); + +static GtkItemFactoryEntry msgview_entries[] = +{ + {N_("/_File"), NULL, NULL, 0, ""}, + {N_("/_File/_Save as..."), NULL, save_as_cb, 0, NULL}, + {N_("/_File/---"), NULL, NULL, 0, ""}, +#if GTK_CHECK_VERSION(2, 10, 0) + {N_("/_File/Page set_up..."), NULL, page_setup_cb, 0, NULL}, +#endif + {N_("/_File/_Print..."), NULL, print_cb, 0, NULL}, + {N_("/_File/---"), NULL, NULL, 0, ""}, + {N_("/_File/_Close"), NULL, close_cb, 0, NULL}, + + {N_("/_Edit"), NULL, NULL, 0, ""}, + {N_("/_Edit/_Copy"), NULL, copy_cb, 0, NULL}, + {N_("/_Edit/Select _all"), NULL, allsel_cb, 0, NULL}, + {N_("/_Edit/---"), NULL, NULL, 0, ""}, + {N_("/_Edit/_Find in current message..."), + NULL, search_cb, 0, NULL}, + + {N_("/_View"), NULL, NULL, 0, ""}, + +#define ENC_SEPARATOR \ + {N_("/_View/Character _encoding/---"), NULL, NULL, 0, ""} +#define ENC_ACTION(action) \ + NULL, set_charset_cb, action, "/View/Character encoding/Auto detect" + + {N_("/_View/Character _encoding"), NULL, NULL, 0, ""}, + {N_("/_View/Character _encoding/_Auto detect"), + NULL, set_charset_cb, C_AUTO, ""}, + ENC_SEPARATOR, + {N_("/_View/Character _encoding/7bit ascii (US-ASC_II)"), + ENC_ACTION(C_US_ASCII)}, + + {N_("/_View/Character _encoding/Unicode (_UTF-8)"), + ENC_ACTION(C_UTF_8)}, + ENC_SEPARATOR, + {N_("/_View/Character _encoding/Western European (ISO-8859-_1)"), + ENC_ACTION(C_ISO_8859_1)}, + {N_("/_View/Character _encoding/Western European (ISO-8859-15)"), + ENC_ACTION(C_ISO_8859_15)}, + {N_("/_View/Character _encoding/Western European (Windows-1252)"), + ENC_ACTION(C_WINDOWS_1252)}, + ENC_SEPARATOR, + {N_("/_View/Character _encoding/Central European (ISO-8859-_2)"), + ENC_ACTION(C_ISO_8859_2)}, + ENC_SEPARATOR, + {N_("/_View/Character _encoding/_Baltic (ISO-8859-13)"), + ENC_ACTION(C_ISO_8859_13)}, + {N_("/_View/Character _encoding/Baltic (ISO-8859-_4)"), + ENC_ACTION(C_ISO_8859_4)}, + {N_("/_View/Character _encoding/Baltic (Windows-1257)"), + ENC_ACTION(C_WINDOWS_1257)}, + ENC_SEPARATOR, + {N_("/_View/Character _encoding/Greek (ISO-8859-_7)"), + ENC_ACTION(C_ISO_8859_7)}, + ENC_SEPARATOR, + {N_("/_View/Character _encoding/Arabic (ISO-8859-_6)"), + ENC_ACTION(C_ISO_8859_6)}, + {N_("/_View/Character _encoding/Arabic (Windows-1256)"), + ENC_ACTION(C_CP1256)}, + ENC_SEPARATOR, + {N_("/_View/Character _encoding/Hebrew (ISO-8859-_8)"), + ENC_ACTION(C_ISO_8859_8)}, + {N_("/_View/Character _encoding/Hebrew (Windows-1255)"), + ENC_ACTION(C_CP1255)}, + ENC_SEPARATOR, + {N_("/_View/Character _encoding/Turkish (ISO-8859-_9)"), + ENC_ACTION(C_ISO_8859_9)}, + ENC_SEPARATOR, + {N_("/_View/Character _encoding/Cyrillic (ISO-8859-_5)"), + ENC_ACTION(C_ISO_8859_5)}, + {N_("/_View/Character _encoding/Cyrillic (KOI8-_R)"), + ENC_ACTION(C_KOI8_R)}, + {N_("/_View/Character _encoding/Cyrillic (KOI8-U)"), + ENC_ACTION(C_KOI8_U)}, + {N_("/_View/Character _encoding/Cyrillic (Windows-1251)"), + ENC_ACTION(C_CP1251)}, + ENC_SEPARATOR, + {N_("/_View/Character _encoding/Japanese (ISO-2022-_JP)"), + ENC_ACTION(C_ISO_2022_JP)}, + {N_("/_View/Character _encoding/Japanese (ISO-2022-JP-2)"), + ENC_ACTION(C_ISO_2022_JP_2)}, + {N_("/_View/Character _encoding/Japanese (_EUC-JP)"), + ENC_ACTION(C_EUC_JP)}, + {N_("/_View/Character _encoding/Japanese (_Shift__JIS)"), + ENC_ACTION(C_SHIFT_JIS)}, + ENC_SEPARATOR, + {N_("/_View/Character _encoding/Simplified Chinese (_GB2312)"), + ENC_ACTION(C_GB2312)}, + {N_("/_View/Character _encoding/Simplified Chinese (GBK)"), + ENC_ACTION(C_GBK)}, + {N_("/_View/Character _encoding/Traditional Chinese (_Big5)"), + ENC_ACTION(C_BIG5)}, + {N_("/_View/Character _encoding/Traditional Chinese (EUC-_TW)"), + ENC_ACTION(C_EUC_TW)}, + {N_("/_View/Character _encoding/Chinese (ISO-2022-_CN)"), + ENC_ACTION(C_ISO_2022_CN)}, + ENC_SEPARATOR, + {N_("/_View/Character _encoding/Korean (EUC-_KR)"), + ENC_ACTION(C_EUC_KR)}, + {N_("/_View/Character _encoding/Korean (ISO-2022-KR)"), + ENC_ACTION(C_ISO_2022_KR)}, + ENC_SEPARATOR, + {N_("/_View/Character _encoding/Thai (TIS-620)"), + ENC_ACTION(C_TIS_620)}, + {N_("/_View/Character _encoding/Thai (Windows-874)"), + ENC_ACTION(C_WINDOWS_874)}, + +#undef ENC_SEPARATOR +#undef ENC_ACTION + + {N_("/_View/---"), NULL, NULL, 0, ""}, + {N_("/_View/Mess_age source"), NULL, view_source_cb, 0, NULL}, + {N_("/_View/All _headers"), + NULL, show_all_header_cb, 0, ""}, + + {N_("/_Message"), NULL, NULL, 0, ""}, + {N_("/_Message/Compose _new message"), + NULL, compose_cb, 0, NULL}, + {N_("/_Message/---"), NULL, NULL, 0, ""}, + {N_("/_Message/_Reply"), NULL, reply_cb, COMPOSE_REPLY, NULL}, + {N_("/_Message/Repl_y to/_all"), + NULL, reply_cb, COMPOSE_REPLY_TO_ALL, NULL}, + {N_("/_Message/Repl_y to/_sender"), + NULL, reply_cb, COMPOSE_REPLY_TO_SENDER, NULL}, + {N_("/_Message/Repl_y to/mailing _list"), + NULL, reply_cb, COMPOSE_REPLY_TO_LIST, NULL}, + {N_("/_Message/---"), NULL, NULL, 0, ""}, + {N_("/_Message/_Forward"), NULL, reply_cb, COMPOSE_FORWARD, NULL}, + {N_("/_Message/For_ward as attachment"), + NULL, reply_cb, COMPOSE_FORWARD_AS_ATTACH, NULL}, + {N_("/_Message/Redirec_t"), NULL, reply_cb, COMPOSE_REDIRECT, NULL}, + {N_("/_Message/---"), NULL, NULL, 0, ""}, + {N_("/_Message/Re-_edit"), NULL, reedit_cb, 0, NULL}, + + {N_("/_Tools"), NULL, NULL, 0, ""}, + {N_("/_Tools/_Address book"), NULL, addressbook_open_cb, 0, NULL}, + {N_("/_Tools/Add sender to address boo_k"), + NULL, add_address_cb, 0, NULL}, + {N_("/_Tools/---"), NULL, NULL, 0, ""}, + {N_("/_Tools/_Create filter rule"), + NULL, NULL, 0, ""}, + {N_("/_Tools/_Create filter rule/_Automatically"), + NULL, create_filter_cb, FLT_BY_AUTO, NULL}, + {N_("/_Tools/_Create filter rule/by _From"), + NULL, create_filter_cb, FLT_BY_FROM, NULL}, + {N_("/_Tools/_Create filter rule/by _To"), + NULL, create_filter_cb, FLT_BY_TO, NULL}, + {N_("/_Tools/_Create filter rule/by _Subject"), + NULL, create_filter_cb, FLT_BY_SUBJECT, NULL}, +#ifndef G_OS_WIN32 + {N_("/_Tools/---"), NULL, NULL, 0, ""}, + {N_("/_Tools/Actio_ns"), NULL, NULL, 0, ""}, +#endif + + {N_("/_Help"), NULL, NULL, 0, ""}, + {N_("/_Help/_About"), NULL, about_cb, 0, NULL} +}; + + +MessageView *messageview_create(void) +{ + MessageView *messageview; + GtkWidget *vbox; + GtkWidget *notebook; + GtkWidget *hbox; + GtkWidget *toolbar_vbox; + GtkWidget *toolbar_hbox; + GtkWidget *image; + GtkWidget *arrow; + GtkWidget *mime_toggle_btn; + GtkWidget *menu_tool_btn; + GtkWidget *tool_menu; + GtkWidget *menuitem; + HeaderView *headerview; + TextView *textview; + MimeView *mimeview; + GtkTooltips *tip; + + debug_print(_("Creating message view...\n")); + messageview = g_new0(MessageView, 1); + + messageview->type = MVIEW_TEXT; + + headerview = headerview_create(); + + textview = textview_create(); + textview->messageview = messageview; + + mimeview = mimeview_create(); + mimeview->textview = textview_create(); + mimeview->textview->messageview = messageview; + mimeview->imageview = imageview_create(); + mimeview->imageview->messageview = messageview; + mimeview->messageview = messageview; + + notebook = gtk_notebook_new(); + gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE); + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE); + gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE); + gtk_widget_show(notebook); + + gtk_container_add(GTK_CONTAINER(notebook), GTK_WIDGET_PTR(textview)); + gtk_notebook_set_tab_label_text + (GTK_NOTEBOOK(notebook), GTK_WIDGET_PTR(textview), _("Text")); + + gtk_container_add(GTK_CONTAINER(notebook), GTK_WIDGET_PTR(mimeview)); + gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(notebook), + GTK_WIDGET_PTR(mimeview), + _("Attachments")); + + gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 0); + gtk_widget_show_all(notebook); + + /* Attachment toolbar */ + toolbar_vbox = gtk_vbox_new(FALSE, 0); + gtk_widget_show(toolbar_vbox); + + toolbar_hbox = gtk_hbox_new(FALSE, 0); + gtk_widget_show(toolbar_hbox); + gtk_box_pack_start(GTK_BOX(toolbar_vbox), toolbar_hbox, TRUE, FALSE, 0); + + mime_toggle_btn = gtk_toggle_button_new(); + gtk_widget_show(mime_toggle_btn); + gtk_box_pack_start(GTK_BOX(toolbar_hbox), mime_toggle_btn, + FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(mime_toggle_btn), "toggled", + G_CALLBACK(messageview_toggle_attach_cb), messageview); + + image = stock_pixbuf_widget_scale(NULL, STOCK_PIXMAP_MAIL_ATTACH, 18, 18); + gtk_widget_show(image); + gtk_container_add(GTK_CONTAINER(mime_toggle_btn), image); + + tip = gtk_tooltips_new(); + gtk_tooltips_set_tip(tip, mime_toggle_btn, + _("Switch to attachment list view"), NULL); + + arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT); + gtk_widget_show(arrow); + gtk_widget_set_size_request(arrow, 5, -1); + + menu_tool_btn = gtk_button_new(); + gtk_container_add(GTK_CONTAINER(menu_tool_btn), arrow); + GTK_WIDGET_UNSET_FLAGS(menu_tool_btn, GTK_CAN_FOCUS); + gtk_widget_show(menu_tool_btn); + gtk_box_pack_start(GTK_BOX(toolbar_hbox), menu_tool_btn, + FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(menu_tool_btn), "button_press_event", + G_CALLBACK(messageview_menu_tool_btn_pressed), + messageview); + + tool_menu = gtk_menu_new(); + MENUITEM_ADD(tool_menu, menuitem, NULL, 0); + MENUITEM_ADD_WITH_MNEMONIC(tool_menu, menuitem, _("Save _all attachments..."), 0); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(messageview_save_all_cb), messageview); + gtk_widget_show_all(tool_menu); + + gtk_widget_hide(toolbar_vbox); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_widget_show(hbox); + gtk_box_pack_start(GTK_BOX(hbox), toolbar_vbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET_PTR(headerview), TRUE, TRUE, 0); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); + gtk_widget_show(vbox); + + /* to remove without destroyed */ + gtk_widget_ref(GTK_WIDGET_PTR(mimeview->textview)); + gtk_widget_ref(GTK_WIDGET_PTR(mimeview->imageview)); + + g_signal_connect(G_OBJECT(notebook), "switch_page", + G_CALLBACK(messageview_switch_page_cb), messageview); + + messageview->vbox = vbox; + + messageview->notebook = notebook; + + messageview->hbox = hbox; + messageview->toolbar_vbox = toolbar_vbox; + messageview->mime_toggle_btn = mime_toggle_btn; + messageview->menu_tool_btn = menu_tool_btn; + messageview->tool_menu = tool_menu; + + messageview->new_window = FALSE; + messageview->window = NULL; + messageview->window_vbox = NULL; + messageview->body_vbox = NULL; + + messageview->headerview = headerview; + messageview->textview = textview; + messageview->mimeview = mimeview; + + messageview->statusbar = NULL; + messageview->statusbar_cid = 0; + + messageview->current_page = 0; + + return messageview; +} + +MessageView *messageview_create_with_new_window(void) +{ + MessageView *msgview; + GtkWidget *window; + GtkWidget *window_vbox; + GtkWidget *body_vbox; + GtkWidget *vspacer; + GtkWidget *menubar; + GtkItemFactory *ifactory; + GtkWidget *statusbar; + guint n_menu_entries; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), _("Message View - Sylpheed")); + gtk_window_set_wmclass(GTK_WINDOW(window), "message_view", "Sylpheed"); + gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE); + gtk_widget_set_size_request(window, prefs_common.msgwin_width, + prefs_common.msgwin_height); + + msgview = messageview_create(); + + window_vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(window), window_vbox); + + g_signal_connect(G_OBJECT(window), "size_allocate", + G_CALLBACK(messageview_size_allocate_cb), + msgview); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(messageview_delete_cb), msgview); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(key_pressed), msgview); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + + n_menu_entries = sizeof(msgview_entries) / sizeof (msgview_entries[0]); + menubar = menubar_create(window, msgview_entries, n_menu_entries, + "", msgview); +#if 0 + menu_factory_copy_rc("
", ""); +#endif + gtk_box_pack_start(GTK_BOX(window_vbox), menubar, FALSE, TRUE, 0); + + vspacer = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(window_vbox), vspacer, FALSE, TRUE, + BORDER_WIDTH); + + body_vbox = gtk_vbox_new(FALSE, BORDER_WIDTH); + gtk_box_pack_start(GTK_BOX(window_vbox), body_vbox, TRUE, TRUE, 0); + + gtk_box_pack_start(GTK_BOX(body_vbox), GTK_WIDGET_PTR(msgview), + TRUE, TRUE, 0); + gtk_widget_grab_focus(msgview->textview->text); + + statusbar = gtk_statusbar_new(); + gtk_box_pack_end(GTK_BOX(body_vbox), statusbar, FALSE, FALSE, 0); + msgview->statusbar = statusbar; + msgview->statusbar_cid = gtk_statusbar_get_context_id + (GTK_STATUSBAR(statusbar), "Message View"); + + msgview->new_window = TRUE; + msgview->window = window; + msgview->window_vbox = window_vbox; + msgview->body_vbox = body_vbox; + msgview->menubar = menubar; + msgview->menu_locked = FALSE; + msgview->visible = TRUE; + + gtk_widget_show_all(window); + gtk_widget_hide(msgview->toolbar_vbox); + + messageview_init(msgview); + + messageview_set_encoding_menu(msgview); + + ifactory = gtk_item_factory_from_widget(menubar); +#ifndef G_OS_WIN32 + action_update_msgview_menu(ifactory, msgview); +#endif + + messageview_list = g_list_append(messageview_list, msgview); + + return msgview; +} + +void messageview_init(MessageView *messageview) +{ + headerview_init(messageview->headerview); + textview_init(messageview->textview); + mimeview_init(messageview->mimeview); + /* messageview_set_font(messageview); */ +} + +void messageview_reflect_prefs(MessageView *messageview) +{ + if (messageview->type == MVIEW_MIME) + messageview_set_mime_view_layout(messageview); +} + +GList *messageview_get_window_list(void) +{ + return messageview_list; +} + +gint messageview_show(MessageView *messageview, MsgInfo *msginfo, + gboolean all_headers) +{ + gchar *file; + MimeInfo *mimeinfo; + + g_return_val_if_fail(msginfo != NULL, -1); + + mimeinfo = procmime_scan_message(msginfo); + if (!mimeinfo) { + messageview_change_view_type(messageview, MVIEW_TEXT); + textview_show_error(messageview->textview); + return -1; + } + + file = procmsg_get_message_file_path(msginfo); + if (!file) { + g_warning("can't get message file path.\n"); + procmime_mimeinfo_free_all(mimeinfo); + messageview_change_view_type(messageview, MVIEW_TEXT); + textview_show_error(messageview->textview); + return -1; + } + + if (messageview->msginfo != msginfo) { + procmsg_msginfo_free(messageview->msginfo); + messageview->msginfo = procmsg_msginfo_get_full_info(msginfo); + if (!messageview->msginfo) + messageview->msginfo = procmsg_msginfo_copy(msginfo); + } + procmime_mimeinfo_free_all(messageview->mimeinfo); + messageview->mimeinfo = mimeinfo; + g_free(messageview->file); + messageview->file = file; + + if (messageview->window && msginfo->subject) { + gchar *title; + + title = g_strconcat(msginfo->subject, " - Sylpheed", NULL); + gtk_window_set_title(GTK_WINDOW(messageview->window), title); + g_free(title); + } + headerview_show(messageview->headerview, messageview->msginfo); + + textview_set_all_headers(messageview->textview, all_headers); + textview_set_all_headers(messageview->mimeview->textview, all_headers); + + if (mimeinfo->mime_type != MIME_TEXT && + (prefs_common.html_only_as_attach || + mimeinfo->mime_type != MIME_TEXT_HTML)) { + messageview_change_view_type(messageview, MVIEW_MIME); + mimeview_show_message(messageview->mimeview, mimeinfo, file); + messageview_set_tool_menu(messageview, mimeinfo); + } else { + messageview_change_view_type(messageview, MVIEW_TEXT); + textview_show_message(messageview->textview, mimeinfo, file); + } + + if (messageview->new_window) + messageview_set_menu_state(messageview); + + syl_plugin_signal_emit("messageview-show", messageview, msginfo, + all_headers); + + return 0; +} + +static void messageview_set_mime_view_layout(MessageView *messageview) +{ + if (prefs_common.attach_toolbutton_pos == 0) + gtk_box_set_child_packing(GTK_BOX(messageview->hbox), messageview->toolbar_vbox, FALSE, FALSE, 0, GTK_PACK_START); + else + gtk_box_set_child_packing(GTK_BOX(messageview->hbox), messageview->toolbar_vbox, FALSE, FALSE, 0, GTK_PACK_END); + + if (prefs_common.show_attach_tab) + gtk_widget_hide(messageview->toolbar_vbox); + else + gtk_widget_show(messageview->toolbar_vbox); + + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(messageview->notebook), + prefs_common.show_attach_tab); +} + +static void messageview_change_view_type(MessageView *messageview, + MessageType type) +{ + GtkWidget *notebook = messageview->notebook; + + if (messageview->type == type) return; + + if (type == MVIEW_MIME) { + messageview_set_mime_view_layout(messageview); + gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), + messageview->current_page); + if (messageview->current_page == 0) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(messageview->mime_toggle_btn), FALSE); + else + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(messageview->mime_toggle_btn), TRUE); + } else if (type == MVIEW_TEXT) { + gint current_page = messageview->current_page; + + gtk_widget_hide(messageview->toolbar_vbox); + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE); + gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 0); + messageview->current_page = current_page; + mimeview_clear(messageview->mimeview); + } else + return; + + messageview->type = type; +} + +static void messageview_set_tool_menu(MessageView *messageview, + MimeInfo *mimeinfo) +{ + MimeInfo *partinfo; + const gchar *name; + GtkWidget *menuitem; + GList *cur_item, *next = NULL; + gint pos = 0; + gboolean exist; + + cur_item = GTK_MENU_SHELL(messageview->tool_menu)->children; + while (cur_item != NULL) { + next = cur_item->next; + if (GTK_BIN(cur_item->data)->child == NULL) + break; + gtk_widget_destroy(GTK_WIDGET(cur_item->data)); + cur_item = next; + } + + for (partinfo = mimeinfo; partinfo != NULL; + partinfo = procmime_mimeinfo_next(partinfo)) { + if (partinfo->filename || partinfo->name) { + name = g_basename(partinfo->filename ? partinfo->filename : partinfo->name); + if (*name == '\0') + name = "mimetmp"; + menuitem = gtk_menu_item_new_with_label(name); + gtk_widget_show(menuitem); + gtk_menu_shell_insert(GTK_MENU_SHELL(messageview->tool_menu), menuitem, pos++); + g_object_set_data(G_OBJECT(menuitem), "tool-menu-mimeinfo", partinfo); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(messageview_open_file_cb), + messageview); + } + } + + exist = (pos != 0); + for (cur_item = next; cur_item != NULL; cur_item = cur_item->next) { + menuitem = GTK_WIDGET(cur_item->data); + gtk_widget_set_sensitive(menuitem, exist); + } +} + +static void messageview_set_menu_state(MessageView *messageview) +{ + GtkItemFactory *ifactory; + GtkWidget *menuitem; + + messageview->menu_locked = TRUE; + + ifactory = gtk_item_factory_from_widget(messageview->menubar); + menuitem = gtk_item_factory_get_widget + (ifactory, "/View/All headers"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), + messageview->textview->show_all_headers); + + if (messageview->msginfo && + FOLDER_ITEM_IS_SENT_FOLDER(messageview->msginfo->folder)) + menu_set_sensitive(ifactory, "/Message/Re-edit", TRUE); + else + menu_set_sensitive(ifactory, "/Message/Re-edit", FALSE); + + messageview->menu_locked = FALSE; +} + +static void messageview_set_encoding_menu(MessageView *messageview) +{ + GtkItemFactoryEntry *entry; + GtkItemFactory *ifactory; + CharSet encoding; + gchar *path, *p, *q; + GtkWidget *item; + + encoding = conv_get_charset_from_str(prefs_common.force_charset); + ifactory = gtk_item_factory_from_widget(messageview->menubar); + + for (entry = msgview_entries; entry->callback != view_source_cb; + entry++) { + if (entry->callback == set_charset_cb && + (CharSet)entry->callback_action == encoding) { + p = q = path = g_strdup(entry->path); + while (*p) { + if (*p == '_') { + if (p[1] == '_') { + p++; + *q++ = '_'; + } + } else + *q++ = *p; + p++; + } + *q = '\0'; + item = gtk_item_factory_get_item(ifactory, path); + gtk_widget_activate(item); + g_free(path); + break; + } + } +} + +void messageview_clear(MessageView *messageview) +{ + messageview_set_tool_menu(messageview, NULL); + g_free(messageview->file); + messageview->file = NULL; + procmime_mimeinfo_free_all(messageview->mimeinfo); + messageview->mimeinfo = NULL; + procmsg_msginfo_free(messageview->msginfo); + messageview->msginfo = NULL; + messageview_change_view_type(messageview, MVIEW_TEXT); + headerview_clear(messageview->headerview); + textview_clear(messageview->textview); + mimeview_clear(messageview->mimeview); +} + +void messageview_destroy(MessageView *messageview) +{ + GtkWidget *textview = GTK_WIDGET_PTR(messageview->mimeview->textview); + GtkWidget *imageview = GTK_WIDGET_PTR(messageview->mimeview->imageview); + + messageview_list = g_list_remove(messageview_list, messageview); + + headerview_destroy(messageview->headerview); + textview_destroy(messageview->textview); + mimeview_destroy(messageview->mimeview); + + procmsg_msginfo_free(messageview->msginfo); + + gtk_widget_destroy(messageview->tool_menu); + if (messageview->window) + gtk_widget_destroy(messageview->window); + + g_free(messageview); + + gtk_widget_unref(textview); + gtk_widget_unref(imageview); +} + +void messageview_quote_color_set(void) +{ +} + +void messageview_set_font(MessageView *messageview) +{ + textview_set_font(messageview->textview, NULL); +} + +TextView *messageview_get_current_textview(MessageView *messageview) +{ + TextView *text = NULL; + + if (messageview->type == MVIEW_TEXT) + text = messageview->textview; + else if (messageview->type == MVIEW_MIME) { + if (gtk_notebook_get_current_page + (GTK_NOTEBOOK(messageview->notebook)) == 0) + text = messageview->textview; + else if (messageview->mimeview->type == MIMEVIEW_TEXT) + text = messageview->mimeview->textview; + } + + return text; +} + +MimeInfo *messageview_get_selected_mime_part(MessageView *messageview) +{ + if (messageview->type == MVIEW_MIME) + return mimeview_get_selected_part(messageview->mimeview); + + return NULL; +} + +void messageview_copy_clipboard(MessageView *messageview) +{ + TextView *text; + + text = messageview_get_current_textview(messageview); + if (text) { + GtkTextView *textview = GTK_TEXT_VIEW(text->text); + GtkTextBuffer *buffer; + GtkClipboard *clipboard; + + buffer = gtk_text_view_get_buffer(textview); + clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + gtk_text_buffer_copy_clipboard(buffer, clipboard); + } +} + +void messageview_select_all(MessageView *messageview) +{ + TextView *text; + + text = messageview_get_current_textview(messageview); + if (text) { + GtkTextView *textview = GTK_TEXT_VIEW(text->text); + GtkTextBuffer *buffer; + GtkTextIter start, end; + + buffer = gtk_text_view_get_buffer(textview); + gtk_text_buffer_get_bounds(buffer, &start, &end); + gtk_text_buffer_select_range(buffer, &start, &end); + } +} + +void messageview_set_position(MessageView *messageview, gint pos) +{ + textview_set_position(messageview->textview, pos); +} + +gboolean messageview_search_string(MessageView *messageview, const gchar *str, + gboolean case_sens) +{ + return textview_search_string(messageview->textview, str, case_sens); + return FALSE; +} + +gboolean messageview_search_string_backward(MessageView *messageview, + const gchar *str, + gboolean case_sens) +{ + return textview_search_string_backward(messageview->textview, + str, case_sens); + return FALSE; +} + +gboolean messageview_is_visible(MessageView *messageview) +{ + return messageview->visible; +} + +void messageview_save_as(MessageView *messageview) +{ + gchar *filename = NULL; + MsgInfo *msginfo; + gchar *src, *dest; + FileselFileType types[4] = {{NULL, NULL}}; + gint selected_type = 0; + gint result; + gboolean all_headers; + + if (!messageview->msginfo) return; + msginfo = messageview->msginfo; + + if (msginfo->subject && *msginfo->subject) { + filename = g_strdup_printf("%s.eml", msginfo->subject); + } else { + filename = g_strdup_printf("%u.eml", msginfo->msgnum); + } + subst_for_filename(filename); + + types[0].type = _("Original (EML/RFC 822)"); + types[0].ext = "eml"; + types[1].type = _("Text"); + types[1].ext = "txt"; + types[2].type = _("Text (UTF-8)"); + types[2].ext = "txt"; + dest = filesel_save_as_type(filename, types, prefs_common.save_file_type, &selected_type); + + g_free(filename); + if (!dest) + return; + + all_headers = messageview->textview->show_all_headers; + + if (selected_type == 1) { + result = procmsg_save_message_as_text(msginfo, dest, conv_get_locale_charset_str(), all_headers); + } else if (selected_type == 2) { + result = procmsg_save_message_as_text(msginfo, dest, NULL, all_headers); + } else { + src = procmsg_get_message_file(msginfo); + result = copy_file(src, dest, TRUE); + g_free(src); + } + + if (result < 0) { + gchar *utf8_dest; + + utf8_dest = conv_filename_to_utf8(dest); + alertpanel_error(_("Can't save the file `%s'."), + g_basename(utf8_dest)); + g_free(utf8_dest); + } + + g_free(dest); + + prefs_common.save_file_type = selected_type; +} + +static gint messageview_delete_cb(GtkWidget *widget, GdkEventAny *event, + MessageView *messageview) +{ + messageview_destroy(messageview); + return TRUE; +} + +static void messageview_size_allocate_cb(GtkWidget *widget, + GtkAllocation *allocation) +{ + g_return_if_fail(allocation != NULL); + + prefs_common.msgwin_width = allocation->width; + prefs_common.msgwin_height = allocation->height; +} + +static void messageview_toggle_attach_cb(GtkToggleButton *toggle, + MessageView *messageview) +{ + if (gtk_toggle_button_get_active(toggle)) + gtk_notebook_set_current_page(GTK_NOTEBOOK(messageview->notebook), 1); + else + gtk_notebook_set_current_page(GTK_NOTEBOOK(messageview->notebook), 0); +} + +static void messageview_switch_page_cb(GtkNotebook *notebook, + GtkNotebookPage *page, guint page_num, + MessageView *messageview) +{ + if (messageview->current_page != page_num) { + messageview->current_page = page_num; + if (page_num == 0) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(messageview->mime_toggle_btn), FALSE); + else + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(messageview->mime_toggle_btn), TRUE); + } +} + +static gint messageview_menu_tool_btn_pressed(GtkWidget *widget, + GdkEventButton *event, + MessageView *messageview) +{ + if (!event) + return FALSE; + + gtk_menu_popup(GTK_MENU(messageview->tool_menu), NULL, NULL, + menu_button_position, messageview->mime_toggle_btn, + event->button, event->time); + + return TRUE; +} + +static void messageview_open_file_cb(GtkWidget *widget, MessageView *messageview) +{ + MimeInfo *partinfo; + + partinfo = g_object_get_data(G_OBJECT(widget), "tool-menu-mimeinfo"); + if (partinfo) + mimeview_launch_part(messageview->mimeview, partinfo); +} + +static void messageview_save_all_cb(GtkWidget *widget, MessageView *messageview) +{ + mimeview_save_all(messageview->mimeview); +} + +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, + MessageView *messageview) +{ + if (event && event->keyval == GDK_Escape && messageview->window) { + messageview_destroy(messageview); + return TRUE; + } + return FALSE; +} + +static void save_as_cb(gpointer data, guint action, GtkWidget *widget) +{ + MessageView *messageview = (MessageView *)data; + messageview_save_as(messageview); +} + +#if GTK_CHECK_VERSION(2, 10, 0) +static void page_setup_cb(gpointer data, guint action, GtkWidget *widget) +{ + printing_page_setup_gtk(); +} +#endif + +static void print_cb(gpointer data, guint action, GtkWidget *widget) +{ + MessageView *messageview = (MessageView *)data; + + if (!messageview->msginfo) return; + + printing_print_message(messageview->msginfo, + messageview->textview->show_all_headers); +} + +static void close_cb(gpointer data, guint action, GtkWidget *widget) +{ + MessageView *messageview = (MessageView *)data; + messageview_destroy(messageview); +} + +static void copy_cb(gpointer data, guint action, GtkWidget *widget) +{ + MessageView *messageview = (MessageView *)data; + messageview_copy_clipboard(messageview); +} + +static void allsel_cb(gpointer data, guint action, GtkWidget *widget) +{ + MessageView *messageview = (MessageView *)data; + messageview_select_all(messageview); +} + +static void search_cb(gpointer data, guint action, GtkWidget *widget) +{ + MessageView *messageview = (MessageView *)data; + message_search(messageview); +} + +static void set_charset_cb(gpointer data, guint action, GtkWidget *widget) +{ + MessageView *messageview = (MessageView *)data; + const gchar *charset; + + if (GTK_CHECK_MENU_ITEM(widget)->active) { + charset = conv_get_charset_str((CharSet)action); + g_free(messageview->forced_charset); + messageview->forced_charset = g_strdup(charset); + if (messageview->msginfo) + messageview_show(messageview, messageview->msginfo, + FALSE); + } +} + +static void view_source_cb(gpointer data, guint action, GtkWidget *widget) +{ + MessageView *messageview = (MessageView *)data; + SourceWindow *srcwin; + + if (!messageview->msginfo) return; + + srcwin = source_window_create(); + source_window_show_msg(srcwin, messageview->msginfo); + source_window_show(srcwin); +} + +static void show_all_header_cb(gpointer data, guint action, GtkWidget *widget) +{ + MessageView *messageview = (MessageView *)data; + MsgInfo *msginfo = messageview->msginfo; + + if (!msginfo) return; + if (messageview->menu_locked) return; + + messageview->msginfo = NULL; + messageview_show(messageview, msginfo, + GTK_CHECK_MENU_ITEM(widget)->active); + procmsg_msginfo_free(msginfo); +} + +static void compose_cb(gpointer data, guint action, GtkWidget *widget) +{ + MessageView *messageview = (MessageView *)data; + PrefsAccount *ac = NULL; + FolderItem *item = NULL; + + if (messageview->msginfo) + item = messageview->msginfo->folder; + + if (item) { + ac = account_find_from_item(item); + if (ac && ac->protocol == A_NNTP && + FOLDER_TYPE(item->folder) == F_NEWS) { + compose_new(ac, item, item->path, NULL); + return; + } + } + + compose_new(ac, item, NULL, NULL); +} + +static void reply_cb(gpointer data, guint action, GtkWidget *widget) +{ + MessageView *messageview = (MessageView *)data; + GSList *mlist = NULL; + MsgInfo *msginfo; + gchar *text = NULL; + ComposeMode mode = (ComposeMode)action; + gchar *prev_force_charset; + + msginfo = messageview->msginfo; + mlist = g_slist_append(NULL, msginfo); + + text = gtkut_text_view_get_selection + (GTK_TEXT_VIEW(messageview->textview->text)); + if (text && *text == '\0') { + g_free(text); + text = NULL; + } + + if (!COMPOSE_QUOTE_MODE(mode)) + mode |= prefs_common.reply_with_quote + ? COMPOSE_WITH_QUOTE : COMPOSE_WITHOUT_QUOTE; + + prev_force_charset = prefs_common.force_charset; + prefs_common.force_charset = messageview->forced_charset; + + switch (COMPOSE_MODE(mode)) { + case COMPOSE_REPLY: + case COMPOSE_REPLY_TO_SENDER: + case COMPOSE_REPLY_TO_ALL: + case COMPOSE_REPLY_TO_LIST: + compose_reply(msginfo, msginfo->folder, mode, text); + break; + case COMPOSE_FORWARD: + compose_forward(mlist, msginfo->folder, FALSE, text); + break; + case COMPOSE_FORWARD_AS_ATTACH: + compose_forward(mlist, msginfo->folder, TRUE, NULL); + break; + case COMPOSE_REDIRECT: + compose_redirect(msginfo, msginfo->folder); + break; + default: + g_warning("messageview.c: reply_cb(): invalid mode: %d\n", + mode); + } + + prefs_common.force_charset = prev_force_charset; + + /* summary_set_marks_selected(summaryview); */ + g_free(text); + g_slist_free(mlist); +} + +static void reedit_cb(gpointer data, guint action, GtkWidget *widget) +{ + MessageView *messageview = (MessageView *)data; + MsgInfo *msginfo; + + if (!messageview->msginfo) return; + msginfo = messageview->msginfo; + if (!msginfo->folder) return; + if (msginfo->folder->stype != F_OUTBOX && + msginfo->folder->stype != F_DRAFT && + msginfo->folder->stype != F_QUEUE) return; + + compose_reedit(msginfo); +} + +static void addressbook_open_cb(gpointer data, guint action, GtkWidget *widget) +{ + addressbook_open(NULL); +} + +static void add_address_cb(gpointer data, guint action, GtkWidget *widget) +{ + MessageView *messageview = (MessageView *)data; + MsgInfo *msginfo; + gchar *from; + + if (!messageview->msginfo) return; + msginfo = messageview->msginfo; + from = g_strdup(msginfo->from); + eliminate_address_comment(from); + extract_address(from); + addressbook_add_contact(msginfo->fromname, from, NULL); + g_free(from); +} + +static void create_filter_cb(gpointer data, guint action, GtkWidget *widget) +{ + MessageView *messageview = (MessageView *)data; + gchar *header = NULL; + gchar *key = NULL; + + if (!messageview->msginfo) return; + + filter_get_keyword_from_msg(messageview->msginfo, &header, &key, + (FilterCreateType)action); + prefs_filter_open(messageview->msginfo, header, key); + + g_free(header); + g_free(key); +} + +static void about_cb(gpointer data, guint action, GtkWidget *widget) +{ + about_show(); +} diff --git a/src/messageview.h b/src/messageview.h new file mode 100644 index 0000000..eee37c4 --- /dev/null +++ b/src/messageview.h @@ -0,0 +1,112 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2012 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __MESSAGEVIEW_H__ +#define __MESSAGEVIEW_H__ + +#include +#include + +typedef struct _MessageView MessageView; + +#include "mainwindow.h" +#include "headerview.h" +#include "textview.h" +#include "mimeview.h" +#include "procmsg.h" +#include "procmime.h" + +typedef enum +{ + MVIEW_TEXT, + MVIEW_MIME +} MessageType; + +struct _MessageView +{ + GtkWidget *vbox; + + GtkWidget *notebook; + + MessageType type; + + GtkWidget *hbox; + GtkWidget *toolbar_vbox; + GtkWidget *mime_toggle_btn; + GtkWidget *menu_tool_btn; + GtkWidget *tool_menu; + + gboolean new_window; + GtkWidget *window; + GtkWidget *window_vbox; + GtkWidget *body_vbox; + GtkWidget *menubar; + gboolean menu_locked; + + HeaderView *headerview; + TextView *textview; + MimeView *mimeview; + + GtkWidget *statusbar; + gint statusbar_cid; + + MainWindow *mainwin; + + MsgInfo *msginfo; + + gchar *forced_charset; + + gboolean visible; + gint current_page; + + MimeInfo *mimeinfo; + gchar *file; +}; + +MessageView *messageview_create (void); +MessageView *messageview_create_with_new_window (void); +void messageview_init (MessageView *messageview); +void messageview_reflect_prefs (MessageView *messageview); +gint messageview_show (MessageView *messageview, + MsgInfo *msginfo, + gboolean all_headers); +void messageview_clear (MessageView *messageview); +void messageview_destroy (MessageView *messageview); + +void messageview_quote_color_set (void); +void messageview_set_font (MessageView *messageview); + +TextView *messageview_get_current_textview (MessageView *messageview); +MimeInfo *messageview_get_selected_mime_part (MessageView *messageview); + +void messageview_copy_clipboard (MessageView *messageview); +void messageview_select_all (MessageView *messageview); +void messageview_set_position (MessageView *messageview, + gint pos); + +gboolean messageview_search_string (MessageView *messageview, + const gchar *str, + gboolean case_sens); +gboolean messageview_search_string_backward (MessageView *messageview, + const gchar *str, + gboolean case_sens); + +gboolean messageview_is_visible (MessageView *messageview); + +#endif /* __MESSAGEVIEW_H__ */ diff --git a/src/mgutils.c b/src/mgutils.c new file mode 100644 index 0000000..45ba8f4 --- /dev/null +++ b/src/mgutils.c @@ -0,0 +1,220 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * General functions for create common address book entries. + */ + +#include +#include +#include + +#include "mgutils.h" + +/* +* Dump linked list of character strings (for debug). +*/ +void mgu_print_list( GSList *list, FILE *stream ) { + GSList *node = list; + while( node ) { + fprintf( stream, "\t- >%s<\n", (gchar *)node->data ); + node = g_slist_next( node ); + } +} + +/* +* Dump linked list of character strings (for debug). +*/ +void mgu_print_dlist( GList *list, FILE *stream ) { + GList *node = list; + while( node ) { + fprintf( stream, "\t- >%s<\n", (gchar *)node->data ); + node = g_list_next( node ); + } +} + +/* +* Free linked list of character strings. +*/ +void mgu_free_list( GSList *list ) { + GSList *node = list; + while( node ) { + g_free( node->data ); + node->data = NULL; + node = g_slist_next( node ); + } + g_slist_free( list ); +} + +/* +* Free linked list of character strings. +*/ +void mgu_free_dlist( GList *list ) { + GList *node = list; + while( node ) { + g_free( node->data ); + node->data = NULL; + node = g_list_next( node ); + } + g_list_free( list ); +} + +/* +* Coalesce linked list of characaters into one long string. +*/ +gchar *mgu_list_coalesce( GSList *list ) { + gchar *str = NULL; + gchar *buf = NULL; + gchar *start = NULL; + GSList *node = NULL; + gint len; + + if( ! list ) return NULL; + + /* Calculate maximum length of text */ + len = 0; + node = list; + while( node ) { + str = node->data; + len += 1 + strlen( str ); + node = g_slist_next( node ); + } + + /* Create new buffer. */ + buf = g_new0( gchar, len+1 ); + start = buf; + node = list; + while( node ) { + str = node->data; + len = strlen( str ); + strcpy( start, str ); + start += len; + node = g_slist_next( node ); + } + return buf; +} + +struct mgu_error_entry { + gint e_code; + gchar *e_reason; +}; + +static const struct mgu_error_entry mgu_error_list[] = { + { MGU_SUCCESS, "Success" }, + { MGU_BAD_ARGS, "Bad arguments" }, + { MGU_NO_FILE, "File not specified" }, + { MGU_OPEN_FILE, "Error opening file" }, + { MGU_ERROR_READ, "Error reading file" }, + { MGU_EOF, "End of file encountered" }, + { MGU_OO_MEMORY, "Error allocating memory" }, + { MGU_BAD_FORMAT, "Bad file format" }, + { MGU_LDAP_CONNECT, "Error connecting to LDAP server" }, + { MGU_LDAP_INIT, "Error initializing LDAP" }, + { MGU_LDAP_BIND, "Error binding to LDAP server" }, + { MGU_LDAP_SEARCH, "Error searching LDAP database" }, + { MGU_LDAP_TIMEOUT, "Timeout performing LDAP operation" }, + { MGU_LDAP_CRITERIA, "Error in LDAP search criteria" }, + { MGU_LDAP_CRITERIA, "Error in LDAP search criteria" }, + { MGU_LDAP_NOENTRIES, "No LDAP entries found for search criteria" }, + { MGU_ERROR_WRITE, "Error writing to file" }, + { MGU_OPEN_DIRECTORY, "Error opening directory" }, + { MGU_NO_PATH, "No path specified" }, + { -999, NULL } +}; + +static const struct mgu_error_entry *mgu_error_find( gint err ) { + gint i; + for ( i = 0; mgu_error_list[i].e_code != -999; i++ ) { + if ( err == mgu_error_list[i].e_code ) + return & mgu_error_list[i]; + } + return NULL; +} + +/* +* Return error message for specified error code. +*/ +gchar *mgu_error2string( gint err ) { + const struct mgu_error_entry *e; + e = mgu_error_find( err ); + return ( e != NULL ) ? e->e_reason : "Unknown error"; +} + +/* +* Replace existing string with new string. +*/ +gchar *mgu_replace_string( gchar *str, const gchar *value ) { + if( str ) g_free( str ); + if( value ) { + str = g_strdup( value ); + g_strstrip( str ); + } + else { + str = NULL; + } + return str; +} + +/* +* Clear a linked list by setting node data pointers to NULL. Note that +* items are not freed. +*/ +void mgu_clear_slist( GSList *list ) { + GSList *node = list; + while( node ) { + node->data = NULL; + node = g_slist_next( node ); + } +} + +/* +* Clear a linked list by setting node data pointers to NULL. Note that +* items are not freed. +*/ +void mgu_clear_list( GList *list ) { + GList *node = list; + while( node ) { + node->data = NULL; + node = g_list_next( node ); + } +} + +/* +* Test and reformat an email address. +* Enter: address. +* Return: Address, or NULL if address is empty. +* Note: Leading and trailing white space is removed. +*/ +gchar *mgu_email_check_empty( gchar *address ) { + gchar *retVal = NULL; + if( address ) { + retVal = g_strdup( address ); + retVal = g_strchug( retVal ); + retVal = g_strchomp( retVal ); + if( *retVal == '\0' ) { + g_free( retVal ); + retVal = NULL; + } + } + return retVal; +} + +/* +* End of Source. +*/ diff --git a/src/mgutils.h b/src/mgutils.h new file mode 100644 index 0000000..626bf75 --- /dev/null +++ b/src/mgutils.h @@ -0,0 +1,62 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * General definitions for common address book entries. + */ + +#ifndef __MGUTILS_H__ +#define __MGUTILS_H__ + +#include +#include + +/* Error codes */ +#define MGU_SUCCESS 0 +#define MGU_BAD_ARGS -1 +#define MGU_NO_FILE -2 +#define MGU_OPEN_FILE -3 +#define MGU_ERROR_READ -4 +#define MGU_EOF -5 +#define MGU_OO_MEMORY -6 +#define MGU_BAD_FORMAT -7 +#define MGU_LDAP_CONNECT -8 +#define MGU_LDAP_INIT -9 +#define MGU_LDAP_BIND -10 +#define MGU_LDAP_SEARCH -11 +#define MGU_LDAP_TIMEOUT -12 +#define MGU_LDAP_CRITERIA -13 +#define MGU_LDAP_NOENTRIES -14 +#define MGU_ERROR_WRITE -15 +#define MGU_OPEN_DIRECTORY -16 +#define MGU_NO_PATH -17 + +/* Function prototypes */ +void mgu_print_list ( GSList *list, FILE *stream ); +void mgu_print_dlist ( GList *list, FILE *stream ); +void mgu_free_list ( GSList *list ); +void mgu_free_dlist ( GList *list ); +gchar *mgu_list_coalesce ( GSList *list ); +gchar *mgu_error2string ( gint err ); +gchar *mgu_replace_string ( gchar *str, const gchar *value ); +void mgu_clear_slist ( GSList *list ); +void mgu_clear_list ( GList *list ); +gchar *mgu_email_check_empty ( gchar *address ); + +#endif /* __MGUTILS_H__ */ diff --git a/src/mimeview.c b/src/mimeview.c new file mode 100644 index 0000000..e06b530 --- /dev/null +++ b/src/mimeview.c @@ -0,0 +1,1431 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2012 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef G_OS_WIN32 +# include +#endif + +#include "main.h" +#include "mimeview.h" +#include "textview.h" +#include "imageview.h" +#include "procmime.h" +#include "procheader.h" +#include "summaryview.h" +#include "menu.h" +#include "compose.h" +#include "printing.h" +#include "filesel.h" +#include "alertpanel.h" +#include "inputdialog.h" +#include "utils.h" +#include "gtkutils.h" +#include "prefs_common.h" +#include "rfc2015.h" + +enum +{ + COL_MIMETYPE, + COL_SIZE, + COL_NAME, + COL_MIME_INFO, + N_COLS +}; + +static void mimeview_set_multipart_tree (MimeView *mimeview, + MimeInfo *mimeinfo, + GtkTreeIter *parent); +static gboolean mimeview_append_part (MimeView *mimeview, + MimeInfo *partinfo, + GtkTreeIter *iter, + GtkTreeIter *parent); +static void mimeview_show_message_part (MimeView *mimeview, + MimeInfo *partinfo); +static void mimeview_show_image_part (MimeView *mimeview, + MimeInfo *partinfo); +static void mimeview_show_mime_part (MimeView *mimeview, + MimeInfo *partinfo); +#if USE_GPGME +static void mimeview_show_signature_part (MimeView *mimeview, + MimeInfo *partinfo); +#endif +static void mimeview_change_view_type (MimeView *mimeview, + MimeViewType type); + +static void mimeview_selection_changed (GtkTreeSelection *selection, + MimeView *mimeview); + +static gint mimeview_button_pressed (GtkWidget *widget, + GdkEventButton *event, + MimeView *mimeview); +static gint mimeview_key_pressed (GtkWidget *widget, + GdkEventKey *event, + MimeView *mimeview); + +static void mimeview_drag_begin (GtkWidget *widget, + GdkDragContext *drag_context, + MimeView *mimeview); +static void mimeview_drag_end (GtkWidget *widget, + GdkDragContext *drag_context, + MimeView *mimeview); +static void mimeview_drag_data_get (GtkWidget *widget, + GdkDragContext *drag_context, + GtkSelectionData *selection_data, + guint info, + guint time, + MimeView *mimeview); + +static void mimeview_display_as_text (MimeView *mimeview); +static void mimeview_launch (MimeView *mimeview); +static void mimeview_open_with (MimeView *mimeview); +static void mimeview_view_file (const gchar *filename, + MimeInfo *partinfo, + const gchar *cmdline); + +static void mimeview_reply (MimeView *mimeview, + guint action); + +#if USE_GPGME +static void mimeview_check_signature (MimeView *mimeview); +#endif + +static GtkItemFactoryEntry mimeview_popup_entries[] = +{ + {N_("/_Open"), NULL, mimeview_launch, 0, NULL}, + {N_("/Open _with..."), NULL, mimeview_open_with, 0, NULL}, + {N_("/_Display as text"), NULL, mimeview_display_as_text, 0, NULL}, + {N_("/_Save as..."), NULL, mimeview_save_as, 0, NULL}, + {N_("/Save _all..."), NULL, mimeview_save_all, 0, NULL}, + {N_("/_Print..."), NULL, mimeview_print, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Reply"), NULL, NULL, 0, ""}, + {N_("/_Reply/_Reply"), NULL, mimeview_reply, COMPOSE_REPLY, NULL}, + {N_("/_Reply/Reply to _all"), + NULL, mimeview_reply, COMPOSE_REPLY_TO_ALL, NULL}, + {N_("/_Reply/Reply to _sender"), + NULL, mimeview_reply, COMPOSE_REPLY_TO_SENDER, NULL}, + {N_("/_Reply/Reply to mailing _list"), + NULL, mimeview_reply, COMPOSE_REPLY_TO_LIST, NULL}, +#if USE_GPGME + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Check signature"), NULL, mimeview_check_signature, 0, NULL} +#endif +}; + +static GtkTargetEntry mimeview_mime_types[] = +{ + {"text/uri-list", 0, 0} +}; + +MimeView *mimeview_create(void) +{ + MimeView *mimeview; + + GtkWidget *paned; + GtkWidget *scrolledwin; + GtkWidget *treeview; + GtkTreeStore *store; + GtkTreeSelection *selection; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkWidget *mime_vbox; + GtkWidget *popupmenu; + GtkItemFactory *popupfactory; + gint n_entries; + GtkWidget *reply_separator; + GtkWidget *reply_menuitem; + GList *child; + + debug_print(_("Creating MIME view...\n")); + mimeview = g_new0(MimeView, 1); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + gtk_widget_set_size_request(scrolledwin, -1, 80); + + store = gtk_tree_store_new(N_COLS, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_POINTER); + + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), COL_NAME); + gtk_tree_view_set_reorderable(GTK_TREE_VIEW(treeview), FALSE); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); + + gtk_container_add(GTK_CONTAINER(scrolledwin), treeview); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes + (_("Data type"), renderer, "text", COL_MIMETYPE, NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "xalign", 1.0, "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes + (_("Size"), renderer, "text", COL_SIZE, NULL); + gtk_tree_view_column_set_alignment(column, 1.0); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "ypad", 0, NULL); + column = gtk_tree_view_column_new_with_attributes + (_("Name"), renderer, "text", COL_NAME, NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + gtk_tree_view_enable_model_drag_source + (GTK_TREE_VIEW(treeview), GDK_BUTTON1_MASK, + mimeview_mime_types, 1, GDK_ACTION_COPY); + + g_signal_connect(G_OBJECT(selection), "changed", + G_CALLBACK(mimeview_selection_changed), mimeview); + g_signal_connect(G_OBJECT(treeview), "button_press_event", + G_CALLBACK(mimeview_button_pressed), mimeview); + g_signal_connect(G_OBJECT(treeview), "key_press_event", + G_CALLBACK(mimeview_key_pressed), mimeview); + + g_signal_connect_after(G_OBJECT (treeview),"drag-begin", + G_CALLBACK (mimeview_drag_begin), mimeview); + g_signal_connect(G_OBJECT (treeview),"drag-end", + G_CALLBACK (mimeview_drag_end), mimeview); + g_signal_connect(G_OBJECT(treeview), "drag-data-get", + G_CALLBACK(mimeview_drag_data_get), mimeview); + + mime_vbox = gtk_vbox_new(FALSE, 0); + gtk_container_set_reallocate_redraws(GTK_CONTAINER(mime_vbox), TRUE); + + paned = gtk_vpaned_new(); + gtk_paned_add1(GTK_PANED(paned), scrolledwin); + gtk_paned_add2(GTK_PANED(paned), mime_vbox); + + n_entries = sizeof(mimeview_popup_entries) / + sizeof(mimeview_popup_entries[0]); + popupmenu = menu_create_items(mimeview_popup_entries, n_entries, + "", &popupfactory, mimeview); + + reply_menuitem = gtk_item_factory_get_item(popupfactory, "/Reply"); + child = g_list_find(GTK_MENU_SHELL(popupmenu)->children, + reply_menuitem); + reply_separator = GTK_WIDGET(child->prev->data); + + mimeview->paned = paned; + mimeview->scrolledwin = scrolledwin; + mimeview->treeview = treeview; + mimeview->store = store; + mimeview->selection = selection; + mimeview->mime_vbox = mime_vbox; + mimeview->popupmenu = popupmenu; + mimeview->popupfactory = popupfactory; + mimeview->reply_separator = reply_separator; + mimeview->reply_menuitem = reply_menuitem; + mimeview->type = -1; + + return mimeview; +} + +void mimeview_init(MimeView *mimeview) +{ + textview_init(mimeview->textview); + imageview_init(mimeview->imageview); +} + +/* + * Check whether the message is OpenPGP signed + */ +#if USE_GPGME +static gboolean mimeview_is_signed(MimeView *mimeview) +{ + MimeInfo *partinfo; + + debug_print("mimeview_is signed of %p\n", mimeview); + + if (!mimeview) return FALSE; + if (!mimeview->opened) return FALSE; + if (!rfc2015_is_available()) return FALSE; + + debug_print("mimeview_is_signed: open\n" ); + + if (!mimeview->messageview->file) return FALSE; + + debug_print("mimeview_is_signed: file\n" ); + + partinfo = mimeview_get_selected_part(mimeview); + g_return_val_if_fail(partinfo != NULL, FALSE); + + /* walk the tree and see whether there is a signature somewhere */ + do { + if (rfc2015_has_signature(partinfo)) + return TRUE; + } while ((partinfo = partinfo->parent) != NULL); + + debug_print("mimeview_is_signed: FALSE\n" ); + + return FALSE; +} + +static void set_unchecked_signature(MimeInfo *mimeinfo) +{ + MimeInfo **signedinfo; + + if (!rfc2015_is_available()) return; + + signedinfo = rfc2015_find_signature(mimeinfo); + if (signedinfo == NULL) return; + + g_free(signedinfo[1]->sigstatus); + signedinfo[1]->sigstatus = + g_strdup(_("Select \"Check signature\" to check")); + + g_free(signedinfo[1]->sigstatus_full); + signedinfo[1]->sigstatus_full = NULL; + + g_free(signedinfo); +} +#endif /* USE_GPGME */ + +void mimeview_show_message(MimeView *mimeview, MimeInfo *mimeinfo, + const gchar *file) +{ + GtkTreeModel *model = GTK_TREE_MODEL(mimeview->store); + GtkTreeIter iter; + gboolean valid; + + mimeview_clear(mimeview); + textview_clear(mimeview->messageview->textview); + + g_return_if_fail(file != NULL); + g_return_if_fail(mimeinfo != NULL); + +#if USE_GPGME + if (rfc2015_is_available() && prefs_common.auto_check_signatures) { + FILE *fp; + + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + return; + } + rfc2015_check_signature(mimeinfo, fp); + fclose(fp); + } else + set_unchecked_signature(mimeinfo); +#endif + + g_signal_handlers_block_by_func + (G_OBJECT(mimeview->selection), + G_CALLBACK(mimeview_selection_changed), mimeview); + + mimeview_set_multipart_tree(mimeview, mimeinfo, NULL); + gtk_tree_view_expand_all(GTK_TREE_VIEW(mimeview->treeview)); + + g_signal_handlers_unblock_by_func + (G_OBJECT(mimeview->selection), + G_CALLBACK(mimeview_selection_changed), mimeview); + + /* search first text part */ + for (valid = gtk_tree_model_get_iter_first(model, &iter); valid; + valid = gtkut_tree_model_next(model, &iter)) { + MimeInfo *partinfo; + + gtk_tree_model_get(model, &iter, COL_MIME_INFO, &partinfo, -1); + if (partinfo && + (partinfo->mime_type == MIME_TEXT || + partinfo->mime_type == MIME_TEXT_HTML)) + break; + } + textview_show_message(mimeview->messageview->textview, mimeinfo, file); + + if (!valid) + valid = gtk_tree_model_get_iter_first(model, &iter); + + if (valid) { + GtkTreePath *path; + + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(mimeview->treeview), + path, NULL, FALSE); + gtk_tree_path_free(path); + if (mimeview_get_selected_part(mimeview)) + gtk_widget_grab_focus(mimeview->treeview); + } +} + +void mimeview_clear(MimeView *mimeview) +{ + mimeview->has_attach_file = FALSE; + + gtk_tree_store_clear(mimeview->store); + textview_clear(mimeview->textview); + imageview_clear(mimeview->imageview); + + gtk_tree_path_free(mimeview->opened); + mimeview->opened = NULL; + + g_free(mimeview->drag_file); + mimeview->drag_file = NULL; +} + +void mimeview_destroy(MimeView *mimeview) +{ + textview_destroy(mimeview->textview); + imageview_destroy(mimeview->imageview); + g_object_unref(mimeview->popupfactory); + g_free(mimeview->drag_file); + g_free(mimeview); +} + +MimeInfo *mimeview_get_selected_part(MimeView *mimeview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(mimeview->store); + GtkTreeIter iter; + MimeInfo *partinfo = NULL; + + if (!mimeview->opened) + return NULL; + if (gtk_notebook_get_current_page + (GTK_NOTEBOOK(mimeview->messageview->notebook)) == 0) + return NULL; + + if (gtk_tree_model_get_iter(model, &iter, mimeview->opened)) + gtk_tree_model_get(model, &iter, COL_MIME_INFO, &partinfo, -1); + + return partinfo; +} + +gboolean mimeview_step(MimeView *mimeview, GtkScrollType type) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(mimeview->treeview); + GtkTreeModel *model = GTK_TREE_MODEL(mimeview->store); + GtkTreeIter iter; + gboolean moved; + + if (!mimeview->opened) + return FALSE; + if (!gtk_tree_model_get_iter(model, &iter, mimeview->opened)) + return FALSE; + + if (type == GTK_SCROLL_STEP_FORWARD) { + if (gtkut_tree_model_next(model, &iter)) + gtkut_tree_view_expand_parent_all(treeview, &iter); + else + return FALSE; + } else { + if (!gtkut_tree_model_prev(model, &iter)) + return FALSE; + } + + g_signal_emit_by_name(G_OBJECT(treeview), "move-cursor", + GTK_MOVEMENT_DISPLAY_LINES, + type == GTK_SCROLL_STEP_FORWARD ? 1 : -1, &moved); + + return TRUE; +} + +static void mimeview_set_multipart_tree(MimeView *mimeview, + MimeInfo *mimeinfo, + GtkTreeIter *parent) +{ + GtkTreeIter iter; + + g_return_if_fail(mimeinfo != NULL); + + if (mimeinfo->children) + mimeinfo = mimeinfo->children; + + while (mimeinfo != NULL) { + mimeview_append_part(mimeview, mimeinfo, &iter, parent); + + if (mimeinfo->children) + mimeview_set_multipart_tree(mimeview, mimeinfo, &iter); + else if (mimeinfo->sub && + mimeinfo->sub->mime_type != MIME_TEXT && + mimeinfo->sub->mime_type != MIME_TEXT_HTML) + mimeview_set_multipart_tree(mimeview, mimeinfo->sub, + &iter); + mimeinfo = mimeinfo->next; + } +} + +static gchar *get_part_name(MimeInfo *partinfo) +{ +#if USE_GPGME + if (partinfo->sigstatus) + return partinfo->sigstatus; + else +#endif + if (partinfo->name) + return partinfo->name; + else if (partinfo->filename) + return partinfo->filename; + else + return ""; +} + +static gboolean mimeview_append_part(MimeView *mimeview, MimeInfo *partinfo, + GtkTreeIter *iter, GtkTreeIter *parent) +{ + gchar *mime_type; + gchar *size; + gchar *name; + + mime_type = partinfo->content_type ? partinfo->content_type : ""; + size = to_human_readable(partinfo->content_size); + name = get_part_name(partinfo); + if (name && *name != '\0') + mimeview->has_attach_file = TRUE; + + gtk_tree_store_append(mimeview->store, iter, parent); + gtk_tree_store_set(mimeview->store, iter, + COL_MIMETYPE, mime_type, + COL_SIZE, size, + COL_NAME, name, + COL_MIME_INFO, partinfo, + -1); + + return TRUE; +} + +static void mimeview_show_message_part(MimeView *mimeview, MimeInfo *partinfo) +{ + FILE *fp; + const gchar *fname; + + if (!partinfo) return; + + fname = mimeview->messageview->file; + if (!fname) return; + + if ((fp = g_fopen(fname, "rb")) == NULL) { + FILE_OP_ERROR(fname, "fopen"); + return; + } + + if (fseek(fp, partinfo->fpos, SEEK_SET) < 0) { + FILE_OP_ERROR(fname, "fseek"); + fclose(fp); + return; + } + + mimeview_change_view_type(mimeview, MIMEVIEW_TEXT); + textview_show_part(mimeview->textview, partinfo, fp); + + fclose(fp); +} + +static void mimeview_show_image_part(MimeView *mimeview, MimeInfo *partinfo) +{ + gchar *filename; + + if (!partinfo) return; + + filename = procmime_get_tmp_file_name(partinfo); + + if (procmime_get_part(filename, mimeview->messageview->file, partinfo) < 0) + alertpanel_error + (_("Can't get the part of multipart message.")); + else { + mimeview_change_view_type(mimeview, MIMEVIEW_IMAGE); + imageview_show_image(mimeview->imageview, partinfo, filename, + prefs_common.resize_image); + g_unlink(filename); + } + + g_free(filename); +} + +static void save_as_button_clicked(GtkWidget *widget, gpointer data) +{ + MimeView *mimeview = (MimeView *)data; + + mimeview_save_as(mimeview); +} + +static void display_as_text_button_clicked(GtkWidget *widget, gpointer data) +{ + MimeView *mimeview = (MimeView *)data; + + mimeview_display_as_text(mimeview); +} + +static void open_button_clicked(GtkWidget *widget, gpointer data) +{ + MimeView *mimeview = (MimeView *)data; + + mimeview_launch(mimeview); +} + +static void open_with_button_clicked(GtkWidget *widget, gpointer data) +{ + MimeView *mimeview = (MimeView *)data; + + mimeview_open_with(mimeview); +} + +static void mimeview_show_mime_part(MimeView *mimeview, MimeInfo *partinfo) +{ + TextView *textview = mimeview->textview; + GtkTextBuffer *buffer; + GtkTextIter iter; + GtkTextChildAnchor *anchor; + GtkWidget *vbbox; + GtkWidget *button; + gchar buf[BUFFSIZE]; + + if (!partinfo) return; + + textview_set_font(textview, NULL); + textview_clear(textview); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text)); + gtk_text_buffer_get_start_iter(buffer, &iter); + + gtk_text_buffer_insert(buffer, &iter, + _("Select an action for the attached file:\n"), + -1); + if (partinfo->filename || partinfo->name) + g_snprintf(buf, sizeof(buf), "[%s %s (%s)]\n\n", + partinfo->filename ? partinfo->filename : + partinfo->name, + partinfo->content_type, + to_human_readable(partinfo->content_size)); + else + g_snprintf(buf, sizeof(buf), "[%s (%s)]\n\n", + partinfo->content_type, + to_human_readable(partinfo->content_size)); + gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, buf, -1, + "mimepart", NULL); + + vbbox = gtk_vbutton_box_new(); + gtk_box_set_spacing(GTK_BOX(vbbox), 5); + + button = gtk_button_new_from_stock(GTK_STOCK_OPEN); + gtk_container_add(GTK_CONTAINER(vbbox), button); + g_signal_connect(button, "clicked", G_CALLBACK(open_button_clicked), + mimeview); + button = gtk_button_new_with_mnemonic(_("Open _with...")); + gtk_container_add(GTK_CONTAINER(vbbox), button); + g_signal_connect(button, "clicked", + G_CALLBACK(open_with_button_clicked), mimeview); + button = gtk_button_new_with_mnemonic(_("_Display as text")); + gtk_container_add(GTK_CONTAINER(vbbox), button); + g_signal_connect(button, "clicked", + G_CALLBACK(display_as_text_button_clicked), mimeview); + button = gtk_button_new_with_mnemonic(_("_Save as...")); + gtk_container_add(GTK_CONTAINER(vbbox), button); + g_signal_connect(button, "clicked", G_CALLBACK(save_as_button_clicked), + mimeview); + + gtk_widget_show_all(vbbox); + + anchor = gtk_text_buffer_create_child_anchor(buffer, &iter); + gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(textview->text), + vbbox, anchor); +} + +#if USE_GPGME +static void check_signature_button_clicked(GtkWidget *widget, gpointer data) +{ + MimeView *mimeview = (MimeView *)data; + + mimeview_check_signature(mimeview); +} + +static void mimeview_show_signature_part(MimeView *mimeview, + MimeInfo *partinfo) +{ + TextView *textview = mimeview->textview; + GtkTextBuffer *buffer; + GtkTextIter iter; + GtkTextChildAnchor *anchor; + GtkWidget *vbbox; + GtkWidget *button; + + if (!partinfo) return; + + textview_set_font(textview, NULL); + textview_clear(textview); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text)); + gtk_text_buffer_get_start_iter(buffer, &iter); + + if (partinfo->sigstatus_full) { + gtk_text_buffer_insert + (buffer, &iter, partinfo->sigstatus_full, -1); + return; + } + + gtk_text_buffer_insert + (buffer, &iter, + _("This signature has not been checked yet.\n\n"), -1); + + vbbox = gtk_vbutton_box_new(); + gtk_box_set_spacing(GTK_BOX(vbbox), 5); + + if (rfc2015_is_available()) { + button = gtk_button_new_with_mnemonic(_("_Check signature")); + gtk_container_add(GTK_CONTAINER(vbbox), button); + g_signal_connect(button, "clicked", + G_CALLBACK(check_signature_button_clicked), + mimeview); + } + + gtk_widget_show_all(vbbox); + + anchor = gtk_text_buffer_create_child_anchor(buffer, &iter); + gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(textview->text), + vbbox, anchor); +} +#endif /* USE_GPGME */ + +static void mimeview_change_view_type(MimeView *mimeview, MimeViewType type) +{ + TextView *textview = mimeview->textview; + ImageView *imageview = mimeview->imageview; + GList *children; + + if (mimeview->type == type) return; + + children = gtk_container_get_children + (GTK_CONTAINER(mimeview->mime_vbox)); + if (children) { + gtkut_container_remove(GTK_CONTAINER(mimeview->mime_vbox), + GTK_WIDGET(children->data)); + g_list_free(children); + } + + switch (mimeview->type) { + case MIMEVIEW_IMAGE: + imageview_clear(mimeview->imageview); + break; + case MIMEVIEW_TEXT: + textview_clear(mimeview->textview); + break; + default: + break; + } + + switch (type) { + case MIMEVIEW_IMAGE: + gtk_container_add(GTK_CONTAINER(mimeview->mime_vbox), + GTK_WIDGET_PTR(imageview)); + break; + case MIMEVIEW_TEXT: + gtk_container_add(GTK_CONTAINER(mimeview->mime_vbox), + GTK_WIDGET_PTR(textview)); + break; + default: + return; + } + + mimeview->type = type; +} + +static void mimeview_selection_changed(GtkTreeSelection *selection, + MimeView *mimeview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(mimeview->store); + GtkTreeIter iter; + GtkTreePath *path; + MimeInfo *partinfo; + + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) { + if (mimeview->opened) { + gtk_tree_path_free(mimeview->opened); + mimeview->opened = NULL; + } + return; + } + + path = gtk_tree_model_get_path(model, &iter); + + if (mimeview->opened && + gtk_tree_path_compare(mimeview->opened, path) == 0) { + gtk_tree_path_free(path); + return; + } + + gtk_tree_path_free(mimeview->opened); + mimeview->opened = path; + path = NULL; + + gtk_tree_model_get(model, &iter, COL_MIME_INFO, &partinfo, -1); + if (!partinfo) + return; + + switch (partinfo->mime_type) { + case MIME_TEXT: + case MIME_TEXT_HTML: + case MIME_MESSAGE_RFC822: + case MIME_MULTIPART: + mimeview_show_message_part(mimeview, partinfo); + break; + case MIME_IMAGE: + if (prefs_common.inline_image) { + mimeview_show_image_part(mimeview, partinfo); + break; + } + /* FALLTHROUGH */ + default: + mimeview_change_view_type(mimeview, MIMEVIEW_TEXT); +#if USE_GPGME + if (rfc2015_is_signature_part(partinfo)) + mimeview_show_signature_part(mimeview, partinfo); + else +#endif + mimeview_show_mime_part(mimeview, partinfo); + break; + } +} + +static gint mimeview_button_pressed(GtkWidget *widget, GdkEventButton *event, + MimeView *mimeview) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(widget); + MimeInfo *partinfo; + + if (!event) return FALSE; + + if (event->button == 2 || event->button == 3) { + GtkTreePath *path; + + if (!gtk_tree_view_get_path_at_pos(treeview, event->x, event->y, + &path, NULL, NULL, NULL)) + return FALSE; + gtk_tree_view_set_cursor(treeview, path, NULL, FALSE); + gtk_tree_path_free(path); + } + + if (event->button == 2 || + (event->button == 1 && event->type == GDK_2BUTTON_PRESS)) { + /* call external program for image, audio or html */ + mimeview_launch(mimeview); + } else if (event->button == 3) { + partinfo = mimeview_get_selected_part(mimeview); + if (partinfo && (partinfo->mime_type == MIME_TEXT || + partinfo->mime_type == MIME_TEXT_HTML || + partinfo->mime_type == MIME_MESSAGE_RFC822 || + partinfo->mime_type == MIME_IMAGE || + partinfo->mime_type == MIME_MULTIPART)) + menu_set_sensitive(mimeview->popupfactory, + "/Display as text", FALSE); + else + menu_set_sensitive(mimeview->popupfactory, + "/Display as text", TRUE); + if (partinfo && + partinfo->mime_type == MIME_APPLICATION_OCTET_STREAM) + menu_set_sensitive(mimeview->popupfactory, + "/Open", FALSE); + else + menu_set_sensitive(mimeview->popupfactory, + "/Open", TRUE); + + menu_set_sensitive(mimeview->popupfactory, + "/Save all...", mimeview->has_attach_file); + + if (partinfo && (partinfo->mime_type == MIME_TEXT || + partinfo->mime_type == MIME_TEXT_HTML || + partinfo->mime_type == MIME_MESSAGE_RFC822)) + menu_set_sensitive(mimeview->popupfactory, + "/Print...", TRUE); + else + menu_set_sensitive(mimeview->popupfactory, + "/Print...", FALSE); + + if (partinfo && partinfo->mime_type == MIME_MESSAGE_RFC822) { + gtk_widget_show(mimeview->reply_separator); + gtk_widget_show(mimeview->reply_menuitem); + } else { + gtk_widget_hide(mimeview->reply_separator); + gtk_widget_hide(mimeview->reply_menuitem); + } +#if USE_GPGME + menu_set_sensitive(mimeview->popupfactory, + "/Check signature", + mimeview_is_signed(mimeview)); +#endif + + gtk_menu_popup(GTK_MENU(mimeview->popupmenu), + NULL, NULL, NULL, NULL, + event->button, event->time); + return TRUE; + } + + return FALSE; +} + +void mimeview_pass_key_press_event(MimeView *mimeview, GdkEventKey *event) +{ + mimeview_key_pressed(mimeview->treeview, event, mimeview); +} + +#define BREAK_ON_MODIFIER_KEY() \ + if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break + +#define KEY_PRESS_EVENT_STOP() \ + g_signal_stop_emission_by_name(G_OBJECT(treeview), "key_press_event"); + +static gint mimeview_key_pressed(GtkWidget *widget, GdkEventKey *event, + MimeView *mimeview) +{ + SummaryView *summaryview = NULL; + GtkTreeView *treeview = GTK_TREE_VIEW(widget); + GtkTreeModel *model = GTK_TREE_MODEL(mimeview->store); + GtkTreeIter iter; + gboolean mod_pressed; + + if (!event) return FALSE; + if (!mimeview->opened) return FALSE; + if (!gtk_tree_model_get_iter(model, &iter, mimeview->opened)) + return FALSE; + + if (mimeview->messageview->mainwin) + summaryview = mimeview->messageview->mainwin->summaryview; + mod_pressed = + ((event->state & (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0); + + switch (event->keyval) { + case GDK_space: + case GDK_KP_Space: + if (textview_scroll_page(mimeview->textview, mod_pressed)) + return TRUE; + + if (gtkut_tree_model_next(model, &iter)) { + GtkTreePath *path; + + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_view_set_cursor(treeview, path, NULL, FALSE); + gtk_tree_path_free(path); + return TRUE; + } + if (summaryview) + summary_pass_key_press_event(summaryview, event); + break; + case GDK_BackSpace: + textview_scroll_page(mimeview->textview, TRUE); + return TRUE; + case GDK_Return: + case GDK_KP_Enter: + textview_scroll_one_line(mimeview->textview, mod_pressed); + return TRUE; + case GDK_t: + BREAK_ON_MODIFIER_KEY(); + KEY_PRESS_EVENT_STOP(); + mimeview_display_as_text(mimeview); + return TRUE; + case GDK_Escape: + if (summaryview) + gtk_widget_grab_focus(summaryview->treeview); + break; + case GDK_Left: + case GDK_Delete: + case GDK_KP_Left: + case GDK_KP_Delete: + if (summaryview) + summary_pass_key_press_event(summaryview, event); + break; + default: + break; + } + + return FALSE; +} + +static void mimeview_drag_begin(GtkWidget *widget, GdkDragContext *drag_context, + MimeView *mimeview) +{ + gchar *filename; + gchar *bname = NULL; + MimeInfo *partinfo; + + if (!mimeview->opened) return; + if (!mimeview->messageview->file) return; + + partinfo = mimeview_get_selected_part(mimeview); + if (!partinfo) return; + + filename = partinfo->filename ? partinfo->filename : partinfo->name; + if (filename) { + const gchar *bname_; + + bname_ = g_basename(filename); + bname = conv_filename_from_utf8(bname_); + subst_for_filename(bname); + } + if (!bname || *bname == '\0') + filename = procmime_get_tmp_file_name_for_user(partinfo); + else + filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S, + bname, NULL); + + if (procmime_get_part(filename, mimeview->messageview->file, partinfo) < 0) { + g_warning(_("Can't save the part of multipart message.")); + } else + mimeview->drag_file = encode_uri(filename); + + g_free(filename); + + gtk_drag_set_icon_default(drag_context); +} + +static void mimeview_drag_end(GtkWidget *widget, GdkDragContext *drag_context, + MimeView *mimeview) +{ + if (mimeview->drag_file) { + g_free(mimeview->drag_file); + mimeview->drag_file = NULL; + } +} + +static void mimeview_drag_data_get(GtkWidget *widget, + GdkDragContext *drag_context, + GtkSelectionData *selection_data, + guint info, + guint time, + MimeView *mimeview) +{ + if (!mimeview->drag_file) return; + + gtk_selection_data_set(selection_data, selection_data->target, 8, + (guchar *)mimeview->drag_file, + strlen(mimeview->drag_file)); +} + +static void mimeview_display_as_text(MimeView *mimeview) +{ + MimeInfo *partinfo; + + if (!mimeview->opened) return; + + partinfo = mimeview_get_selected_part(mimeview); + g_return_if_fail(partinfo != NULL); + mimeview_show_message_part(mimeview, partinfo); +} + +void mimeview_save_as(MimeView *mimeview) +{ + MimeInfo *partinfo; + + if (!mimeview->opened) return; + if (!mimeview->messageview->file) return; + + partinfo = mimeview_get_selected_part(mimeview); + g_return_if_fail(partinfo != NULL); + + mimeview_save_part_as(mimeview, partinfo); +} + +void mimeview_save_all(MimeView *mimeview) +{ + gchar *dir; + + dir = filesel_select_dir(NULL); + if (!dir) return; + + if (procmime_get_all_parts(dir, mimeview->messageview->file, mimeview->messageview->mimeinfo) < 0) + alertpanel_error(_("Can't save the attachments.")); + + g_free(dir); +} + +void mimeview_print(MimeView *mimeview) +{ + MimeInfo *partinfo; + + if (!mimeview->opened) return; + if (!mimeview->messageview->file) return; + + partinfo = mimeview_get_selected_part(mimeview); + g_return_if_fail(partinfo != NULL); + + mimeview_print_part(mimeview, partinfo); +} + +void mimeview_print_part(MimeView *mimeview, MimeInfo *partinfo) +{ + g_return_if_fail(partinfo != NULL); + + if (!mimeview->messageview->file) return; + + if (partinfo->mime_type == MIME_MESSAGE_RFC822) { + gchar *filename; + MsgInfo *msginfo; + MsgFlags flags = {0, 0}; + + filename = procmime_get_tmp_file_name(partinfo); + if (procmime_get_part(filename, mimeview->messageview->file, partinfo) < 0) { + alertpanel_error + (_("Can't save the part of multipart message.")); + g_free(filename); + return; + } + + msginfo = procheader_parse_file(filename, flags, TRUE); + msginfo->file_path = filename; + filename = NULL; + printing_print_message + (msginfo, mimeview->textview->show_all_headers); + procmsg_msginfo_free(msginfo); + } else if (partinfo->mime_type == MIME_TEXT || + partinfo->mime_type == MIME_TEXT_HTML) { + printing_print_message_part(mimeview->messageview->msginfo, + partinfo); + } +} + +void mimeview_launch_part(MimeView *mimeview, MimeInfo *partinfo) +{ + gchar *filename; + + g_return_if_fail(partinfo != NULL); + + if (!mimeview->messageview->file) return; + + filename = procmime_get_tmp_file_name_for_user(partinfo); + + if (procmime_get_part(filename, mimeview->messageview->file, partinfo) < 0) + alertpanel_error + (_("Can't save the part of multipart message.")); + else + mimeview_view_file(filename, partinfo, NULL); + + g_free(filename); +} + +void mimeview_open_part_with(MimeView *mimeview, MimeInfo *partinfo) +{ + gchar *filename; + gchar *cmd; + + g_return_if_fail(partinfo != NULL); + + if (!mimeview->messageview->file) return; + + filename = procmime_get_tmp_file_name_for_user(partinfo); + + if (procmime_get_part(filename, mimeview->messageview->file, partinfo) < 0) { + alertpanel_error + (_("Can't save the part of multipart message.")); + g_free(filename); + return; + } + + if (!prefs_common.mime_open_cmd_history) + prefs_common.mime_open_cmd_history = + add_history(NULL, prefs_common.mime_open_cmd); + + cmd = input_dialog_combo + (_("Open with"), + _("Enter the command line to open file:\n" + "(`%s' will be replaced with file name)"), + prefs_common.mime_open_cmd, + prefs_common.mime_open_cmd_history, + TRUE); + if (cmd) { + mimeview_view_file(filename, partinfo, cmd); + g_free(prefs_common.mime_open_cmd); + prefs_common.mime_open_cmd = cmd; + prefs_common.mime_open_cmd_history = + add_history(prefs_common.mime_open_cmd_history, cmd); + } + + g_free(filename); +} + +void mimeview_save_part_as(MimeView *mimeview, MimeInfo *partinfo) +{ + gchar *filename = NULL; + + g_return_if_fail(partinfo != NULL); + + if (!mimeview->messageview->file) return; + + if (partinfo->filename) { + filename = filesel_save_as(partinfo->filename); + } else if (partinfo->name) { + gchar *defname; + + defname = g_strdup(partinfo->name); + subst_for_filename(defname); + filename = filesel_save_as(defname); + g_free(defname); + } else + filename = filesel_save_as(NULL); + + if (!filename) + return; + + if (procmime_get_part(filename, mimeview->messageview->file, partinfo) < 0) + alertpanel_error + (_("Can't save the part of multipart message.")); + + g_free(filename); +} + +static void mimeview_launch(MimeView *mimeview) +{ + MimeInfo *partinfo; + + if (!mimeview->opened) return; + if (!mimeview->messageview->file) return; + + partinfo = mimeview_get_selected_part(mimeview); + g_return_if_fail(partinfo != NULL); + + mimeview_launch_part(mimeview, partinfo); +} + +static void mimeview_open_with(MimeView *mimeview) +{ + MimeInfo *partinfo; + + if (!mimeview->opened) return; + if (!mimeview->messageview->file) return; + + partinfo = mimeview_get_selected_part(mimeview); + g_return_if_fail(partinfo != NULL); + + mimeview_open_part_with(mimeview, partinfo); +} + +static void mimeview_view_file(const gchar *filename, MimeInfo *partinfo, + const gchar *cmdline) +{ + const gchar *cmd = NULL; + gchar buf[BUFFSIZE]; + + if (!cmdline) { +#ifdef G_OS_WIN32 + DWORD dwtype; + + if (g_file_test(filename, G_FILE_TEST_IS_EXECUTABLE) || + str_has_suffix_case(filename, ".scr") || + str_has_suffix_case(filename, ".pif") || + GetBinaryType(filename, &dwtype)) { + alertpanel_full + (_("Opening executable file"), + _("This is an executable file. Opening executable file is restricted for security.\n" + "If you want to launch it, save it to somewhere and make sure it is not an virus or something like a malicious program."), + ALERT_WARNING, G_ALERTDEFAULT, FALSE, + GTK_STOCK_OK, NULL, NULL); + return; + } + execute_open_file(filename, partinfo->content_type); + return; +#elif defined(__APPLE__) + if (g_file_test(filename, G_FILE_TEST_IS_EXECUTABLE) || + str_has_suffix_case(filename, ".py") || + str_has_suffix_case(filename, ".rb") || + str_has_suffix_case(filename, ".sh")) { + alertpanel_full + (_("Opening executable file"), + _("This is an executable file. Opening executable file is restricted for security.\n" + "If you want to launch it, save it to somewhere and make sure it is not an virus or something like a malicious program."), + ALERT_WARNING, G_ALERTDEFAULT, FALSE, + GTK_STOCK_OK, NULL, NULL); + return; + } + execute_open_file(filename, partinfo->content_type); + return; +#else + if (MIME_IMAGE == partinfo->mime_type) + cmd = prefs_common.mime_image_viewer; + else if (MIME_AUDIO == partinfo->mime_type) + cmd = prefs_common.mime_audio_player; + else if (MIME_TEXT_HTML == partinfo->mime_type) + cmd = prefs_common.uri_cmd; + if (!cmd) { + if (prefs_common.mime_cmd) { + if (str_find_format_times + (prefs_common.mime_cmd, 's') == 2) { + g_snprintf(buf, sizeof(buf), + prefs_common.mime_cmd, + partinfo->content_type, + "%s"); + cmd = buf; + } else + cmd = prefs_common.mime_cmd; + } else { + procmime_execute_open_file + (filename, partinfo->content_type); + return; + } + } +#endif + } else + cmd = cmdline; + + if (cmd && str_find_format_times(cmd, 's') == 1) { + gchar *cmdbuf; + cmdbuf = g_strdup_printf(cmd, filename); + execute_command_line(cmdbuf, TRUE); + g_free(cmdbuf); + } else if (cmd) + g_warning("MIME viewer command line is invalid: '%s'", cmd); +} + +static void mimeview_reply(MimeView *mimeview, guint action) +{ + MimeInfo *partinfo; + gchar *filename; + MsgInfo *msginfo; + MsgFlags flags = {0, 0}; + ComposeMode mode = action; + + if (!mimeview->opened) return; + if (!mimeview->messageview->file) return; + + partinfo = mimeview_get_selected_part(mimeview); + g_return_if_fail(partinfo != NULL); + + if (partinfo->mime_type != MIME_MESSAGE_RFC822) + return; + + filename = procmime_get_tmp_file_name(partinfo); + if (procmime_get_part(filename, mimeview->messageview->file, partinfo) < 0) { + alertpanel_error + (_("Can't save the part of multipart message.")); + g_free(filename); + return; + } + + msginfo = procheader_parse_file(filename, flags, TRUE); + msginfo->file_path = filename; + filename = NULL; + if (prefs_common.reply_with_quote) + mode |= COMPOSE_WITH_QUOTE; + + if (mimeview->messageview->mainwin) + compose_reply(msginfo, mimeview->messageview->mainwin->summaryview->folder_item, + mode, NULL); + else + compose_reply(msginfo, NULL, mode, NULL); + + g_unlink(msginfo->file_path); + procmsg_msginfo_free(msginfo); +} + +#if USE_GPGME +static gboolean update_node_name_func(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + MimeInfo *partinfo; + gchar *part_name; + + gtk_tree_model_get(model, iter, COL_MIME_INFO, &partinfo, -1); + g_return_val_if_fail(partinfo != NULL, FALSE); + + part_name = get_part_name(partinfo); + gtk_tree_store_set(GTK_TREE_STORE(model), iter, COL_NAME, part_name, + -1); + + return FALSE; +} + +static void mimeview_update_names(MimeView *mimeview) +{ + gtk_tree_model_foreach(GTK_TREE_MODEL(mimeview->store), + update_node_name_func, NULL); +} + +static void mimeview_update_signature_info(MimeView *mimeview) +{ + MimeInfo *partinfo; + + if (!mimeview) return; + if (!mimeview->opened) return; + + partinfo = mimeview_get_selected_part(mimeview); + if (!partinfo) return; + + if (rfc2015_is_signature_part(partinfo)) { + mimeview_change_view_type(mimeview, MIMEVIEW_TEXT); + mimeview_show_signature_part(mimeview, partinfo); + } +} + +static void mimeview_check_signature(MimeView *mimeview) +{ + MimeInfo *mimeinfo; + FILE *fp; + + g_return_if_fail (mimeview_is_signed(mimeview)); + + if (!rfc2015_is_available()) + return; + + mimeinfo = mimeview_get_selected_part(mimeview); + g_return_if_fail(mimeinfo != NULL); + g_return_if_fail(mimeview->messageview->file != NULL); + + while (mimeinfo->parent) + mimeinfo = mimeinfo->parent; + + if ((fp = g_fopen(mimeview->messageview->file, "rb")) == NULL) { + FILE_OP_ERROR(mimeview->messageview->file, "fopen"); + return; + } + + rfc2015_check_signature(mimeinfo, fp); + fclose(fp); + + mimeview_update_names(mimeview); + mimeview_update_signature_info(mimeview); + + textview_show_message(mimeview->messageview->textview, mimeinfo, + mimeview->messageview->file); +} +#endif /* USE_GPGME */ diff --git a/src/mimeview.h b/src/mimeview.h new file mode 100644 index 0000000..3d5ff20 --- /dev/null +++ b/src/mimeview.h @@ -0,0 +1,109 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2013 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __MIMEVIEW_H__ +#define __MIMEVIEW_H__ + +#include +#include +#include +#include +#include +#include + +typedef struct _MimeView MimeView; + +#include "textview.h" +#include "imageview.h" +#include "messageview.h" +#include "procmime.h" + +typedef enum +{ + MIMEVIEW_TEXT, + MIMEVIEW_IMAGE +} MimeViewType; + +struct _MimeView +{ + GtkWidget *paned; + + GtkWidget *scrolledwin; + GtkWidget *treeview; + + GtkTreeStore *store; + GtkTreeSelection *selection; + + GtkWidget *mime_vbox; + + MimeViewType type; + + GtkWidget *popupmenu; + GtkItemFactory *popupfactory; + GtkWidget *reply_separator; + GtkWidget *reply_menuitem; + + GtkTreePath *opened; + + TextView *textview; + ImageView *imageview; + + MessageView *messageview; + + /* deprecated: use MessageView */ + MimeInfo *mimeinfo__; + gchar *file__; + + gchar *drag_file; + + gboolean has_attach_file; +}; + +MimeView *mimeview_create (void); +void mimeview_init (MimeView *mimeview); +void mimeview_show_message (MimeView *mimeview, + MimeInfo *mimeinfo, + const gchar *file); +void mimeview_clear (MimeView *mimeview); +void mimeview_destroy (MimeView *mimeview); + +MimeInfo *mimeview_get_selected_part (MimeView *mimeview); + +gboolean mimeview_step (MimeView *mimeview, + GtkScrollType type); + +void mimeview_pass_key_press_event (MimeView *mimeview, + GdkEventKey *event); + +void mimeview_save_as (MimeView *mimeview); +void mimeview_save_all (MimeView *mimeview); + +void mimeview_print (MimeView *mimeview); + +void mimeview_launch_part (MimeView *mimeview, + MimeInfo *partinfo); +void mimeview_open_part_with (MimeView *mimeview, + MimeInfo *partinfo); +void mimeview_save_part_as (MimeView *mimeview, + MimeInfo *partinfo); + +void mimeview_print_part (MimeView *mimeview, + MimeInfo *partinfo); + +#endif /* __MIMEVIEW_H__ */ diff --git a/src/notificationwindow.c b/src/notificationwindow.c new file mode 100644 index 0000000..966c708 --- /dev/null +++ b/src/notificationwindow.c @@ -0,0 +1,347 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#ifdef G_OS_WIN32 +# include +#endif + +#include "notificationwindow.h" +#include "mainwindow.h" +#include "utils.h" +#include "gtkutils.h" + +#define NOTIFICATIONWINDOW_NOTIFY_PERIOD 10000 +#define NOTIFICATIONWINDOW_WIDTH 380 +#define NOTIFICATIONWINDOW_HEIGHT 80 +#define FADE_REFRESH_RATE 50 /* ms */ +#define FADE_SPEED 5 /* pixels */ +#define FADE_IN_LENGTH 10 /* counts */ + +typedef struct _NotificationWindow +{ + GtkWidget *window; + + GtkWidget *msglabel; + GtkWidget *sublabel; + + guint notify_tag; + + gint x; + gint y; + gint width; + gint height; + gint fade_length; + gint fade_count; + gint notify_event_count; + guint timeout; +} NotificationWindow; + +static NotificationWindow notify_window; + +static void notification_window_destroy(void); + +#if GTK_CHECK_VERSION(2, 12, 0) +static gboolean notify_fadein_timeout_cb(gpointer data); +#endif + +static gboolean notify_timeout_cb (gpointer data); + +static gboolean nwin_button_pressed (GtkWidget *widget, + GdkEventButton *event, + gpointer data); +static gboolean nwin_entered (GtkWidget *widget, + GdkEventCrossing *event, + gpointer data); +static gboolean nwin_motion_notify (GtkWidget *widget, + GdkEventMotion *event, + gpointer data); + +static void nwin_destroy_cb (GtkWidget *widget, + gpointer data); + + +static void get_work_area(GdkRectangle *rect) +{ +#ifdef G_OS_WIN32 + RECT rc; + + SystemParametersInfoW(SPI_GETWORKAREA, 0, &rc, 0); + rect->x = rc.left; + rect->y = rc.top; + rect->width = rc.right - rc.left; + rect->height = rc.bottom - rc.top; +#else + rect->x = 0; + rect->y = 0; + rect->width = gdk_screen_width(); + rect->height = gdk_screen_height(); +#endif +} + +gint notification_window_open(const gchar *message, const gchar *submessage, + guint timeout) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *msglabel; + GtkWidget *sublabel; + GdkRectangle rect; + gint x, y; + GtkRequisition requisition; + gint window_width; + gint window_height; + + window_width = NOTIFICATIONWINDOW_WIDTH * gtkut_get_dpi_multiplier(); + window_height = NOTIFICATIONWINDOW_HEIGHT * gtkut_get_dpi_multiplier(); + + if (notify_window.window) { + notification_window_destroy(); + } + + window = gtk_window_new(GTK_WINDOW_POPUP); + gtk_window_set_title(GTK_WINDOW(window), _("Notification")); + gtk_window_set_wmclass(GTK_WINDOW(window), "notification", "Sylpheed"); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + gtk_widget_set_events(window, GDK_EXPOSURE_MASK|GDK_BUTTON_MOTION_MASK|GDK_POINTER_MOTION_MASK|GDK_POINTER_MOTION_HINT_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|GDK_ENTER_NOTIFY_MASK|GDK_LEAVE_NOTIFY_MASK); + gtk_window_set_skip_taskbar_hint(GTK_WINDOW(window), TRUE); + gtk_window_set_gravity(GTK_WINDOW(window), GDK_GRAVITY_SOUTH_EAST); + gtk_widget_set_size_request(window, window_width, -1); +#if GTK_CHECK_VERSION(2, 12, 0) + gtk_window_set_opacity(GTK_WINDOW(window), 0.0); +#endif + gtk_widget_realize(window); + gdk_window_set_type_hint(window->window, GDK_WINDOW_TYPE_HINT_NOTIFICATION); + + /* move window bottom-right */ + get_work_area(&rect); + x = rect.x + rect.width - window_width - 2; + if (x < 0) x = 0; + y = rect.y + rect.height - window_height - 2; + if (y < 0) y = 0; + gtk_window_move(GTK_WINDOW(window), x, y); + + g_signal_connect(G_OBJECT(window), "destroy", + G_CALLBACK(nwin_destroy_cb), NULL); + g_signal_connect(G_OBJECT(window), "button_press_event", + G_CALLBACK(nwin_button_pressed), NULL); + g_signal_connect(G_OBJECT(window), "enter_notify_event", + G_CALLBACK(nwin_entered), NULL); + g_signal_connect(G_OBJECT(window), "motion_notify_event", + G_CALLBACK(nwin_motion_notify), NULL); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add(GTK_CONTAINER(window), vbox); + + msglabel = gtk_label_new(message); + gtk_box_pack_start(GTK_BOX(vbox), msglabel, FALSE, FALSE, 0); + + sublabel = gtk_label_new(""); + gtk_label_set_ellipsize(GTK_LABEL(sublabel), PANGO_ELLIPSIZE_END); + gtk_label_set_markup(GTK_LABEL(sublabel), submessage); + gtk_box_pack_start(GTK_BOX(vbox), sublabel, FALSE, FALSE, 0); + gtk_label_set_justify(GTK_LABEL(sublabel), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(sublabel), 0.0, 0.5); + + gtk_widget_show_all(window); + + /* adjust window size and position */ + gtk_widget_get_child_requisition(window, &requisition); + notify_window.width = window_width; + notify_window.height = MAX(requisition.height, window_height); + gtk_widget_set_size_request(window, window_width, notify_window.height); + y = rect.y + rect.height - notify_window.height - 2; + if (y < 0) y = 0; + gtk_window_move(GTK_WINDOW(window), x, y); + +#if GTK_CHECK_VERSION(2, 12, 0) + notify_window.notify_tag = g_timeout_add(FADE_REFRESH_RATE, + notify_fadein_timeout_cb, + NULL); +#else + if (timeout > 0) { + notify_window.notify_tag = g_timeout_add(timeout * 1000, + notify_timeout_cb, NULL); + } +#endif + + debug_print("notification window created\n"); + + notify_window.window = window; + notify_window.msglabel = msglabel; + notify_window.sublabel = sublabel; + notify_window.x = x; + notify_window.y = y; + notify_window.fade_length = FADE_IN_LENGTH; + notify_window.fade_count = 0; + notify_window.notify_event_count = 0; + notify_window.timeout = timeout; + + return 0; +} + +void notification_window_set_message(const gchar *message, + const gchar *submessage) +{ + if (notify_window.window) { + gtk_label_set_text(GTK_LABEL(notify_window.msglabel), message); + gtk_label_set_markup(GTK_LABEL(notify_window.sublabel), submessage); + } +} + +void notification_window_close(void) +{ + notification_window_destroy(); +} + +static void notification_window_destroy(void) +{ + if (notify_window.window) { + if (notify_window.notify_tag > 0) { + g_source_remove(notify_window.notify_tag); + notify_window.notify_tag = 0; + } + + gtk_widget_destroy(notify_window.window); + + notify_window.window = NULL; + notify_window.msglabel = NULL; + notify_window.sublabel = NULL; + + debug_print("notification window removed\n"); + } +} + +#if GTK_CHECK_VERSION(2, 12, 0) +static gboolean notify_fadein_timeout_cb(gpointer data) +{ + gdk_threads_enter(); + notify_window.fade_length--; + notify_window.fade_count++; + + gtk_window_set_opacity(GTK_WINDOW(notify_window.window), + (gdouble)notify_window.fade_count / FADE_IN_LENGTH); + + if (notify_window.fade_length <= 0) { + notify_window.fade_length = 0; + notify_window.fade_count = 0; + if (notify_window.timeout > 0) { + notify_window.notify_tag = + g_timeout_add(notify_window.timeout * 1000, + notify_timeout_cb, NULL); + } + gdk_threads_leave(); + return FALSE; + } + gdk_threads_leave(); + return TRUE; +} +#endif + +static gboolean notify_fadeout_timeout_cb(gpointer data) +{ + gdk_threads_enter(); + notify_window.fade_length -= FADE_SPEED; + notify_window.fade_count++; + + gtk_window_move(GTK_WINDOW(notify_window.window), + notify_window.x, notify_window.y + notify_window.fade_count * FADE_SPEED); +#if GTK_CHECK_VERSION(2, 12, 0) + gtk_window_set_opacity(GTK_WINDOW(notify_window.window), + (gdouble)notify_window.fade_length / (gdk_screen_height() - notify_window.y)); +#endif + + if (notify_window.fade_length <= 0) { + notification_window_destroy(); + gdk_threads_leave(); + return FALSE; + } + gdk_threads_leave(); + return TRUE; +} + +static gboolean notify_timeout_cb(gpointer data) +{ + gdk_threads_enter(); + notify_window.fade_length = gdk_screen_height() - notify_window.y; + notify_window.notify_tag = g_timeout_add(FADE_REFRESH_RATE, + notify_fadeout_timeout_cb, + NULL); + gdk_threads_leave(); + + return FALSE; +} + +static gboolean nwin_button_pressed(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + if (!event) + return FALSE; + + notification_window_destroy(); + main_window_popup(main_window_get()); + + return TRUE; +} + +static gboolean nwin_entered(GtkWidget *widget, GdkEventCrossing *event, + gpointer data) +{ + return FALSE; +} + +static gboolean nwin_motion_notify(GtkWidget *widget, GdkEventMotion *event, + gpointer data) +{ + if (notify_window.fade_count > 0 && + notify_window.notify_event_count == 0) { + notify_window.notify_event_count++; + return FALSE; + } + + if (notify_window.notify_tag > 0) { + g_source_remove(notify_window.notify_tag); + notify_window.notify_tag = 0; + } + notify_window.fade_count = 0; + notify_window.notify_event_count = 0; +#if GTK_CHECK_VERSION(2, 12, 0) + gtk_window_set_opacity(GTK_WINDOW(notify_window.window), 1.0); +#endif + gtk_window_move(GTK_WINDOW(notify_window.window), + notify_window.x, notify_window.y); + if (notify_window.timeout > 0) { + notify_window.notify_tag = + g_timeout_add(notify_window.timeout * 1000, + notify_timeout_cb, NULL); + } + + return FALSE; +} + +static void nwin_destroy_cb(GtkWidget *widget, gpointer data) +{ +} diff --git a/src/notificationwindow.h b/src/notificationwindow.h new file mode 100644 index 0000000..a6ad2e4 --- /dev/null +++ b/src/notificationwindow.h @@ -0,0 +1,33 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2013 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __NOTIFICATIONWINDOW_H__ +#define __NOTIFICATIONWINDOW_H__ + +#include +#include + +gint notification_window_open (const gchar *message, + const gchar *submessage, + guint timeout); +void notification_window_set_message (const gchar *message, + const gchar *submessage); +void notification_window_close (void); + +#endif /* __NOTIFICATIONWINDOW_H__ */ diff --git a/src/passphrase.c b/src/passphrase.c new file mode 100644 index 0000000..20447c1 --- /dev/null +++ b/src/passphrase.c @@ -0,0 +1,354 @@ +/* passphrase.c - GTK+ based passphrase callback + * Copyright (C) 2001 Werner Koch (dd9jn) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if USE_GPGME + +#include +#include +#include +#include +#include +#ifdef GDK_WINDOWING_X11 +# include +#endif /* GDK_WINDOWING_X11 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_SYS_MMAN_H +# include +#endif + +#ifdef G_OS_WIN32 +# include +#endif + +#include "passphrase.h" +#include "prefs_common.h" +#include "manage_window.h" +#include "utils.h" +#include "gtkutils.h" + +static gboolean grab_all = FALSE; + +static gboolean pass_ack; +static gchar *last_pass = NULL; + +static void passphrase_ok_cb(GtkWidget *widget, gpointer data); +static void passphrase_cancel_cb(GtkWidget *widget, gpointer data); +static gint passphrase_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer data); +static gboolean passphrase_key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data); +static gchar* passphrase_mbox(const gchar *uid_hint, const gchar *pass_hint, + gint prev_bad); + +static GtkWidget *create_description(const gchar *uid_hint, + const gchar *pass_hint, gint prev_bad); + +void +gpgmegtk_set_passphrase_grab(gint yes) +{ + grab_all = yes; +} + +static gchar* +passphrase_mbox(const gchar *uid_hint, const gchar *pass_hint, gint prev_bad) +{ + gchar *the_passphrase = NULL; + GtkWidget *vbox; + GtkWidget *confirm_box; + GtkWidget *window; + GtkWidget *pass_entry; + GtkWidget *ok_button; + GtkWidget *cancel_button; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), _("Passphrase")); + gtk_widget_set_size_request(window, 450, -1); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(passphrase_deleted), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(passphrase_key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + manage_window_set_transient(GTK_WINDOW(window)); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add(GTK_CONTAINER(window), vbox); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 8); + + if (uid_hint || pass_hint) { + GtkWidget *label; + label = create_description (uid_hint, pass_hint, prev_bad); + gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0); + } + + pass_entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(vbox), pass_entry, FALSE, FALSE, 0); + gtk_entry_set_visibility(GTK_ENTRY(pass_entry), FALSE); + gtk_widget_grab_focus(pass_entry); + + gtkut_stock_button_set_create(&confirm_box, &ok_button, GTK_STOCK_OK, + &cancel_button, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), confirm_box, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_button); + + g_signal_connect(G_OBJECT(ok_button), "clicked", + G_CALLBACK(passphrase_ok_cb), NULL); + g_signal_connect(G_OBJECT(pass_entry), "activate", + G_CALLBACK(passphrase_ok_cb), NULL); + g_signal_connect(G_OBJECT(cancel_button), "clicked", + G_CALLBACK(passphrase_cancel_cb), NULL); + + gtk_window_set_position (GTK_WINDOW(window), GTK_WIN_POS_CENTER); + if (grab_all) + gtk_window_set_policy (GTK_WINDOW(window), FALSE, FALSE, TRUE); + + gtk_widget_show_all(window); + + if (grab_all) { + /* make sure that window is viewable + * FIXME: this is still not enough */ + gtk_widget_show_now(window); + gdk_flush(); +#ifdef GDK_WINDOWING_X11 + gdk_x11_display_grab(gdk_display_get_default()); +#endif /* GDK_WINDOWING_X11 */ + if (gdk_pointer_grab(window->window, TRUE, 0, + window->window, NULL, GDK_CURRENT_TIME)) { +#ifdef GDK_WINDOWING_X11 + gdk_x11_display_ungrab(gdk_display_get_default()); +#endif /* GDK_WINDOWING_X11 */ + g_warning("OOPS: Could not grab mouse\n"); + gtk_widget_destroy(window); + return NULL; + } + if (gdk_keyboard_grab(window->window, FALSE, GDK_CURRENT_TIME)) { + gdk_display_pointer_ungrab(gdk_display_get_default(), + GDK_CURRENT_TIME); +#ifdef GDK_WINDOWING_X11 + gdk_x11_display_ungrab(gdk_display_get_default()); +#endif /* GDK_WINDOWING_X11 */ + g_warning("OOPS: Could not grab keyboard\n"); + gtk_widget_destroy(window); + return NULL; + } + } + + gtk_main(); + + if (grab_all) { + gdk_display_keyboard_ungrab(gdk_display_get_default(), + GDK_CURRENT_TIME); + gdk_display_pointer_ungrab(gdk_display_get_default(), GDK_CURRENT_TIME); +#ifdef GDK_WINDOWING_X11 + gdk_x11_display_ungrab(gdk_display_get_default()); +#endif /* GDK_WINDOWING_X11 */ + gdk_flush(); + } + + manage_window_focus_out(window, NULL, NULL); + + if (pass_ack) { + const gchar *entry_text; + entry_text = gtk_entry_get_text(GTK_ENTRY(pass_entry)); + the_passphrase = g_locale_from_utf8(entry_text, -1, NULL, NULL, NULL); + if (!the_passphrase) + the_passphrase = g_strdup(entry_text); + } + gtk_widget_destroy(window); + + return the_passphrase; +} + + +static void +passphrase_ok_cb(GtkWidget *widget, gpointer data) +{ + pass_ack = TRUE; + gtk_main_quit(); +} + +static void +passphrase_cancel_cb(GtkWidget *widget, gpointer data) +{ + pass_ack = FALSE; + gtk_main_quit(); +} + + +static gint +passphrase_deleted(GtkWidget *widget, GdkEventAny *event, gpointer data) +{ + passphrase_cancel_cb(NULL, NULL); + return TRUE; +} + + +static gboolean +passphrase_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + if (event && event->keyval == GDK_Escape) + passphrase_cancel_cb(NULL, NULL); + return FALSE; +} + +static gint +linelen (const gchar *s) +{ + gint i; + + for (i = 0; *s && *s != '\n'; s++, i++) + ; + + return i; +} + +static GtkWidget * +create_description(const gchar *uid_hint, const gchar *pass_hint, gint prev_bad) +{ + const gchar *uid = NULL, *info = NULL; + gchar *buf; + GtkWidget *label; + + if (!uid_hint) + uid = _("[no user id]"); + else + uid = uid_hint; + if (!pass_hint) + info = ""; + else + info = pass_hint; + + buf = g_strdup_printf (_("%sPlease enter the passphrase for:\n\n" + " %.*s \n" + "(%.*s)\n"), + prev_bad ? + _("Bad passphrase! Try again...\n\n") : "", + linelen (uid), uid, linelen (info), info); + + label = gtk_label_new (buf); + gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); + g_free (buf); + + return label; +} + +static int free_passphrase(gpointer _unused) +{ + if (last_pass != NULL) { +#if HAVE_MLOCK + munlock(last_pass, strlen(last_pass)); +#endif + g_free(last_pass); + last_pass = NULL; + debug_print("%% passphrase removed\n"); + } + + return FALSE; +} + +gpgme_error_t +gpgmegtk_passphrase_cb(void *opaque, const char *uid_hint, + const char *passphrase_hint, int prev_bad, int fd) +{ + const char *pass; +#if defined(G_OS_WIN32) && !defined(HAVE_GPGME_IO_WRITEN) + HANDLE hd = (HANDLE)fd; + DWORD n; +#endif + + if (prefs_common.store_passphrase && last_pass != NULL && !prev_bad) { +#ifdef HAVE_GPGME_IO_WRITEN + gpgme_io_writen(fd, last_pass, strlen(last_pass)); + gpgme_io_writen(fd, "\n", 1); +#elif defined(G_OS_WIN32) + WriteFile(hd, last_pass, strlen(last_pass), &n, NULL); + WriteFile(hd, "\n", 1, &n, NULL); +#else + write(fd, last_pass, strlen(last_pass)); + write(fd, "\n", 1); +#endif + return GPG_ERR_NO_ERROR; + } + gpgmegtk_set_passphrase_grab (prefs_common.passphrase_grab); + debug_print ("%% requesting passphrase for `%s':\n", uid_hint); + pass = passphrase_mbox (uid_hint, passphrase_hint, prev_bad); + gpgmegtk_free_passphrase(); + if (!pass) { + debug_print ("%% cancel passphrase entry\n"); +#ifdef HAVE_GPGME_IO_WRITEN + gpgme_io_writen(fd, "\n", 1); +#elif defined(G_OS_WIN32) + WriteFile(hd, "\n", 1, &n, NULL); + CloseHandle(hd); /* somehow it will block without this */ +#else + write(fd, "\n", 1); +#endif + return GPG_ERR_CANCELED; + } + else { + if (prefs_common.store_passphrase) { + last_pass = g_strdup(pass); +#if HAVE_MLOCK + if (mlock(last_pass, strlen(last_pass)) == -1) + debug_print("%% locking passphrase failed\n"); +#endif + + if (prefs_common.store_passphrase_timeout > 0) { + g_timeout_add_full(G_PRIORITY_LOW, prefs_common.store_passphrase_timeout * 60 * 1000, free_passphrase, NULL, NULL); + } + } + debug_print ("%% sending passphrase\n"); + } +#ifdef HAVE_GPGME_IO_WRITEN + gpgme_io_writen(fd, pass, strlen(pass)); + gpgme_io_writen(fd, "\n", 1); +#elif defined(G_OS_WIN32) + WriteFile(hd, pass, strlen(pass), &n, NULL); + WriteFile(hd, "\n", 1, &n, NULL); +#else + write(fd, pass, strlen(pass)); + write(fd, "\n", 1); +#endif + return GPG_ERR_NO_ERROR; +} + +void gpgmegtk_free_passphrase() +{ + (void)free_passphrase(NULL); // could be inline +} + +#endif /* USE_GPGME */ diff --git a/src/passphrase.h b/src/passphrase.h new file mode 100644 index 0000000..e0c9ead --- /dev/null +++ b/src/passphrase.h @@ -0,0 +1,35 @@ +/* passphrase.h - GTK+ based passphrase callback + * Copyright (C) 2001 Werner Koch (dd9jn) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef GPGMEGTK_PASSPHRASE_H +#define GPGMEGTK_PASSPHRASE_H + +#include +#include + +struct passphrase_cb_info_s { + gpgme_ctx_t c; + int did_it; +}; + +void gpgmegtk_set_passphrase_grab (gint yesno); +gpgme_error_t gpgmegtk_passphrase_cb(void *opaque, const char *uid_hint, + const char *passphrase_info, int prev_bad, int fd); +void gpgmegtk_free_passphrase(); + +#endif /* GPGMEGTK_PASSPHRASE_H */ diff --git a/src/plugin-marshal.c b/src/plugin-marshal.c new file mode 100644 index 0000000..defddf4 --- /dev/null +++ b/src/plugin-marshal.c @@ -0,0 +1,260 @@ + +#include + + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_char (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* VOID:POINTER (plugin-marshal.list:1) */ + +/* VOID:POINTER,POINTER (plugin-marshal.list:2) */ +void +syl_plugin_marshal_VOID__POINTER_POINTER (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__POINTER_POINTER) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__POINTER_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + data2); +} + +/* VOID:POINTER,POINTER,STRING,STRING,POINTER (plugin-marshal.list:3) */ +void +syl_plugin_marshal_VOID__POINTER_POINTER_STRING_STRING_POINTER (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__POINTER_POINTER_STRING_STRING_POINTER) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer arg_3, + gpointer arg_4, + gpointer arg_5, + gpointer data2); + register GMarshalFunc_VOID__POINTER_POINTER_STRING_STRING_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 6); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__POINTER_POINTER_STRING_STRING_POINTER) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + g_marshal_value_peek_string (param_values + 3), + g_marshal_value_peek_string (param_values + 4), + g_marshal_value_peek_pointer (param_values + 5), + data2); +} + +/* BOOLEAN:POINTER,INT,INT,STRING,POINTER (plugin-marshal.list:4) */ +void +syl_plugin_marshal_BOOLEAN__POINTER_INT_INT_STRING_POINTER (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_INT_INT_STRING_POINTER) (gpointer data1, + gpointer arg_1, + gint arg_2, + gint arg_3, + gpointer arg_4, + gpointer arg_5, + gpointer data2); + register GMarshalFunc_BOOLEAN__POINTER_INT_INT_STRING_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 6); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__POINTER_INT_INT_STRING_POINTER) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_int (param_values + 2), + g_marshal_value_peek_int (param_values + 3), + g_marshal_value_peek_string (param_values + 4), + g_marshal_value_peek_pointer (param_values + 5), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* VOID:POINTER,POINTER,BOOLEAN (plugin-marshal.list:5) */ +void +syl_plugin_marshal_VOID__POINTER_POINTER_BOOLEAN (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__POINTER_POINTER_BOOLEAN) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gboolean arg_3, + gpointer data2); + register GMarshalFunc_VOID__POINTER_POINTER_BOOLEAN callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 4); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__POINTER_POINTER_BOOLEAN) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + g_marshal_value_peek_boolean (param_values + 3), + data2); +} + +/* VOID:INT (plugin-marshal.list:6) */ + +/* VOID:POINTER,STRING,STRING,POINTER (plugin-marshal.list:7) */ +void +syl_plugin_marshal_VOID__POINTER_STRING_STRING_POINTER (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__POINTER_STRING_STRING_POINTER) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer arg_3, + gpointer arg_4, + gpointer data2); + register GMarshalFunc_VOID__POINTER_STRING_STRING_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 5); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__POINTER_STRING_STRING_POINTER) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_string (param_values + 2), + g_marshal_value_peek_string (param_values + 3), + g_marshal_value_peek_pointer (param_values + 4), + data2); +} + diff --git a/src/plugin-marshal.h b/src/plugin-marshal.h new file mode 100644 index 0000000..d6966a1 --- /dev/null +++ b/src/plugin-marshal.h @@ -0,0 +1,58 @@ + +#ifndef __syl_plugin_marshal_MARSHAL_H__ +#define __syl_plugin_marshal_MARSHAL_H__ + +#include + +G_BEGIN_DECLS + +/* VOID:POINTER (plugin-marshal.list:1) */ +#define syl_plugin_marshal_VOID__POINTER g_cclosure_marshal_VOID__POINTER + +/* VOID:POINTER,POINTER (plugin-marshal.list:2) */ +extern void syl_plugin_marshal_VOID__POINTER_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:POINTER,POINTER,STRING,STRING,POINTER (plugin-marshal.list:3) */ +extern void syl_plugin_marshal_VOID__POINTER_POINTER_STRING_STRING_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN:POINTER,INT,INT,STRING,POINTER (plugin-marshal.list:4) */ +extern void syl_plugin_marshal_BOOLEAN__POINTER_INT_INT_STRING_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:POINTER,POINTER,BOOLEAN (plugin-marshal.list:5) */ +extern void syl_plugin_marshal_VOID__POINTER_POINTER_BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:INT (plugin-marshal.list:6) */ +#define syl_plugin_marshal_VOID__INT g_cclosure_marshal_VOID__INT + +/* VOID:POINTER,STRING,STRING,POINTER (plugin-marshal.list:7) */ +extern void syl_plugin_marshal_VOID__POINTER_STRING_STRING_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +G_END_DECLS + +#endif /* __syl_plugin_marshal_MARSHAL_H__ */ + diff --git a/src/plugin-marshal.list b/src/plugin-marshal.list new file mode 100644 index 0000000..2c692c4 --- /dev/null +++ b/src/plugin-marshal.list @@ -0,0 +1,7 @@ +VOID:POINTER +VOID:POINTER,POINTER +VOID:POINTER,POINTER,STRING,STRING,POINTER +BOOLEAN:POINTER,INT,INT,STRING,POINTER +VOID:POINTER,POINTER,BOOLEAN +VOID:INT +VOID:POINTER,STRING,STRING,POINTER diff --git a/src/plugin-types.h b/src/plugin-types.h new file mode 100644 index 0000000..2db52ab --- /dev/null +++ b/src/plugin-types.h @@ -0,0 +1,38 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2016 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PLUGIN_TYPES_H__ +#define __PLUGIN_TYPES_H__ + +#include + +typedef struct _SylPluginAttachInfo SylPluginAttachInfo; + +#include "procmime.h" + +struct _SylPluginAttachInfo +{ + gchar *file; + gchar *content_type; + EncodingType encoding; + gchar *name; + gsize size; +}; + +#endif /* __PLUGIN_TYPES_H__ */ diff --git a/src/plugin.c b/src/plugin.c new file mode 100644 index 0000000..52fbc5d --- /dev/null +++ b/src/plugin.c @@ -0,0 +1,1535 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2016 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "plugin.h" +#include "utils.h" +#include "folder.h" +#include "plugin-marshal.h" + +G_DEFINE_TYPE(SylPlugin, syl_plugin, G_TYPE_OBJECT); + +enum { + PLUGIN_LOAD, + PLUGIN_UNLOAD, + FOLDERVIEW_MENU_POPUP, + SUMMARYVIEW_MENU_POPUP, + COMPOSE_CREATED, + COMPOSE_DESTROY, + TEXTVIEW_MENU_POPUP, + COMPOSE_SEND, + MESSAGEVIEW_SHOW, + INC_MAIL_START, + INC_MAIL_FINISHED, + PREFS_COMMON_OPEN, + PREFS_ACCOUNT_OPEN, + PREFS_FILTER_OPEN, + PREFS_FILTER_EDIT_OPEN, + PREFS_TEMPLATE_OPEN, + PLUGIN_MANAGER_OPEN, + MAIN_WINDOW_TOOLBAR_CHANGED, + COMPOSE_TOOLBAR_CHANGED, + COMPOSE_ATTACH_CHANGED, + LAST_SIGNAL +}; + +#define GETFUNC(sym) { func = syl_plugin_lookup_symbol(sym); } + +#define SAFE_CALL(func_ptr) { if (func_ptr) func_ptr(); } +#define SAFE_CALL_RET(func_ptr) (func_ptr ? func_ptr() : NULL) +#define SAFE_CALL_RET_VAL(func_ptr, retval) \ + (func_ptr ? func_ptr() : retval) +#define SAFE_CALL_ARG1(func_ptr, arg1) { if (func_ptr) func_ptr(arg1); } +#define SAFE_CALL_ARG1_RET(func_ptr, arg1) \ + (func_ptr ? func_ptr(arg1) : NULL) +#define SAFE_CALL_ARG1_RET_VAL(func_ptr, arg1, retval) \ + (func_ptr ? func_ptr(arg1) : retval) +#define SAFE_CALL_ARG2(func_ptr, arg1, arg2) \ + { if (func_ptr) func_ptr(arg1, arg2); } +#define SAFE_CALL_ARG2_RET(func_ptr, arg1, arg2) \ + (func_ptr ? func_ptr(arg1, arg2) : NULL) +#define SAFE_CALL_ARG2_RET_VAL(func_ptr, arg1, arg2, retval) \ + (func_ptr ? func_ptr(arg1, arg2) : retval) +#define SAFE_CALL_ARG3(func_ptr, arg1, arg2, arg3) \ + { if (func_ptr) func_ptr(arg1, arg2, arg3); } +#define SAFE_CALL_ARG3_RET(func_ptr, arg1, arg2, arg3) \ + (func_ptr ? func_ptr(arg1, arg2, arg3) : NULL) +#define SAFE_CALL_ARG3_RET_VAL(func_ptr, arg1, arg2, arg3, retval) \ + (func_ptr ? func_ptr(arg1, arg2, arg3) : retval) +#define SAFE_CALL_ARG4(func_ptr, arg1, arg2, arg3, arg4) \ + { if (func_ptr) func_ptr(arg1, arg2, arg3, arg4); } +#define SAFE_CALL_ARG4_RET(func_ptr, arg1, arg2, arg3, arg4) \ + (func_ptr ? func_ptr(arg1, arg2, arg3, arg4) : NULL) +#define SAFE_CALL_ARG4_RET_VAL(func_ptr, arg1, arg2, arg3, arg4, retval) \ + (func_ptr ? func_ptr(arg1, arg2, arg3, arg4) : retval) + +#define CALL_VOID_POINTER(getobj, sym) \ +{ \ + void (*func)(gpointer); \ + gpointer obj; \ + obj = getobj(); \ + if (obj) { \ + GETFUNC(sym); \ + SAFE_CALL_ARG1(func, obj); \ + } \ +} + + +static guint plugin_signals[LAST_SIGNAL] = { 0 }; + +static GHashTable *sym_table = NULL; +static GSList *module_list = NULL; +static GObject *plugin_obj = NULL; + +static void syl_plugin_init(SylPlugin *self) +{ +} + +static void syl_plugin_class_init(SylPluginClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + plugin_signals[PLUGIN_LOAD] = + g_signal_new("plugin-load", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, plugin_load), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + plugin_signals[PLUGIN_UNLOAD] = + g_signal_new("plugin-unload", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, plugin_unload), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + plugin_signals[FOLDERVIEW_MENU_POPUP] = + g_signal_new("folderview-menu-popup", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, + folderview_menu_popup), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + plugin_signals[SUMMARYVIEW_MENU_POPUP] = + g_signal_new("summaryview-menu-popup", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, + summaryview_menu_popup), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + plugin_signals[COMPOSE_CREATED] = + g_signal_new("compose-created", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, compose_created), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + plugin_signals[COMPOSE_DESTROY] = + g_signal_new("compose-destroy", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, compose_destroy), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + plugin_signals[TEXTVIEW_MENU_POPUP] = + g_signal_new("textview-menu-popup", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, + textview_menu_popup), + NULL, NULL, + syl_plugin_marshal_VOID__POINTER_POINTER_STRING_STRING_POINTER, + G_TYPE_NONE, + 5, + G_TYPE_POINTER, + G_TYPE_POINTER, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_POINTER); + plugin_signals[COMPOSE_SEND] = + g_signal_new("compose-send", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(SylPluginClass, compose_send), + NULL, NULL, + syl_plugin_marshal_BOOLEAN__POINTER_INT_INT_STRING_POINTER, + G_TYPE_BOOLEAN, + 5, + G_TYPE_POINTER, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_STRING, + G_TYPE_POINTER); + plugin_signals[MESSAGEVIEW_SHOW] = + g_signal_new("messageview-show", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, messageview_show), + NULL, NULL, + syl_plugin_marshal_VOID__POINTER_POINTER_BOOLEAN, + G_TYPE_NONE, + 3, + G_TYPE_POINTER, + G_TYPE_POINTER, + G_TYPE_BOOLEAN); + + plugin_signals[INC_MAIL_START] = + g_signal_new("inc-mail-start", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, inc_mail_start), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + plugin_signals[INC_MAIL_FINISHED] = + g_signal_new("inc-mail-finished", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, inc_mail_finished), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, + G_TYPE_INT); + + plugin_signals[PREFS_COMMON_OPEN] = + g_signal_new("prefs-common-open", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, prefs_common_open), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + plugin_signals[PREFS_ACCOUNT_OPEN] = + g_signal_new("prefs-account-open", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, prefs_account_open), + NULL, NULL, + syl_plugin_marshal_VOID__POINTER_POINTER, + G_TYPE_NONE, + 2, + G_TYPE_POINTER, + G_TYPE_POINTER); + plugin_signals[PREFS_FILTER_OPEN] = + g_signal_new("prefs-filter-open", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, prefs_filter_open), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + plugin_signals[PREFS_FILTER_EDIT_OPEN] = + g_signal_new("prefs-filter-edit-open", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, prefs_filter_edit_open), + NULL, NULL, + syl_plugin_marshal_VOID__POINTER_STRING_STRING_POINTER, + G_TYPE_NONE, + 4, + G_TYPE_POINTER, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_POINTER); + plugin_signals[PREFS_TEMPLATE_OPEN] = + g_signal_new("prefs-template-open", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, prefs_template_open), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + plugin_signals[PLUGIN_MANAGER_OPEN] = + g_signal_new("plugin-manager-open", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, plugin_manager_open), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + plugin_signals[MAIN_WINDOW_TOOLBAR_CHANGED] = + g_signal_new("main-window-toolbar-changed", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, main_window_toolbar_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + plugin_signals[COMPOSE_TOOLBAR_CHANGED] = + g_signal_new("compose-toolbar-changed", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, compose_toolbar_changed), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + plugin_signals[COMPOSE_TOOLBAR_CHANGED] = + g_signal_new("compose-attach-changed", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SylPluginClass, compose_attach_changed), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); +} + +void syl_plugin_signal_connect(const gchar *name, GCallback callback, + gpointer data) +{ + g_return_if_fail(plugin_obj != NULL); + + g_signal_connect(plugin_obj, name, callback, data); +} + +void syl_plugin_signal_disconnect(gpointer func, gpointer data) +{ + g_return_if_fail(plugin_obj != NULL); + + g_signal_handlers_disconnect_by_func(plugin_obj, func, data); +} + +void syl_plugin_signal_emit(const gchar *name, ...) +{ + guint signal_id; + + g_return_if_fail(plugin_obj != NULL); + + if (g_signal_parse_name(name, G_TYPE_FROM_INSTANCE(plugin_obj), &signal_id, NULL, FALSE)) { + \ + va_list var_args; + va_start(var_args, name); + g_signal_emit_valist(plugin_obj, signal_id, 0, var_args); + va_end(var_args); + } else + g_warning("%s: signal '%s' not found", G_STRLOC, name); +} + +gint syl_plugin_init_lib(void) +{ + if (!g_module_supported()) { + g_warning("Plug-in is not supported."); + return -1; + } + + if (!sym_table) { + sym_table = g_hash_table_new(g_str_hash, g_str_equal); + } + + if (!plugin_obj) { + plugin_obj = g_object_new(SYL_TYPE_PLUGIN, NULL); + } + + return 0; +} + +gint syl_plugin_load(const gchar *name) +{ + GModule *module; + SylPluginLoadFunc load_func = NULL; + gchar *file; + + g_return_val_if_fail(plugin_obj != NULL, -1); + g_return_val_if_fail(name != NULL, -1); + + debug_print("syl_plugin_load: loading %s\n", name); + + if (!g_path_is_absolute(name)) + file = g_strconcat(PLUGINDIR, G_DIR_SEPARATOR_S, name, NULL); + else + file = g_strdup(name); + + module = g_module_open(file, G_MODULE_BIND_LAZY); + if (!module) { + g_warning("Cannot open module: %s: %s", name, g_module_error()); + g_free(file); + return -1; + } + if (g_slist_find(module_list, module)) { + g_warning("Module %s is already loaded", name); + g_free(file); + return -1; + } + + if (g_module_symbol(module, "plugin_load", (gpointer)&load_func)) { + if (!syl_plugin_check_version(module)) { + g_warning("Version check failed. Skipping: %s", name); + g_module_close(module); + g_free(file); + return -1; + } + + debug_print("calling plugin_load() in %s\n", + g_module_name(module)); + load_func(); + module_list = g_slist_prepend(module_list, module); + g_signal_emit(plugin_obj, plugin_signals[PLUGIN_LOAD], 0, module); + } else { + g_warning("Cannot get symbol: %s: %s", name, g_module_error()); + g_module_close(module); + g_free(file); + return -1; + } + + g_free(file); + + return 0; +} + +gint syl_plugin_load_all(const gchar *dir) +{ + GDir *d; + const gchar *dir_name; + gchar *path; + gint count = 0; + + g_return_val_if_fail(dir != NULL, -1); + + debug_print("loading plugins from directory: %s\n", dir); + + if ((d = g_dir_open(dir, 0, NULL)) == NULL) { + debug_print("failed to open directory: %s\n", dir); + return -1; + } + + while ((dir_name = g_dir_read_name(d)) != NULL) { + if (!g_str_has_suffix(dir_name, "." G_MODULE_SUFFIX)) + continue; + path = g_strconcat(dir, G_DIR_SEPARATOR_S, dir_name, NULL); + if (syl_plugin_load(path) == 0) + count++; + g_free(path); + } + + g_dir_close(d); + + return count; +} + +void syl_plugin_unload_all(void) +{ + GSList *cur; + + g_return_if_fail(plugin_obj != NULL); + + for (cur = module_list; cur != NULL; cur = cur->next) { + GModule *module = (GModule *)cur->data; + SylPluginUnloadFunc unload_func = NULL; + + if (g_module_symbol(module, "plugin_unload", + (gpointer)&unload_func)) { + g_signal_emit(plugin_obj, plugin_signals[PLUGIN_UNLOAD], + 0, module); + debug_print("calling plugin_unload() in %s\n", + g_module_name(module)); + unload_func(); + } else { + g_warning("Cannot get symbol: %s", g_module_error()); + } + if (!g_module_close(module)) { + g_warning("Module unload failed: %s", g_module_error()); + } + } + + g_slist_free(module_list); + module_list = NULL; +} + +GSList *syl_plugin_get_module_list(void) +{ + return module_list; +} + +SylPluginInfo *syl_plugin_get_info(GModule *module) +{ + SylPluginInfo * (*plugin_info_func)(void); + + g_return_val_if_fail(module != NULL, NULL); + + debug_print("getting plugin information of %s\n", + g_module_name(module)); + + if (g_module_symbol(module, "plugin_info", + (gpointer)&plugin_info_func)) { + debug_print("calling plugin_info() in %s\n", + g_module_name(module)); + return plugin_info_func(); + } else { + g_warning("Cannot get symbol: %s: %s", g_module_name(module), + g_module_error()); + return NULL; + } +} + +gboolean syl_plugin_check_version(GModule *module) +{ + gint (*version_func)(void); + gint ver; + gint a_major; + gint a_minor; + gint p_major; + gint p_minor; + + g_return_val_if_fail(module != NULL, FALSE); + + if (g_module_symbol(module, "plugin_interface_version", + (gpointer)&version_func)) { + debug_print("calling plugin_interface_version() in %s\n", + g_module_name(module)); + ver = version_func(); + } else { + g_warning("Cannot get symbol: %s: %s", g_module_name(module), + g_module_error()); + return FALSE; + } + + a_major = SYL_PLUGIN_INTERFACE_VERSION & 0xff00; + a_minor = SYL_PLUGIN_INTERFACE_VERSION & 0x00ff; + p_major = ver & 0xff00; + p_minor = ver & 0x00ff; + if (a_major == p_major && a_minor >= p_minor) { + debug_print("Version OK: plugin: %d, app: %d\n", + ver, SYL_PLUGIN_INTERFACE_VERSION); + return TRUE; + } else { + g_warning("Plugin interface version mismatch: plugin: %d, app: %d", ver, SYL_PLUGIN_INTERFACE_VERSION); + return FALSE; + } +} + +gint syl_plugin_add_symbol(const gchar *name, gpointer sym) +{ + g_hash_table_insert(sym_table, (gpointer)name, sym); + return 0; +} + +gpointer syl_plugin_lookup_symbol(const gchar *name) +{ + return g_hash_table_lookup(sym_table, name); +} + +const gchar *syl_plugin_get_prog_version(void) +{ + gpointer sym; + + sym = syl_plugin_lookup_symbol("prog_version"); + return (gchar *)sym; +} + +void syl_plugin_main_window_lock(void) +{ + CALL_VOID_POINTER(syl_plugin_main_window_get, + "main_window_lock"); +} + +void syl_plugin_main_window_unlock(void) +{ + CALL_VOID_POINTER(syl_plugin_main_window_get, + "main_window_unlock"); +} + +gpointer syl_plugin_main_window_get(void) +{ + gpointer (*func)(void); + + func = syl_plugin_lookup_symbol("main_window_get"); + return SAFE_CALL_RET(func); +} + +void syl_plugin_main_window_popup(gpointer mainwin) +{ + void (*func)(gpointer); + + func = syl_plugin_lookup_symbol("main_window_popup"); + SAFE_CALL_ARG1(func, mainwin); +} + +GtkWidget *syl_plugin_main_window_get_toolbar(void) +{ + gpointer widget; + + widget = syl_plugin_lookup_symbol("main_window_toolbar"); + return GTK_WIDGET(widget); +} + +GtkWidget *syl_plugin_main_window_get_statusbar(void) +{ + gpointer widget; + + widget = syl_plugin_lookup_symbol("main_window_statusbar"); + return GTK_WIDGET(widget); +} + +void syl_plugin_app_will_exit(gboolean force) +{ + void (*func)(gboolean); + + func = syl_plugin_lookup_symbol("app_will_exit"); + SAFE_CALL_ARG1(func, force); +} + +static GtkItemFactory *get_item_factory(const gchar *path) +{ + GtkItemFactory *ifactory; + + if (!path) + return NULL; + + if (strncmp(path, "
", 6) == 0) + ifactory = syl_plugin_lookup_symbol("main_window_menu_factory"); + else if (strncmp(path, "", 12) == 0) + ifactory = syl_plugin_lookup_symbol("folderview_mail_popup_factory"); + else if (strncmp(path, "", 12) == 0) + ifactory = syl_plugin_lookup_symbol("folderview_imap_popup_factory"); + else if (strncmp(path, "", 12) == 0) + ifactory = syl_plugin_lookup_symbol("folderview_news_popup_factory"); + else if (strncmp(path, "", 13) == 0) + ifactory = syl_plugin_lookup_symbol("summaryview_popup_factory"); + else + ifactory = syl_plugin_lookup_symbol("main_window_menu_factory"); + + return ifactory; +} + +gint syl_plugin_add_menuitem(const gchar *parent, const gchar *label, + SylPluginCallbackFunc func, gpointer data) +{ + GtkItemFactory *ifactory; + GtkWidget *menu; + GtkWidget *menuitem; + + if (!parent) + return -1; + + ifactory = get_item_factory(parent); + if (!ifactory) + return -1; + + menu = gtk_item_factory_get_widget(ifactory, parent); + if (!menu) + return -1; + + if (label) + menuitem = gtk_menu_item_new_with_label(label); + else { + menuitem = gtk_menu_item_new(); + gtk_widget_set_sensitive(menuitem, FALSE); + } + gtk_widget_show(menuitem); + gtk_menu_append(GTK_MENU(menu), menuitem); + if (func) + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(func), data); + + return 0; +} + +gint syl_plugin_add_factory_item(const gchar *parent, const gchar *label, + SylPluginCallbackFunc func, gpointer data) +{ + GtkItemFactory *ifactory; + GtkItemFactoryEntry entry = {NULL, NULL, NULL, 0, NULL}; + + if (!parent) + return -1; + + ifactory = get_item_factory(parent); + if (!ifactory) + return -1; + + if (label) { + entry.path = (gchar *)label; + if (g_str_has_suffix(label, "/---")) + entry.item_type = ""; + else + entry.item_type = NULL; + } else { + entry.path = "/---"; + entry.item_type = ""; + } + entry.callback = func; + g_print("entry.path = %s\n", entry.path); + + gtk_item_factory_create_item(ifactory, &entry, data, 2); + + return 0; +} + +void syl_plugin_menu_set_sensitive(const gchar *path, gboolean sensitive) +{ + GtkItemFactory *ifactory; + GtkWidget *widget; + + g_return_if_fail(path != NULL); + + ifactory = get_item_factory(path); + if (!ifactory) + return; + + widget = gtk_item_factory_get_item(ifactory, path); + gtk_widget_set_sensitive(widget, sensitive); +} + +void syl_plugin_menu_set_sensitive_all(GtkMenuShell *menu_shell, + gboolean sensitive) +{ + GList *cur; + + for (cur = menu_shell->children; cur != NULL; cur = cur->next) + gtk_widget_set_sensitive(GTK_WIDGET(cur->data), sensitive); +} + +void syl_plugin_menu_set_active(const gchar *path, gboolean is_active) +{ + GtkItemFactory *ifactory; + GtkWidget *widget; + + g_return_if_fail(path != NULL); + + ifactory = get_item_factory(path); + if (!ifactory) + return; + + widget = gtk_item_factory_get_item(ifactory, path); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), is_active); +} + +gpointer syl_plugin_folderview_get(void) +{ + gpointer (*func)(void); + GETFUNC("folderview_get"); + return SAFE_CALL_RET(func); +} + +void syl_plugin_folderview_add_sub_widget(GtkWidget *widget) +{ + void (*func)(gpointer, GtkWidget *); + gpointer folderview; + + folderview = syl_plugin_folderview_get(); + if (folderview) { + GETFUNC("folderview_add_sub_widget"); + SAFE_CALL_ARG2(func, folderview, widget); + } +} + +void syl_plugin_folderview_select(FolderItem *item) +{ + void (*func)(gpointer, FolderItem *); + gpointer folderview; + + folderview = syl_plugin_folderview_get(); + if (folderview) { + GETFUNC("folderview_select"); + SAFE_CALL_ARG2(func, folderview, item); + } +} + +void syl_plugin_folderview_unselect(void) +{ + CALL_VOID_POINTER(syl_plugin_folderview_get, + "folderview_unselect"); +} + +void syl_plugin_folderview_select_next_unread(void) +{ + CALL_VOID_POINTER(syl_plugin_folderview_get, + "folderview_select_next_unread"); +} + +FolderItem *syl_plugin_folderview_get_selected_item(void) +{ + FolderItem * (*func)(gpointer); + gpointer folderview; + + folderview = syl_plugin_folderview_get(); + if (folderview) { + GETFUNC("folderview_get_selected_item"); + return SAFE_CALL_ARG1_RET(func, folderview); + } + + return NULL; +} + +gint syl_plugin_folderview_check_new(Folder *folder) +{ + gint (*func)(Folder *); + GETFUNC("folderview_check_new"); + return SAFE_CALL_ARG1_RET_VAL(func, folder, FALSE); +} + +gint syl_plugin_folderview_check_new_item(FolderItem *item) +{ + gint (*func)(FolderItem *); + GETFUNC("folderview_check_new_item"); + return SAFE_CALL_ARG1_RET_VAL(func, item, FALSE); +} + +gint syl_plugin_folderview_check_new_all(void) +{ + gint (*func)(void); + GETFUNC("folderview_check_new_all"); + return SAFE_CALL_RET_VAL(func, FALSE); +} + +void syl_plugin_folderview_update_item(FolderItem *item, + gboolean update_summary) +{ + void (*func)(FolderItem *, gboolean); + GETFUNC("folderview_update_item"); + SAFE_CALL_ARG2(func, item, update_summary); +} + +void syl_plugin_folderview_update_item_foreach(GHashTable *table, + gboolean update_summary) +{ + void (*func)(GHashTable *, gboolean); + GETFUNC("folderview_update_item_foreach"); + SAFE_CALL_ARG2(func, table, update_summary); +} + +void syl_plugin_folderview_update_all_updated(gboolean update_summary) +{ + void (*func)(gboolean); + GETFUNC("folderview_update_all_updated"); + SAFE_CALL_ARG1(func, update_summary); +} + +void syl_plugin_folderview_check_new_selected(void) +{ + CALL_VOID_POINTER(syl_plugin_folderview_get, + "folderview_check_new_selected"); +} + +gpointer syl_plugin_summary_view_get(void) +{ + gpointer sym; + + sym = syl_plugin_lookup_symbol("summaryview"); + return sym; +} + +void syl_plugin_summary_select_by_msgnum(guint msgnum) +{ + void (*func)(gpointer, guint); + gpointer summary; + + summary = syl_plugin_summary_view_get(); + if (summary) { + func = syl_plugin_lookup_symbol("summary_select_by_msgnum"); + SAFE_CALL_ARG2(func, summary, msgnum); + } +} + +gboolean syl_plugin_summary_select_by_msginfo(MsgInfo *msginfo) +{ + gboolean (*func)(gpointer, MsgInfo *); + gpointer summary; + + summary = syl_plugin_summary_view_get(); + if (summary) { + func = syl_plugin_lookup_symbol("summary_select_by_msginfo"); + return SAFE_CALL_ARG2_RET_VAL(func, summary, msginfo, FALSE); + } + + return FALSE; +} + +void syl_plugin_open_message(const gchar *folder_id, guint msgnum) +{ + FolderItem *item; + MsgInfo *msginfo; + + item = folder_find_item_from_identifier(folder_id); + msginfo = folder_item_get_msginfo(item, msgnum); + + if (msginfo) { + if (!syl_plugin_summary_select_by_msginfo(msginfo)) { + syl_plugin_open_message_by_new_window(msginfo); + } + procmsg_msginfo_free(msginfo); + } +} + +void syl_plugin_summary_show_queued_msgs(void) +{ + CALL_VOID_POINTER(syl_plugin_summary_view_get, + "summary_show_queued_msgs"); +} + +void syl_plugin_summary_lock(void) +{ + CALL_VOID_POINTER(syl_plugin_summary_view_get, + "summary_lock"); +} + +void syl_plugin_summary_unlock(void) +{ + CALL_VOID_POINTER(syl_plugin_summary_view_get, + "summary_unlock"); +} + +gboolean syl_plugin_summary_is_locked(void) +{ + gboolean (*func)(gpointer); + gpointer summary; + + summary = syl_plugin_summary_view_get(); + if (summary) { + GETFUNC("summary_is_locked"); + return SAFE_CALL_ARG1_RET_VAL(func, summary, FALSE); + } + + return FALSE; +} + +gboolean syl_plugin_summary_is_read_locked(void) +{ + gboolean (*func)(gpointer); + gpointer summary; + + summary = syl_plugin_summary_view_get(); + if (summary) { + GETFUNC("summary_is_read_locked"); + return SAFE_CALL_ARG1_RET_VAL(func, summary, FALSE); + } + + return FALSE; +} + +void syl_plugin_summary_write_lock(void) +{ + CALL_VOID_POINTER(syl_plugin_summary_view_get, + "summary_write_lock"); +} + +void syl_plugin_summary_write_unlock(void) +{ + CALL_VOID_POINTER(syl_plugin_summary_view_get, + "summary_write_unlock"); +} + +gboolean syl_plugin_summary_is_write_locked(void) +{ + gboolean (*func)(gpointer); + gpointer summary; + + summary = syl_plugin_summary_view_get(); + if (summary) { + GETFUNC("summary_is_write_locked"); + return SAFE_CALL_ARG1_RET_VAL(func, summary, FALSE); + } + + return FALSE; +} + +FolderItem *syl_plugin_summary_get_current_folder(void) +{ + FolderItem * (*func)(gpointer); + gpointer summary; + + summary = syl_plugin_summary_view_get(); + if (summary) { + GETFUNC("summary_get_current_folder"); + return SAFE_CALL_ARG1_RET(func, summary); + } + + return NULL; +} + +gint syl_plugin_summary_get_selection_type(void) +{ + gint (*func)(gpointer); + gpointer summary; + + summary = syl_plugin_summary_view_get(); + if (summary) { + GETFUNC("summary_get_selection_type"); + return SAFE_CALL_ARG1_RET_VAL(func, summary, 0); + } + + return 0; +} + +GSList *syl_plugin_summary_get_selected_msg_list(void) +{ + GSList * (*func)(gpointer); + gpointer summary; + + summary = syl_plugin_summary_view_get(); + if (summary) { + GETFUNC("summary_get_selected_msg_list"); + return SAFE_CALL_ARG1_RET(func, summary); + } + + return NULL; +} + +GSList *syl_plugin_summary_get_msg_list(void) +{ + GSList * (*func)(gpointer); + gpointer summary; + + summary = syl_plugin_summary_view_get(); + if (summary) { + GETFUNC("summary_get_msg_list"); + return SAFE_CALL_ARG1_RET(func, summary); + } + + return NULL; +} + +void syl_plugin_summary_redisplay_msg(void) +{ + CALL_VOID_POINTER(syl_plugin_summary_view_get, + "summary_redisplay_msg"); +} + +void syl_plugin_summary_open_msg(void) +{ + CALL_VOID_POINTER(syl_plugin_summary_view_get, + "summary_open_msg"); +} + +void syl_plugin_summary_view_source(void) +{ + CALL_VOID_POINTER(syl_plugin_summary_view_get, + "summary_view_source"); +} + +void syl_plugin_summary_reedit(void) +{ + CALL_VOID_POINTER(syl_plugin_summary_view_get, + "summary_reedit"); +} + +void syl_plugin_summary_update_selected_rows(void) +{ + CALL_VOID_POINTER(syl_plugin_summary_view_get, + "summary_update_selected_rows"); +} + +void syl_plugin_summary_update_by_msgnum(guint msgnum) +{ + void (*func)(gpointer, guint); + gpointer summary; + + summary = syl_plugin_summary_view_get(); + if (summary) { + func = syl_plugin_lookup_symbol("summary_update_by_msgnum"); + SAFE_CALL_ARG2(func, summary, msgnum); + } +} + +gpointer syl_plugin_messageview_create_with_new_window(void) +{ + gpointer (*func)(void); + + func = syl_plugin_lookup_symbol("messageview_create_with_new_window"); + return SAFE_CALL_RET(func); +} + +void syl_plugin_open_message_by_new_window(MsgInfo *msginfo) +{ + gpointer msgview; + gpointer (*func)(gpointer, MsgInfo *, gboolean); + + msgview = syl_plugin_messageview_create_with_new_window(); + if (msgview) { + func = syl_plugin_lookup_symbol("messageview_show"); + SAFE_CALL_ARG3(func, msgview, msginfo, FALSE); + } +} + + +gpointer syl_plugin_compose_new(PrefsAccount *account, FolderItem *item, + const gchar *mailto, GPtrArray *attach_files) +{ + gpointer (*func)(PrefsAccount *, FolderItem *, const gchar *, + GPtrArray *); + + func = syl_plugin_lookup_symbol("compose_new"); + return SAFE_CALL_ARG4_RET(func, account, item, mailto, attach_files); +} + +gpointer syl_plugin_compose_reply(MsgInfo *msginfo, FolderItem *item, + gint mode, const gchar *body) +{ + gpointer (*func)(MsgInfo *, FolderItem *, gint, const gchar *); + + GETFUNC("compose_reply"); + return SAFE_CALL_ARG4_RET(func, msginfo, item, mode, body); +} + +gpointer syl_plugin_compose_forward(GSList *mlist, FolderItem *item, + gboolean as_attach, const gchar *body) +{ + gpointer (*func)(GSList *, FolderItem *, gboolean, const gchar *); + + GETFUNC("compose_forward"); + return SAFE_CALL_ARG4_RET(func, mlist, item, as_attach, body); +} + +gpointer syl_plugin_compose_redirect(MsgInfo *msginfo, FolderItem *item) +{ + gpointer (*func)(MsgInfo *, FolderItem *); + + GETFUNC("compose_redirect"); + return SAFE_CALL_ARG2_RET(func, msginfo, item); +} + +gpointer syl_plugin_compose_reedit(MsgInfo *msginfo) +{ + gpointer (*func)(MsgInfo *); + + GETFUNC("compose_reedit"); + return SAFE_CALL_ARG1_RET(func, msginfo); +} + +void syl_plugin_compose_entry_set(gpointer compose, const gchar *text, + gint type) +{ + void (*func)(gpointer, const gchar *, gint); + + func = syl_plugin_lookup_symbol("compose_entry_set"); + SAFE_CALL_ARG3(func, compose, text, type); +} + +void syl_plugin_compose_entry_append(gpointer compose, const gchar *text, + gint type) +{ + void (*func)(gpointer, const gchar *, gint); + + func = syl_plugin_lookup_symbol("compose_entry_append"); + SAFE_CALL_ARG3(func, compose, text, type); +} + +gchar *syl_plugin_compose_entry_get_text(gpointer compose, gint type) +{ + gchar * (*func)(gpointer, gint); + + func = syl_plugin_lookup_symbol("compose_entry_get_text"); + return SAFE_CALL_ARG2_RET(func, compose, type); +} + +void syl_plugin_compose_lock(gpointer compose) +{ + void (*func)(gpointer); + + func = syl_plugin_lookup_symbol("compose_lock"); + SAFE_CALL_ARG1(func, compose); +} + +void syl_plugin_compose_unlock(gpointer compose) +{ + void (*func)(gpointer); + + func = syl_plugin_lookup_symbol("compose_unlock"); + SAFE_CALL_ARG1(func, compose); +} + +GtkWidget *syl_plugin_compose_get_toolbar(gpointer compose) +{ + GtkWidget * (*func)(gpointer); + + func = syl_plugin_lookup_symbol("compose_get_toolbar"); + return SAFE_CALL_ARG1_RET(func, compose); +} + +GtkWidget *syl_plugin_compose_get_misc_hbox(gpointer compose) +{ + GtkWidget * (*func)(gpointer); + + func = syl_plugin_lookup_symbol("compose_get_misc_hbox"); + return SAFE_CALL_ARG1_RET(func, compose); +} + +GtkWidget *syl_plugin_compose_get_textview(gpointer compose) +{ + GtkWidget * (*func)(gpointer); + + func = syl_plugin_lookup_symbol("compose_get_textview"); + return SAFE_CALL_ARG1_RET(func, compose); +} + +gint syl_plugin_compose_send(gpointer compose, gboolean close_on_success) +{ + gint (*func)(gpointer, gboolean); + + GETFUNC("compose_send"); + return SAFE_CALL_ARG2_RET_VAL(func, compose, close_on_success, -1); +} + +void syl_plugin_compose_attach_append(gpointer compose, + const gchar *file, + const gchar *filename, + const gchar *content_type) +{ + void (*func)(gpointer, const gchar *, const gchar *, const gchar *); + + GETFUNC("compose_attach_append"); + SAFE_CALL_ARG4(func, compose, file, filename, content_type); +} + +void syl_plugin_compose_attach_remove_all(gpointer compose) +{ + void (*func)(gpointer); + + GETFUNC("compose_attach_remove_all"); + SAFE_CALL_ARG1(func, compose); +} + +GSList *syl_plugin_get_attach_list(gpointer compose) +{ + GSList * (*func)(gpointer); + + GETFUNC("compose_get_attach_list"); + return SAFE_CALL_ARG1_RET(func, compose); +} + + +FolderItem *syl_plugin_folder_sel(Folder *cur_folder, gint sel_type, + const gchar *default_folder) +{ + FolderItem * (*func)(Folder *, gint, const gchar *); + + func = syl_plugin_lookup_symbol("foldersel_folder_sel"); + return SAFE_CALL_ARG3_RET(func, cur_folder, sel_type, default_folder); +} + +FolderItem *syl_plugin_folder_sel_full(Folder *cur_folder, gint sel_type, + const gchar *default_folder, + const gchar *message) +{ + FolderItem * (*func)(Folder *, gint, const gchar *, const gchar *); + + func = syl_plugin_lookup_symbol("foldersel_folder_sel_full"); + return SAFE_CALL_ARG4_RET(func, cur_folder, sel_type, default_folder, + message); +} + +gchar *syl_plugin_input_dialog(const gchar *title, const gchar *message, + const gchar *default_string) +{ + gchar * (*func)(const gchar *, const gchar *, const gchar *); + + func = syl_plugin_lookup_symbol("input_dialog"); + return SAFE_CALL_ARG3_RET(func, title, message, default_string); +} + +gchar *syl_plugin_input_dialog_with_invisible(const gchar *title, + const gchar *message, + const gchar *default_string) +{ + gchar * (*func)(const gchar *, const gchar *, const gchar *); + + func = syl_plugin_lookup_symbol("input_dialog_with_invisible"); + return SAFE_CALL_ARG3_RET(func, title, message, default_string); +} + +void syl_plugin_manage_window_set_transient(GtkWindow *window) +{ + void (*func)(GtkWindow *); + + func = syl_plugin_lookup_symbol("manage_window_set_transient"); + SAFE_CALL_ARG1(func, window); +} + +void syl_plugin_manage_window_signals_connect(GtkWindow *window) +{ + void (*func)(GtkWindow *); + + func = syl_plugin_lookup_symbol("manage_window_signals_connect"); + SAFE_CALL_ARG1(func, window); +} + +GtkWidget *syl_plugin_manage_window_get_focus_window(void) +{ + GtkWidget * (*func)(void); + + func = syl_plugin_lookup_symbol("manage_window_get_focus_window"); + return SAFE_CALL_RET(func); +} + +void syl_plugin_inc_mail(void) +{ + void (*func)(gpointer); + + func = syl_plugin_lookup_symbol("inc_mail"); + SAFE_CALL_ARG1(func, syl_plugin_main_window_get()); +} + +gboolean syl_plugin_inc_is_active(void) +{ + gboolean (*func)(void); + + func = syl_plugin_lookup_symbol("inc_is_active"); + return SAFE_CALL_RET_VAL(func, FALSE); +} + +void syl_plugin_inc_lock(void) +{ + void (*func)(void); + + func = syl_plugin_lookup_symbol("inc_lock"); + SAFE_CALL(func); +} + +void syl_plugin_inc_unlock(void) +{ + void (*func)(void); + + func = syl_plugin_lookup_symbol("inc_unlock"); + SAFE_CALL(func); +} + +void syl_plugin_update_check(gboolean show_dialog_always) +{ + void (*func)(gboolean); + + func = syl_plugin_lookup_symbol("update_check"); + SAFE_CALL_ARG1(func, show_dialog_always); +} + +void syl_plugin_update_check_set_check_url(const gchar *url) +{ + void (*func)(const gchar *); + + func = syl_plugin_lookup_symbol("update_check_set_check_url"); + SAFE_CALL_ARG1(func, url); +} + +const gchar *syl_plugin_update_check_get_check_url(void) +{ + const gchar * (*func)(void); + + func = syl_plugin_lookup_symbol("update_check_get_check_url"); + return SAFE_CALL_RET(func); +} + +void syl_plugin_update_check_set_download_url(const gchar *url) +{ + void (*func)(const gchar *); + + func = syl_plugin_lookup_symbol("update_check_set_download_url"); + SAFE_CALL_ARG1(func, url); +} + +const gchar *syl_plugin_update_check_get_download_url(void) +{ + const gchar * (*func)(void); + + func = syl_plugin_lookup_symbol("update_check_get_download_url"); + return SAFE_CALL_RET(func); +} + +void syl_plugin_update_check_set_jump_url(const gchar *url) +{ + void (*func)(const gchar *); + + func = syl_plugin_lookup_symbol("update_check_set_jump_url"); + SAFE_CALL_ARG1(func, url); +} + +const gchar *syl_plugin_update_check_get_jump_url(void) +{ + const gchar * (*func)(void); + + func = syl_plugin_lookup_symbol("update_check_get_jump_url"); + return SAFE_CALL_RET(func); +} + +void syl_plugin_update_check_set_check_plugin_url(const gchar *url) +{ + void (*func)(const gchar *); + + func = syl_plugin_lookup_symbol("update_check_set_check_plugin_url"); + SAFE_CALL_ARG1(func, url); +} + +const gchar *syl_plugin_update_check_get_check_plugin_url(void) +{ + const gchar * (*func)(void); + + func = syl_plugin_lookup_symbol("update_check_get_check_plugin_url"); + return SAFE_CALL_RET(func); +} + +void syl_plugin_update_check_set_jump_plugin_url(const gchar *url) +{ + void (*func)(const gchar *); + + func = syl_plugin_lookup_symbol("update_check_set_jump_plugin_url"); + SAFE_CALL_ARG1(func, url); +} + +const gchar *syl_plugin_update_check_get_jump_plugin_url(void) +{ + const gchar * (*func)(void); + + func = syl_plugin_lookup_symbol("update_check_get_jump_plugin_url"); + return SAFE_CALL_RET(func); +} + +gint syl_plugin_alertpanel_full(const gchar *title, const gchar *message, + gint type, gint default_value, + gboolean can_disable, + const gchar *btn1_label, + const gchar *btn2_label, + const gchar *btn3_label) +{ + gint (*func)(const gchar *, const gchar *, gint, gint, gboolean, + const gchar *, const gchar *, const gchar *); + + GETFUNC("alertpanel_full"); + return func ? func(title, message, type, default_value, can_disable, + btn1_label, btn2_label, btn3_label) : -1; +} + +gint syl_plugin_alertpanel(const gchar *title, const gchar *message, + const gchar *btn1_label, + const gchar *btn2_label, + const gchar *btn3_label) +{ + gint (*func)(const gchar *, const gchar *, + const gchar *, const gchar *, const gchar *); + + GETFUNC("alertpanel"); + return func ? func(title, message, btn1_label, btn2_label, btn3_label) + : -1; +} + +void syl_plugin_alertpanel_message(const gchar *title, const gchar *message, + gint type) +{ + void (*func)(const gchar *, const gchar *, gint); + + GETFUNC("alertpanel_message"); + SAFE_CALL_ARG3(func, title, message, type); +} + +gint syl_plugin_alertpanel_message_with_disable(const gchar *title, + const gchar *message, + gint type) +{ + gint (*func)(const gchar *, const gchar *, gint); + + GETFUNC("alertpanel_message_with_disable"); + return SAFE_CALL_ARG3_RET_VAL(func, title, message, type, 0); +} + +gint syl_plugin_send_message(const gchar *file, PrefsAccount *ac, + GSList *to_list) +{ + gint (*func)(const gchar *, PrefsAccount *, GSList *); + + GETFUNC("send_message"); + return SAFE_CALL_ARG3_RET_VAL(func, file, ac, to_list, -1); +} + +gint syl_plugin_send_message_queue_all(FolderItem *queue, gboolean save_msgs, + gboolean filter_msgs) +{ + gint (*func)(FolderItem *, gboolean, gboolean); + + GETFUNC("send_message_queue_all"); + return SAFE_CALL_ARG3_RET_VAL(func, queue, save_msgs, filter_msgs, -1); +} + +gint syl_plugin_send_message_set_reply_flag(const gchar *reply_target, + const gchar *msgid) +{ + gint (*func)(const gchar *, const gchar *); + + GETFUNC("send_message_set_reply_flag"); + return SAFE_CALL_ARG2_RET_VAL(func, reply_target, msgid, -1); +} + +gint syl_plugin_send_message_set_forward_flags(const gchar *forward_targets) +{ + gint (*func)(const gchar *); + + GETFUNC("send_message_set_forward_flags"); + return SAFE_CALL_ARG1_RET_VAL(func, forward_targets, -1); +} + +gint syl_plugin_notification_window_open(const gchar *message, + const gchar *submessage, + guint timeout) +{ + gint (*func)(const gchar *, const gchar *, guint); + + GETFUNC("notification_window_open"); + return SAFE_CALL_ARG3_RET_VAL(func, message, submessage, timeout, -1); +} + +void syl_plugin_notification_window_set_message(const gchar *message, + const gchar *submessage) +{ + void (*func)(const gchar *, const gchar *); + + GETFUNC("notification_window_set_message"); + SAFE_CALL_ARG2(func, message, submessage); +} + +void syl_plugin_notification_window_close(void) +{ + void (*func)(void); + + GETFUNC("notification_window_close"); + SAFE_CALL(func); +} diff --git a/src/plugin.h b/src/plugin.h new file mode 100644 index 0000000..9b28dc5 --- /dev/null +++ b/src/plugin.h @@ -0,0 +1,376 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2016 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PLUGIN_H__ +#define __PLUGIN_H__ + +#include +#include +#include +#include + +#include "procmsg.h" +#include "folder.h" +#include "filter.h" +#include "plugin-types.h" + +/* SylPlugin object */ + +#define SYL_TYPE_PLUGIN (syl_plugin_get_type()) +#define SYL_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SYL_TYPE_PLUGIN, SylPlugin)) +#define SYL_IS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SYL_TYPE_PLUGIN)) +#define SYL_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SYL_TYPE_PLUGIN, SylPluginClass)) +#define SYL_IS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SYL_TYPE_PLUGIN)) +#define SYL_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SYL_TYPE_PLUGIN, SylPluginClass)) + +typedef struct _SylPlugin SylPlugin; +typedef struct _SylPluginClass SylPluginClass; +typedef struct _SylPluginInfo SylPluginInfo; + +typedef void (*SylPluginLoadFunc) (void); +typedef void (*SylPluginUnloadFunc) (void); +typedef void (*SylPluginCallbackFunc) (void); + +#define SYL_PLUGIN_INTERFACE_VERSION 0x010a + +struct _SylPlugin +{ + GObject parent_instance; +}; + +struct _SylPluginClass +{ + GObjectClass parent_class; + + void (* plugin_load) (GObject *obj, GModule *module); + void (* plugin_unload) (GObject *obj, GModule *module); + + void (* folderview_menu_popup) (GObject *obj, gpointer ifactory); + void (* summaryview_menu_popup) (GObject *obj, gpointer ifactory); + + void (* compose_created) (GObject *obj, gpointer compose); + void (* compose_destroy) (GObject *obj, gpointer compose); + + void (* textview_menu_popup) (GObject *obj, + GtkMenu *menu, + GtkTextView *textview, + const gchar *uri, + const gchar *selected_text, + MsgInfo *msginfo); + + gboolean (* compose_send) (GObject *obj, + gpointer compose, + gint compose_mode, + gint send_mode, + const gchar *msg_file, + GSList *to_list); + + void (* messageview_show) (GObject *obj, + gpointer msgview, + MsgInfo *msginfo, + gboolean all_headers); + + void (* inc_mail_start) (GObject *obj, + PrefsAccount *account); + void (* inc_mail_finished) (GObject *obj, + gint new_messages); + + /* Prefs dialogs */ + void (* prefs_common_open) (GObject *obj, + GtkWidget *window); + void (* prefs_account_open) (GObject *obj, + PrefsAccount *account, + GtkWidget *window); + void (* prefs_filter_open) (GObject *obj, + GtkWidget *window); + void (* prefs_filter_edit_open) (GObject *obj, + FilterRule *rule, + const gchar *header, + const gchar *key, + GtkWidget *window); + void (* prefs_template_open) (GObject *obj, + GtkWidget *window); + void (* plugin_manager_open) (GObject *obj, + GtkWidget *window); + + void (* main_window_toolbar_changed) (GObject *obj); + void (* compose_toolbar_changed) (GObject *obj, gpointer compose); + void (* compose_attach_changed) (GObject *obj, gpointer compose); +}; + +struct _SylPluginInfo +{ + gchar *name; + gchar *version; + gchar *author; + gchar *description; + + gpointer pad1; + gpointer pad2; + gpointer pad3; + gpointer pad4; +}; + +GType syl_plugin_get_type (void); + +void syl_plugin_signal_connect (const gchar *name, GCallback callback, + gpointer data); +void syl_plugin_signal_disconnect (gpointer func, gpointer data); +void syl_plugin_signal_emit (const gchar *name, ...); + +/* Used by Sylpheed */ + +gint syl_plugin_init_lib (void); + +gint syl_plugin_load (const gchar *file); +gint syl_plugin_load_all (const gchar *dir); +void syl_plugin_unload_all (void); + +GSList *syl_plugin_get_module_list (void); +SylPluginInfo *syl_plugin_get_info (GModule *module); +gboolean syl_plugin_check_version (GModule *module); + +gint syl_plugin_add_symbol (const gchar *name, gpointer sym); +gpointer syl_plugin_lookup_symbol (const gchar *name); + +/* Interfaces which should be implemented by plug-ins + void plugin_load(void); + void plugin_unload(void); + SylPluginInfo *plugin_info(void); + gint plugin_interface_version(void); + */ + +/* Plug-in API (used by plug-ins) */ + +const gchar *syl_plugin_get_prog_version (void); + +void syl_plugin_main_window_lock (void); +void syl_plugin_main_window_unlock (void); +gpointer syl_plugin_main_window_get (void); +void syl_plugin_main_window_popup (gpointer mainwin); +GtkWidget *syl_plugin_main_window_get_toolbar (void); +GtkWidget *syl_plugin_main_window_get_statusbar (void); + +void syl_plugin_app_will_exit (gboolean force); + +/* Menu */ +gint syl_plugin_add_menuitem (const gchar *parent, + const gchar *label, + SylPluginCallbackFunc func, + gpointer data); +gint syl_plugin_add_factory_item (const gchar *menu, + const gchar *label, + SylPluginCallbackFunc func, + gpointer data); + +void syl_plugin_menu_set_sensitive (const gchar *path, + gboolean sensitive); +void syl_plugin_menu_set_sensitive_all (GtkMenuShell *menu_shell, + gboolean sensitive); +void syl_plugin_menu_set_active (const gchar *path, + gboolean is_active); + + +/* FolderView */ +gpointer syl_plugin_folderview_get (void); + +void syl_plugin_folderview_add_sub_widget (GtkWidget *widget); + +void syl_plugin_folderview_select (FolderItem *item); +void syl_plugin_folderview_unselect (void); +void syl_plugin_folderview_select_next_unread (void); +FolderItem *syl_plugin_folderview_get_selected_item + (void); + +gint syl_plugin_folderview_check_new (Folder *folder); +gint syl_plugin_folderview_check_new_item (FolderItem *item); +gint syl_plugin_folderview_check_new_all (void); + +void syl_plugin_folderview_update_item (FolderItem *item, + gboolean update_summary); +void syl_plugin_folderview_update_item_foreach (GHashTable *table, + gboolean update_summary); +void syl_plugin_folderview_update_all_updated (gboolean update_summary); +void syl_plugin_folderview_check_new_selected (void); + +/* SummaryView */ +gpointer syl_plugin_summary_view_get (void); +void syl_plugin_summary_select_by_msgnum (guint msgnum); +gboolean syl_plugin_summary_select_by_msginfo (MsgInfo *msginfo); + +void syl_plugin_open_message (const gchar *folder_id, + guint msgnum); + +void syl_plugin_summary_show_queued_msgs (void); + +void syl_plugin_summary_lock (void); +void syl_plugin_summary_unlock (void); +gboolean syl_plugin_summary_is_locked (void); +gboolean syl_plugin_summary_is_read_locked (void); +void syl_plugin_summary_write_lock (void); +void syl_plugin_summary_write_unlock (void); +gboolean syl_plugin_summary_is_write_locked (void); + +FolderItem *syl_plugin_summary_get_current_folder + (void); + +gint syl_plugin_summary_get_selection_type (void); +GSList *syl_plugin_summary_get_selected_msg_list(void); +GSList *syl_plugin_summary_get_msg_list (void); + +void syl_plugin_summary_redisplay_msg (void); +void syl_plugin_summary_open_msg (void); +void syl_plugin_summary_view_source (void); +void syl_plugin_summary_reedit (void); + +void syl_plugin_summary_update_selected_rows (void); +void syl_plugin_summary_update_by_msgnum (guint msgnum); + +/* MessageView */ +gpointer syl_plugin_messageview_create_with_new_window + (void); +void syl_plugin_open_message_by_new_window (MsgInfo *msginfo); + +/* Compose */ +gpointer syl_plugin_compose_new (PrefsAccount *account, + FolderItem *item, + const gchar *mailto, + GPtrArray *attach_files); +/* compose mode: + 1: reply 2: reply to sender 3: reply to all 4: reply to list + | 1 << 16: with quote */ +gpointer syl_plugin_compose_reply (MsgInfo *msginfo, + FolderItem *item, + gint mode, + const gchar *body); +gpointer syl_plugin_compose_forward (GSList *mlist, + FolderItem *item, + gboolean as_attach, + const gchar *body); +gpointer syl_plugin_compose_redirect (MsgInfo *msginfo, + FolderItem *item); +gpointer syl_plugin_compose_reedit (MsgInfo *msginfo); + +/* entry type: + 0: To 1: Cc 2: Bcc 3: Reply-To 4: Subject 5: Newsgroups 6: Followup-To */ +void syl_plugin_compose_entry_set (gpointer compose, + const gchar *text, + gint type); +void syl_plugin_compose_entry_append (gpointer compose, + const gchar *text, + gint type); +gchar *syl_plugin_compose_entry_get_text (gpointer compose, + gint type); +void syl_plugin_compose_lock (gpointer compose); +void syl_plugin_compose_unlock (gpointer compose); + +GtkWidget *syl_plugin_compose_get_toolbar (gpointer compose); +GtkWidget *syl_plugin_compose_get_misc_hbox (gpointer compose); +GtkWidget *syl_plugin_compose_get_textview (gpointer compose); + +gint syl_plugin_compose_send (gpointer compose, + gboolean close_on_success); +void syl_plugin_compose_attach_append (gpointer compose, + const gchar *file, + const gchar *filename, + const gchar *content_type); +void syl_plugin_compose_attach_remove_all (gpointer compose); +/* returns list of SylPluginAttachInfo (GSList must be freed by caller) */ +GSList *syl_plugin_get_attach_list (gpointer compose); + +/* Others */ +FolderItem *syl_plugin_folder_sel (Folder *cur_folder, + gint sel_type, + const gchar *default_folder); +FolderItem *syl_plugin_folder_sel_full (Folder *cur_folder, + gint sel_type, + const gchar *default_folder, + const gchar *message); + +gchar *syl_plugin_input_dialog (const gchar *title, + const gchar *message, + const gchar *default_string); +gchar *syl_plugin_input_dialog_with_invisible (const gchar *title, + const gchar *message, + const gchar *default_string); + +void syl_plugin_manage_window_set_transient (GtkWindow *window); +void syl_plugin_manage_window_signals_connect (GtkWindow *window); +GtkWidget *syl_plugin_manage_window_get_focus_window + (void); + +void syl_plugin_inc_mail (void); +gboolean syl_plugin_inc_is_active (void); +void syl_plugin_inc_lock (void); +void syl_plugin_inc_unlock (void); + +void syl_plugin_update_check (gboolean show_dialog_always); +void syl_plugin_update_check_set_check_url (const gchar *url); +const gchar *syl_plugin_update_check_get_check_url (void); +void syl_plugin_update_check_set_download_url (const gchar *url); +const gchar *syl_plugin_update_check_get_download_url (void); +void syl_plugin_update_check_set_jump_url (const gchar *url); +const gchar *syl_plugin_update_check_get_jump_url (void); +void syl_plugin_update_check_set_check_plugin_url (const gchar *url); +const gchar *syl_plugin_update_check_get_check_plugin_url(void); +void syl_plugin_update_check_set_jump_plugin_url (const gchar *url); +const gchar *syl_plugin_update_check_get_jump_plugin_url(void); + +/* type corresponds to AlertType + * default_value and return value corresponds to AlertValue */ +gint syl_plugin_alertpanel_full (const gchar *title, + const gchar *message, + gint type, + gint default_value, + gboolean can_disable, + const gchar *btn1_label, + const gchar *btn2_label, + const gchar *btn3_label); +gint syl_plugin_alertpanel (const gchar *title, + const gchar *message, + const gchar *btn1_label, + const gchar *btn2_label, + const gchar *btn3_label); +void syl_plugin_alertpanel_message (const gchar *title, + const gchar *message, + gint type); +gint syl_plugin_alertpanel_message_with_disable (const gchar *title, + const gchar *message, + gint type); + +/* Send message */ +gint syl_plugin_send_message (const gchar *file, + PrefsAccount *ac, + GSList *to_list); +gint syl_plugin_send_message_queue_all (FolderItem *queue, + gboolean save_msgs, + gboolean filter_msgs); +gint syl_plugin_send_message_set_reply_flag (const gchar *reply_target, + const gchar *msgid); +gint syl_plugin_send_message_set_forward_flags (const gchar *forward_targets); + +/* Notification window */ +gint syl_plugin_notification_window_open (const gchar *message, + const gchar *submessage, + guint timeout); +void syl_plugin_notification_window_set_message (const gchar *message, + const gchar *submessage); +void syl_plugin_notification_window_close (void); + +#endif /* __PLUGIN_H__ */ diff --git a/src/plugin_manager.c b/src/plugin_manager.c new file mode 100644 index 0000000..793fa5b --- /dev/null +++ b/src/plugin_manager.c @@ -0,0 +1,255 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2012 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include + +#include "plugin.h" +#include "plugin_manager.h" +#include "manage_window.h" +#include "alertpanel.h" +#include "gtkutils.h" +#include "update_check.h" +#include "utils.h" + +static struct PluginManagerWindow { + GtkWidget *window; + GtkWidget *close_btn; + + GtkWidget *treeview; + GtkListStore *store; + GtkTreeSelection *selection; +} pm_window; + +enum { + COL_INFO, + N_COLS +}; + +static void plugin_manager_create (void); +static void plugin_manager_set_list_row (GtkTreeIter *iter, + SylPluginInfo *info, + const gchar *filename); +static gint plugin_manager_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); + +void plugin_manager_open(void) +{ + GSList *list, *cur; + SylPluginInfo *info; + GModule *module; + const gchar *filename; + + if (!pm_window.window) + plugin_manager_create(); + else + gtk_window_present(GTK_WINDOW(pm_window.window)); + + list = syl_plugin_get_module_list(); + + gtk_list_store_clear(pm_window.store); + + for (cur = list; cur != NULL; cur = cur->next) { + module = (GModule *)cur->data; + + filename = g_module_name(module); + info = syl_plugin_get_info(module); + if (info) { + debug_print("------------------------------\n"); + debug_print("filename : %s\n", filename); + debug_print("plugin name : %s\n", info->name); + debug_print("plugin version: %s\n", info->version); + debug_print("plugin author : %s\n", info->author); + debug_print("description : %s\n", info->description ? info->description : ""); + debug_print("------------------------------\n"); + plugin_manager_set_list_row(NULL, info, filename); + } else { + debug_print("info not found: %s\n", filename); + } + } + + gtk_widget_show(pm_window.window); + manage_window_focus_in(pm_window.window, NULL, NULL); + + syl_plugin_signal_emit("plugin-manager-open", pm_window.window); +} + +#ifdef USE_UPDATE_CHECK_PLUGIN +static gint plugin_manager_update_check(void) +{ + update_check_plugin(TRUE); + return TRUE; +} +#endif /* USE_UPDATE_CHECK_PLUGIN */ + +static void plugin_manager_create(void) +{ + GtkWidget *window; + GtkWidget *vbox; +#ifdef USE_UPDATE_CHECK_PLUGIN + GtkWidget *update_check_btn; +#endif + GtkWidget *close_btn; + GtkWidget *confirm_area; + + GtkWidget *scrolledwin; + GtkWidget *treeview; + GtkListStore *store; + GtkTreeSelection *selection; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), _("Plug-in manager")); + gtk_widget_set_size_request(window, 600, 400); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, TRUE); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + + vbox = gtk_vbox_new(FALSE, 6); + gtk_widget_show(vbox); + gtk_container_add(GTK_CONTAINER(window), vbox); + + gtkut_stock_button_set_create(&confirm_area, +#ifdef USE_UPDATE_CHECK_PLUGIN + &update_check_btn, _("Check for _update"), + &close_btn, GTK_STOCK_CLOSE, + NULL, NULL); + gtkut_box_set_reverse_order(GTK_BOX(confirm_area), TRUE); +#else + &close_btn, GTK_STOCK_CLOSE, + NULL, NULL, + NULL, NULL); +#endif + gtk_widget_show(confirm_area); + gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default(close_btn); + + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(plugin_manager_deleted), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(key_pressed), NULL); +#ifdef USE_UPDATE_CHECK_PLUGIN + g_signal_connect(G_OBJECT(update_check_btn), "clicked", + G_CALLBACK(plugin_manager_update_check), NULL); +#endif + g_signal_connect(G_OBJECT(close_btn), "clicked", + G_CALLBACK(plugin_manager_deleted), NULL); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(scrolledwin); + gtk_widget_set_size_request(scrolledwin, -1, -1); + gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + + store = gtk_list_store_new(N_COLS, G_TYPE_STRING); + + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), COL_INFO); +#if GTK_CHECK_VERSION(2, 10, 0) + gtk_tree_view_set_grid_lines(GTK_TREE_VIEW(treeview), + GTK_TREE_VIEW_GRID_LINES_HORIZONTAL); +#endif + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes + (_("Plug-in information"), renderer, "text", COL_INFO, NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + gtk_widget_show(treeview); + gtk_container_add(GTK_CONTAINER(scrolledwin), treeview); + + gtk_widget_show_all(window); + + pm_window.window = window; + pm_window.close_btn = close_btn; + + pm_window.treeview = treeview; + pm_window.store = store; + pm_window.selection = selection; +} + +static void plugin_manager_set_list_row(GtkTreeIter *iter, SylPluginInfo *info, + const gchar *filename) +{ + GtkListStore *store = pm_window.store; + GtkTreeIter iter_; + gchar *plugin_info; + + g_return_if_fail(info != NULL); + g_return_if_fail(filename != NULL); + + plugin_info = g_strconcat(info->name ? info->name : _("(Unknown)"), + " ", info->version ? info->version : "", "\n", + _("Author: "), info->author ? info->author : _("(Unknown)"), "\n", + _("File: "), filename ? filename : _("(Unknown)"), + info->description ? "\n" : "", + info->description ? _("Description: ") : "", + info->description ? info->description : "", + NULL); + + if (iter) + iter_ = *iter; + else + gtk_list_store_append(store, &iter_); + + gtk_list_store_set(store, &iter_, COL_INFO, plugin_info, -1); + + g_free(plugin_info); +} + +static gint plugin_manager_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + gtk_widget_hide(pm_window.window); + return TRUE; +} + +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) { + gtk_widget_hide(pm_window.window); + return TRUE; + } + + return FALSE; +} diff --git a/src/plugin_manager.h b/src/plugin_manager.h new file mode 100644 index 0000000..c139de2 --- /dev/null +++ b/src/plugin_manager.h @@ -0,0 +1,29 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2009 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PLUGIN_MANAGER_H__ +#define __PLUGIN_MANAGER_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +void plugin_manager_open(void); + +#endif /* __PLUGIN_MANAGER_H__ */ diff --git a/src/prefs_account_dialog.c b/src/prefs_account_dialog.c new file mode 100644 index 0000000..e9cba13 --- /dev/null +++ b/src/prefs_account_dialog.c @@ -0,0 +1,2723 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2017 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "prefs.h" +#include "prefs_ui.h" +#include "prefs_account.h" +#include "prefs_account_dialog.h" +#include "prefs_customheader.h" +#include "prefs_common.h" +#include "account.h" +#include "mainwindow.h" +#include "manage_window.h" +#include "foldersel.h" +#include "inc.h" +#include "menu.h" +#include "gtkutils.h" +#include "utils.h" +#include "alertpanel.h" +#include "smtp.h" +#include "imap.h" +#include "plugin.h" + +static gboolean cancelled; +static gboolean new_account; +static PrefsDialog dialog; + +static struct Basic { + GtkWidget *acname_entry; + GtkWidget *default_chkbtn; + + GtkWidget *name_entry; + GtkWidget *addr_entry; + GtkWidget *org_entry; + + GtkWidget *serv_frame; + GtkWidget *serv_table; + GtkWidget *protocol_optmenu; + GtkWidget *recvserv_label; + GtkWidget *smtpserv_label; + GtkWidget *nntpserv_label; + GtkWidget *recvserv_entry; + GtkWidget *smtpserv_entry; + GtkWidget *nntpserv_entry; + GtkWidget *nntpauth_chkbtn; + GtkWidget *uid_label; + GtkWidget *pass_label; + GtkWidget *uid_entry; + GtkWidget *pass_entry; +} basic; + +static struct Receive { + GtkWidget *pop3_frame; + GtkWidget *use_apop_chkbtn; + GtkWidget *rmmail_chkbtn; + GtkWidget *leave_time_entry; + GtkWidget *getall_chkbtn; + GtkWidget *size_limit_chkbtn; + GtkWidget *size_limit_entry; + GtkWidget *filter_on_recv_chkbtn; + GtkWidget *inbox_label; + GtkWidget *inbox_entry; + GtkWidget *inbox_btn; + + GtkWidget *imap_frame; + GtkWidget *imap_auth_type_optmenu; + GtkWidget *imap_check_inbox_chkbtn; + GtkWidget *imap_filter_inbox_chkbtn; + + GtkWidget *nntp_frame; + GtkWidget *maxarticle_spinbtn; + GtkObject *maxarticle_spinbtn_adj; + + GtkWidget *recvatgetall_chkbtn; +} receive; + +static struct Send { + GtkWidget *date_chkbtn; + GtkWidget *msgid_chkbtn; + + GtkWidget *customhdr_chkbtn; + + GtkWidget *smtp_auth_chkbtn; + GtkWidget *smtp_auth_type_optmenu; + GtkWidget *smtp_uid_entry; + GtkWidget *smtp_pass_entry; + GtkWidget *pop_bfr_smtp_chkbtn; +} p_send; + +static struct Compose { + GtkWidget *sig_radiobtn; + GtkWidget *sig_combo; + GtkWidget *signame_entry; + GtkWidget *sig_text; + GtkTextBuffer *sig_buffer; + GtkWidget *sigpath_entry; + GtkWidget *sig_before_quote_chkbtn; + + GtkWidget *autocc_chkbtn; + GtkWidget *autocc_entry; + GtkWidget *autobcc_chkbtn; + GtkWidget *autobcc_entry; + GtkWidget *autoreplyto_chkbtn; + GtkWidget *autoreplyto_entry; + + gboolean sig_modified; + gchar *sig_names[10]; + gchar *sig_texts[10]; + gint sig_selected; +} compose; + +#if USE_GPGME +static struct Privacy { + GtkWidget *default_sign_chkbtn; + GtkWidget *default_encrypt_chkbtn; + GtkWidget *encrypt_reply_chkbtn; + GtkWidget *encrypt_to_self_chkbtn; + GtkWidget *ascii_armored_chkbtn; + GtkWidget *clearsign_chkbtn; + GtkWidget *defaultkey_radiobtn; + GtkWidget *emailkey_radiobtn; + GtkWidget *customkey_radiobtn; + GtkWidget *customkey_entry; +} privacy; +#endif /* USE_GPGME */ + +#if USE_SSL +static struct SSLPrefs { + GtkWidget *pop_frame; + GtkWidget *pop_nossl_radiobtn; + GtkWidget *pop_ssltunnel_radiobtn; + GtkWidget *pop_starttls_radiobtn; + + GtkWidget *imap_frame; + GtkWidget *imap_nossl_radiobtn; + GtkWidget *imap_ssltunnel_radiobtn; + GtkWidget *imap_starttls_radiobtn; + + GtkWidget *nntp_frame; + GtkWidget *nntp_nossl_radiobtn; + GtkWidget *nntp_ssltunnel_radiobtn; + + GtkWidget *send_frame; + GtkWidget *smtp_nossl_radiobtn; + GtkWidget *smtp_ssltunnel_radiobtn; + GtkWidget *smtp_starttls_radiobtn; + + GtkWidget *use_nonblocking_ssl_chkbtn; +} ssl; +#endif /* USE_SSL */ + +static struct ProxyPrefs { + GtkWidget *socks_chkbtn; + GtkWidget *socks4_radiobtn; + GtkWidget *socks5_radiobtn; + GtkWidget *socks_host_entry; + GtkWidget *socks_port_entry; + GtkWidget *socks_auth_chkbtn; + GtkWidget *socks_name_entry; + GtkWidget *socks_pass_entry; + GtkWidget *socks_send_chkbtn; +} p_proxy; + +static struct Advanced { + GtkWidget *smtpport_chkbtn; + GtkWidget *smtpport_entry; + GtkWidget *popport_hbox; + GtkWidget *popport_chkbtn; + GtkWidget *popport_entry; + GtkWidget *imapport_hbox; + GtkWidget *imapport_chkbtn; + GtkWidget *imapport_entry; + GtkWidget *nntpport_hbox; + GtkWidget *nntpport_chkbtn; + GtkWidget *nntpport_entry; + GtkWidget *domain_chkbtn; + GtkWidget *domain_entry; + + GtkWidget *imap_frame; + GtkWidget *imapdir_entry; + GtkWidget *clear_cache_chkbtn; + + GtkWidget *sent_folder_chkbtn; + GtkWidget *sent_folder_entry; + GtkWidget *draft_folder_chkbtn; + GtkWidget *draft_folder_entry; + GtkWidget *queue_folder_chkbtn; + GtkWidget *queue_folder_entry; + GtkWidget *trash_folder_chkbtn; + GtkWidget *trash_folder_entry; +} advanced; + +static void prefs_account_protocol_set_data_from_optmenu(PrefParam *pparam); +static void prefs_account_protocol_set_optmenu (PrefParam *pparam); +static void prefs_account_protocol_activated (GtkMenuItem *menuitem); + +static void prefs_account_imap_auth_type_set_data_from_optmenu + (PrefParam *pparam); +static void prefs_account_imap_auth_type_set_optmenu (PrefParam *pparam); +static void prefs_account_smtp_auth_type_set_data_from_optmenu + (PrefParam *pparam); +static void prefs_account_smtp_auth_type_set_optmenu (PrefParam *pparam); + +static void prefs_account_enum_set_data_from_radiobtn (PrefParam *pparam); +static void prefs_account_enum_set_radiobtn (PrefParam *pparam); + +#if USE_GPGME +static void prefs_account_ascii_armored_warning (GtkWidget *widget); +#endif /* USE_GPGME */ + +static PrefsUIData ui_data[] = { + /* Basic */ + {"account_name", &basic.acname_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"is_default", &basic.default_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"name", &basic.name_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"address", &basic.addr_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"organization", &basic.org_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"protocol", &basic.protocol_optmenu, + prefs_account_protocol_set_data_from_optmenu, + prefs_account_protocol_set_optmenu}, + {"receive_server", &basic.recvserv_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"smtp_server", &basic.smtpserv_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"nntp_server", &basic.nntpserv_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"use_nntp_auth", &basic.nntpauth_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"user_id", &basic.uid_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"password", &basic.pass_entry, + prefs_set_data_from_entry, prefs_set_entry}, + + /* Receive */ + {"use_apop_auth", &receive.use_apop_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"remove_mail", &receive.rmmail_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"message_leave_time", &receive.leave_time_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"get_all_mail", &receive.getall_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"enable_size_limit", &receive.size_limit_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"size_limit", &receive.size_limit_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"filter_on_receive", &receive.filter_on_recv_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"inbox", &receive.inbox_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"imap_check_inbox_only", &receive.imap_check_inbox_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"imap_filter_inbox_on_receive", &receive.imap_filter_inbox_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"imap_auth_method", &receive.imap_auth_type_optmenu, + prefs_account_imap_auth_type_set_data_from_optmenu, + prefs_account_imap_auth_type_set_optmenu}, + {"max_nntp_articles", &receive.maxarticle_spinbtn, + prefs_set_data_from_spinbtn, prefs_set_spinbtn}, + {"receive_at_get_all", &receive.recvatgetall_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + + /* Send */ + {"add_date", &p_send.date_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"generate_msgid", &p_send.msgid_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"add_custom_header", &p_send.customhdr_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"use_smtp_auth", &p_send.smtp_auth_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"smtp_auth_method", &p_send.smtp_auth_type_optmenu, + prefs_account_smtp_auth_type_set_data_from_optmenu, + prefs_account_smtp_auth_type_set_optmenu}, + {"smtp_user_id", &p_send.smtp_uid_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"smtp_password", &p_send.smtp_pass_entry, + prefs_set_data_from_entry, prefs_set_entry}, + + {"pop_before_smtp", &p_send.pop_bfr_smtp_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + + /* Compose */ + {"signature_type", &compose.sig_radiobtn, + prefs_account_enum_set_data_from_radiobtn, + prefs_account_enum_set_radiobtn}, + {"signature_path", &compose.sigpath_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"signature_before_quote", &compose.sig_before_quote_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"set_autocc", &compose.autocc_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"auto_cc", &compose.autocc_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"set_autobcc", &compose.autobcc_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"auto_bcc", &compose.autobcc_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"set_autoreplyto", &compose.autoreplyto_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"auto_replyto", &compose.autoreplyto_entry, + prefs_set_data_from_entry, prefs_set_entry}, + +#if USE_GPGME + /* Privacy */ + {"default_sign", &privacy.default_sign_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"default_encrypt", &privacy.default_encrypt_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"encrypt_reply", &privacy.encrypt_reply_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"encrypt_to_self", &privacy.encrypt_to_self_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"ascii_armored", &privacy.ascii_armored_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"clearsign", &privacy.clearsign_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"sign_key", &privacy.defaultkey_radiobtn, + prefs_account_enum_set_data_from_radiobtn, + prefs_account_enum_set_radiobtn}, + {"sign_key_id", &privacy.customkey_entry, + prefs_set_data_from_entry, prefs_set_entry}, +#endif /* USE_GPGME */ + +#if USE_SSL + /* SSL */ + {"ssl_pop", &ssl.pop_nossl_radiobtn, + prefs_account_enum_set_data_from_radiobtn, + prefs_account_enum_set_radiobtn}, + {"ssl_imap", &ssl.imap_nossl_radiobtn, + prefs_account_enum_set_data_from_radiobtn, + prefs_account_enum_set_radiobtn}, + {"ssl_nntp", &ssl.nntp_nossl_radiobtn, + prefs_account_enum_set_data_from_radiobtn, + prefs_account_enum_set_radiobtn}, + {"ssl_smtp", &ssl.smtp_nossl_radiobtn, + prefs_account_enum_set_data_from_radiobtn, + prefs_account_enum_set_radiobtn}, + {"use_nonblocking_ssl", &ssl.use_nonblocking_ssl_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, +#endif /* USE_SSL */ + + /* Proxy */ + {"use_socks", &p_proxy.socks_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"socks_type", &p_proxy.socks4_radiobtn, + prefs_account_enum_set_data_from_radiobtn, + prefs_account_enum_set_radiobtn}, + {"proxy_host", &p_proxy.socks_host_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"proxy_port", &p_proxy.socks_port_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"use_proxy_auth", &p_proxy.socks_auth_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"proxy_name", &p_proxy.socks_name_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"proxy_pass", &p_proxy.socks_pass_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"use_socks_for_send", &p_proxy.socks_send_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + + /* Advanced */ + {"set_smtpport", &advanced.smtpport_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"smtp_port", &advanced.smtpport_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"set_popport", &advanced.popport_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"pop_port", &advanced.popport_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"set_imapport", &advanced.imapport_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"imap_port", &advanced.imapport_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"set_nntpport", &advanced.nntpport_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"nntp_port", &advanced.nntpport_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"set_domain", &advanced.domain_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"domain", &advanced.domain_entry, + prefs_set_data_from_entry, prefs_set_entry}, + + {"imap_directory", &advanced.imapdir_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"imap_clear_cache_on_exit", &advanced.clear_cache_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + + {"set_sent_folder", &advanced.sent_folder_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"sent_folder", &advanced.sent_folder_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"set_draft_folder", &advanced.draft_folder_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"draft_folder", &advanced.draft_folder_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"set_queue_folder", &advanced.queue_folder_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"queue_folder", &advanced.queue_folder_entry, + prefs_set_data_from_entry, prefs_set_entry}, + {"set_trash_folder", &advanced.trash_folder_chkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"trash_folder", &advanced.trash_folder_entry, + prefs_set_data_from_entry, prefs_set_entry}, + + {NULL, NULL, NULL, NULL} +}; + +static void prefs_account_create (void); +static void prefs_account_basic_create (void); +static void prefs_account_receive_create (void); +static void prefs_account_send_create (void); +static void prefs_account_compose_create (void); +#if USE_GPGME +static void prefs_account_privacy_create (void); +#endif /* USE_GPGME */ +#if USE_SSL +static void prefs_account_ssl_create (void); +#endif /* USE_SSL */ +static void prefs_account_proxy_create (void); +static void prefs_account_advanced_create (void); + +static void prefs_account_select_folder_cb (GtkWidget *widget, + gpointer data); +static void prefs_account_edit_custom_header (void); + +static void prefs_account_name_entry_changed_cb (GtkWidget *widget, + gpointer data); +static void prefs_account_sig_changed_cb (GtkWidget *widget, + gpointer data); +static void prefs_account_sig_combo_changed_cb (GtkWidget *widget, + gpointer data); + +static gint prefs_account_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean prefs_account_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); +static void prefs_account_ok (void); +static gint prefs_account_apply (void); +static void prefs_account_cancel (void); + + +PrefsAccount *prefs_account_open(PrefsAccount *ac_prefs) +{ + static gboolean ui_registered = FALSE; + + debug_print(_("Opening account preferences window...\n")); + + inc_lock(); + + cancelled = FALSE; + new_account = FALSE; + compose.sig_modified = FALSE; + + if (!ui_registered) { + prefs_register_ui(prefs_account_get_params(), ui_data); + ui_registered = TRUE; + } + + if (!ac_prefs) { + ac_prefs = prefs_account_new(); + new_account = TRUE; + } + + if (!dialog.window) { + prefs_account_create(); + } + + gtkut_box_set_reverse_order(GTK_BOX(dialog.confirm_area), + !prefs_common.comply_gnome_hig); + manage_window_set_transient(GTK_WINDOW(dialog.window)); + gtk_notebook_set_current_page(GTK_NOTEBOOK(dialog.notebook), 0); + gtk_widget_grab_focus(dialog.ok_btn); + + prefs_account_set_tmp_prefs(ac_prefs); + + if (new_account) { + PrefsAccount *def_ac; + gchar *buf; + + g_signal_handlers_block_by_func + (G_OBJECT(compose.sig_buffer), + G_CALLBACK(prefs_account_sig_changed_cb), NULL); + + compose.sig_modified = TRUE; + prefs_set_dialog_to_default(prefs_account_get_params()); + buf = g_strdup_printf(_("Account%d"), ac_prefs->account_id); + gtk_entry_set_text(GTK_ENTRY(basic.acname_entry), buf); + g_free(buf); + compose.sig_modified = FALSE; + + def_ac = account_get_default(); + if (def_ac) { + gtk_entry_set_text(GTK_ENTRY(basic.name_entry), + def_ac->name ? def_ac->name : ""); + gtk_entry_set_text(GTK_ENTRY(basic.addr_entry), + def_ac->address ? def_ac->address : ""); + gtk_entry_set_text(GTK_ENTRY(basic.org_entry), + def_ac->organization ? def_ac->organization : ""); + } + menu_set_sensitive_all + (GTK_MENU_SHELL + (gtk_option_menu_get_menu + (GTK_OPTION_MENU + (basic.protocol_optmenu))), + TRUE); + + gtk_combo_box_set_active(GTK_COMBO_BOX(compose.sig_combo), 0); + + gtk_window_set_title(GTK_WINDOW(dialog.window), + _("Preferences for new account")); + gtk_widget_hide(dialog.apply_btn); + + g_signal_handlers_unblock_by_func + (G_OBJECT(compose.sig_buffer), + G_CALLBACK(prefs_account_sig_changed_cb), NULL); + } else { + gint i; + + for (i = 0; i < 10; i++) { + g_free(compose.sig_names[i]); + compose.sig_names[i] = g_strdup(ac_prefs->sig_names[i]); + } + g_free(compose.sig_texts[0]); + compose.sig_texts[0] = g_strdup(ac_prefs->sig_text); + for (i = 1; i < 10; i++) { + g_free(compose.sig_texts[i]); + compose.sig_texts[i] = g_strdup(ac_prefs->sig_texts[i]); + } + + gtk_combo_box_set_active(GTK_COMBO_BOX(compose.sig_combo), 0); + gtk_entry_set_text(GTK_ENTRY(compose.signame_entry), + compose.sig_names[0] ? compose.sig_names[0] : ""); + prefs_set_escaped_str_to_text(compose.sig_text, compose.sig_texts[0]); + + prefs_set_dialog(prefs_account_get_params()); + gtk_window_set_title(GTK_WINDOW(dialog.window), + _("Account preferences")); + gtk_widget_show(dialog.apply_btn); + } + + gtk_widget_show(dialog.window); + syl_plugin_signal_emit("prefs-account-open", new_account ? NULL : ac_prefs, dialog.window); + + gtk_main(); + + gtk_widget_hide(dialog.window); + + inc_unlock(); + + if (cancelled && new_account) { + prefs_account_free(ac_prefs); + return NULL; + } else { + prefs_account_apply_tmp_prefs(ac_prefs); + return ac_prefs; + } +} + +static void prefs_account_create(void) +{ + gint page = 0; + + debug_print(_("Creating account preferences window...\n")); + + /* create dialog */ + prefs_dialog_create(&dialog); + g_signal_connect(G_OBJECT(dialog.window), "delete_event", + G_CALLBACK(prefs_account_deleted), NULL); + g_signal_connect(G_OBJECT(dialog.window), "key_press_event", + G_CALLBACK(prefs_account_key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(dialog.window); + + g_signal_connect(G_OBJECT(dialog.ok_btn), "clicked", + G_CALLBACK(prefs_account_ok), NULL); + g_signal_connect(G_OBJECT(dialog.apply_btn), "clicked", + G_CALLBACK(prefs_account_apply), NULL); + g_signal_connect(G_OBJECT(dialog.cancel_btn), "clicked", + G_CALLBACK(prefs_account_cancel), NULL); + + prefs_account_basic_create(); + SET_NOTEBOOK_LABEL(dialog.notebook, _("Basic"), page++); + prefs_account_receive_create(); + SET_NOTEBOOK_LABEL(dialog.notebook, _("Receive"), page++); + prefs_account_send_create(); + SET_NOTEBOOK_LABEL(dialog.notebook, _("Send"), page++); + prefs_account_compose_create(); + SET_NOTEBOOK_LABEL(dialog.notebook, _("Compose"), page++); +#if USE_GPGME + prefs_account_privacy_create(); + SET_NOTEBOOK_LABEL(dialog.notebook, _("Privacy"), page++); +#endif /* USE_GPGME */ +#if USE_SSL + prefs_account_ssl_create(); + SET_NOTEBOOK_LABEL(dialog.notebook, _("SSL"), page++); +#endif /* USE_SSL */ + prefs_account_proxy_create(); + SET_NOTEBOOK_LABEL(dialog.notebook, _("Proxy"), page++); + prefs_account_advanced_create(); + SET_NOTEBOOK_LABEL(dialog.notebook, _("Advanced"), page++); + + g_signal_connect(G_OBJECT(basic.name_entry), "changed", + G_CALLBACK(prefs_account_name_entry_changed_cb), NULL); + g_signal_connect(G_OBJECT(basic.addr_entry), "changed", + G_CALLBACK(prefs_account_name_entry_changed_cb), NULL); + g_signal_connect(G_OBJECT(compose.sig_buffer), "changed", + G_CALLBACK(prefs_account_sig_changed_cb), NULL); +} + +#define SET_ACTIVATE(menuitem) \ +{ \ + g_signal_connect(G_OBJECT(menuitem), "activate", \ + G_CALLBACK(prefs_account_protocol_activated), NULL); \ +} + +static void prefs_account_basic_create(void) +{ + GtkWidget *vbox1; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *acname_entry; + GtkWidget *default_chkbtn; + GtkWidget *frame1; + GtkWidget *table1; + GtkWidget *name_entry; + GtkWidget *addr_entry; + GtkWidget *org_entry; + + GtkWidget *serv_frame; + GtkWidget *vbox2; + GtkWidget *optmenu; + GtkWidget *optmenu_menu; + GtkWidget *menuitem; + GtkWidget *serv_table; + GtkWidget *recvserv_label; + GtkWidget *smtpserv_label; + GtkWidget *nntpserv_label; + GtkWidget *recvserv_entry; + GtkWidget *smtpserv_entry; + GtkWidget *nntpserv_entry; + GtkWidget *nntpauth_chkbtn; + GtkWidget *uid_label; + GtkWidget *pass_label; + GtkWidget *uid_entry; + GtkWidget *pass_entry; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox1), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Name of this account")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + acname_entry = gtk_entry_new (); + gtk_widget_show (acname_entry); + gtk_widget_set_size_request (acname_entry, DEFAULT_ENTRY_WIDTH, -1); + gtk_box_pack_start (GTK_BOX (hbox), acname_entry, TRUE, TRUE, 0); + + default_chkbtn = gtk_check_button_new_with_label (_("Set as default")); + gtk_widget_show (default_chkbtn); + gtk_box_pack_end (GTK_BOX (hbox), default_chkbtn, FALSE, FALSE, 0); + + PACK_FRAME (vbox1, frame1, _("Personal information")); + + table1 = gtk_table_new (3, 2, FALSE); + gtk_widget_show (table1); + gtk_container_add (GTK_CONTAINER (frame1), table1); + gtk_container_set_border_width (GTK_CONTAINER (table1), 8); + gtk_table_set_row_spacings (GTK_TABLE (table1), VSPACING_NARROW); + gtk_table_set_col_spacings (GTK_TABLE (table1), 8); + + label = gtk_label_new (_("Full name")); + gtk_widget_show (label); + gtk_table_attach (GTK_TABLE (table1), label, 0, 1, 0, 1, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5); + + label = gtk_label_new (_("Mail address")); + gtk_widget_show (label); + gtk_table_attach (GTK_TABLE (table1), label, 0, 1, 1, 2, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5); + + label = gtk_label_new (_("Organization")); + gtk_widget_show (label); + gtk_table_attach (GTK_TABLE (table1), label, 0, 1, 2, 3, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5); + + name_entry = gtk_entry_new (); + gtk_widget_show (name_entry); + gtk_table_attach (GTK_TABLE (table1), name_entry, 1, 2, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + addr_entry = gtk_entry_new (); + gtk_widget_show (addr_entry); + gtk_table_attach (GTK_TABLE (table1), addr_entry, 1, 2, 1, 2, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + org_entry = gtk_entry_new (); + gtk_widget_show (org_entry); + gtk_table_attach (GTK_TABLE (table1), org_entry, 1, 2, 2, 3, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + PACK_FRAME (vbox1, serv_frame, _("Server information")); + + vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (serv_frame), vbox2); + gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Protocol")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + optmenu = gtk_option_menu_new (); + gtk_widget_show (optmenu); + gtk_box_pack_start (GTK_BOX (hbox), optmenu, FALSE, FALSE, 0); + + optmenu_menu = gtk_menu_new (); + + MENUITEM_ADD (optmenu_menu, menuitem, _("POP3"), A_POP3); + SET_ACTIVATE (menuitem); + MENUITEM_ADD (optmenu_menu, menuitem, _("IMAP4"), A_IMAP4); + SET_ACTIVATE (menuitem); + MENUITEM_ADD (optmenu_menu, menuitem, _("News (NNTP)"), A_NNTP); + SET_ACTIVATE (menuitem); + MENUITEM_ADD (optmenu_menu, menuitem, _("None (local)"), A_LOCAL); + SET_ACTIVATE (menuitem); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (optmenu), optmenu_menu); + + serv_table = gtk_table_new (6, 4, FALSE); + gtk_widget_show (serv_table); + gtk_box_pack_start (GTK_BOX (vbox2), serv_table, FALSE, FALSE, 0); + gtk_table_set_row_spacings (GTK_TABLE (serv_table), VSPACING_NARROW); + gtk_table_set_row_spacing (GTK_TABLE (serv_table), 3, 0); + gtk_table_set_col_spacings (GTK_TABLE (serv_table), 8); + + nntpauth_chkbtn = gtk_check_button_new_with_label + (_("This server requires authentication")); + gtk_widget_show (nntpauth_chkbtn); + gtk_table_attach (GTK_TABLE (serv_table), nntpauth_chkbtn, 0, 4, 4, 5, + GTK_FILL, 0, 0, 0); + + nntpserv_entry = gtk_entry_new (); + gtk_widget_show (nntpserv_entry); + gtk_table_attach (GTK_TABLE (serv_table), nntpserv_entry, 1, 4, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + gtk_table_set_row_spacing (GTK_TABLE (serv_table), 0, 0); + + recvserv_entry = gtk_entry_new (); + gtk_widget_show (recvserv_entry); + gtk_table_attach (GTK_TABLE (serv_table), recvserv_entry, 1, 4, 1, 2, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + smtpserv_entry = gtk_entry_new (); + gtk_widget_show (smtpserv_entry); + gtk_table_attach (GTK_TABLE (serv_table), smtpserv_entry, 1, 4, 2, 3, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + uid_entry = gtk_entry_new (); + gtk_widget_show (uid_entry); + gtk_widget_set_size_request (uid_entry, DEFAULT_ENTRY_WIDTH, -1); + gtk_table_attach (GTK_TABLE (serv_table), uid_entry, 1, 2, 5, 6, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + pass_entry = gtk_entry_new (); + gtk_widget_show (pass_entry); + gtk_widget_set_size_request (pass_entry, DEFAULT_ENTRY_WIDTH, -1); + gtk_table_attach (GTK_TABLE (serv_table), pass_entry, 3, 4, 5, 6, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + gtk_entry_set_visibility (GTK_ENTRY (pass_entry), FALSE); + + nntpserv_label = gtk_label_new (_("News server")); + gtk_widget_show (nntpserv_label); + gtk_table_attach (GTK_TABLE (serv_table), nntpserv_label, 0, 1, 0, 1, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (nntpserv_label), 1, 0.5); + + recvserv_label = gtk_label_new (_("Server for receiving")); + gtk_widget_show (recvserv_label); + gtk_table_attach (GTK_TABLE (serv_table), recvserv_label, 0, 1, 1, 2, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (recvserv_label), 1, 0.5); + + smtpserv_label = gtk_label_new (_("SMTP server (send)")); + gtk_widget_show (smtpserv_label); + gtk_table_attach (GTK_TABLE (serv_table), smtpserv_label, 0, 1, 2, 3, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (smtpserv_label), 1, 0.5); + gtk_table_set_row_spacing (GTK_TABLE (serv_table), 2, 0); + + uid_label = gtk_label_new (_("User ID")); + gtk_widget_show (uid_label); + gtk_table_attach (GTK_TABLE (serv_table), uid_label, 0, 1, 5, 6, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (uid_label), 1, 0.5); + + pass_label = gtk_label_new (_("Password")); + gtk_widget_show (pass_label); + gtk_table_attach (GTK_TABLE (serv_table), pass_label, 2, 3, 5, 6, + 0, 0, 0, 0); + + SET_TOGGLE_SENSITIVITY (nntpauth_chkbtn, uid_label); + SET_TOGGLE_SENSITIVITY (nntpauth_chkbtn, pass_label); + SET_TOGGLE_SENSITIVITY (nntpauth_chkbtn, uid_entry); + SET_TOGGLE_SENSITIVITY (nntpauth_chkbtn, pass_entry); + + basic.acname_entry = acname_entry; + basic.default_chkbtn = default_chkbtn; + + basic.name_entry = name_entry; + basic.addr_entry = addr_entry; + basic.org_entry = org_entry; + + basic.serv_frame = serv_frame; + basic.serv_table = serv_table; + basic.protocol_optmenu = optmenu; + basic.recvserv_label = recvserv_label; + basic.recvserv_entry = recvserv_entry; + basic.smtpserv_label = smtpserv_label; + basic.smtpserv_entry = smtpserv_entry; + basic.nntpserv_label = nntpserv_label; + basic.nntpserv_entry = nntpserv_entry; + basic.nntpauth_chkbtn = nntpauth_chkbtn; + basic.uid_label = uid_label; + basic.pass_label = pass_label; + basic.uid_entry = uid_entry; + basic.pass_entry = pass_entry; +} + +static void prefs_account_receive_create(void) +{ + GtkWidget *vbox1; + GtkWidget *frame1; + GtkWidget *vbox2; + GtkWidget *use_apop_chkbtn; + GtkWidget *rmmail_chkbtn; + GtkWidget *hbox_spc; + GtkWidget *leave_time_label; + GtkWidget *leave_time_entry; + GtkWidget *getall_chkbtn; + GtkWidget *hbox1; + GtkWidget *size_limit_chkbtn; + GtkWidget *size_limit_entry; + GtkWidget *label; + GtkWidget *filter_on_recv_chkbtn; + GtkWidget *vbox3; + GtkWidget *inbox_label; + GtkWidget *inbox_entry; + GtkWidget *inbox_btn; + + GtkWidget *imap_frame; + GtkWidget *optmenu; + GtkWidget *optmenu_menu; + GtkWidget *menuitem; + GtkWidget *imap_check_inbox_chkbtn; + GtkWidget *imap_filter_inbox_chkbtn; + + GtkWidget *nntp_frame; + GtkWidget *maxarticle_label; + GtkWidget *maxarticle_spinbtn; + GtkObject *maxarticle_spinbtn_adj; + GtkWidget *maxarticle_desc_label; + + GtkWidget *recvatgetall_chkbtn; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER); + + PACK_FRAME (vbox1, frame1, _("POP3")); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (frame1), vbox2); + gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); + + PACK_CHECK_BUTTON (vbox2, use_apop_chkbtn, + _("Use secure authentication (APOP)")); + + PACK_CHECK_BUTTON (vbox2, rmmail_chkbtn, + _("Remove messages on server when received")); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0); + + hbox_spc = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox_spc); + gtk_box_pack_start (GTK_BOX (hbox1), hbox_spc, FALSE, FALSE, 0); + gtk_widget_set_size_request (hbox_spc, 12, -1); + + leave_time_label = gtk_label_new (_("Remove after")); + gtk_widget_show (leave_time_label); + gtk_box_pack_start (GTK_BOX (hbox1), leave_time_label, FALSE, FALSE, 0); + + leave_time_entry = gtk_entry_new (); + gtk_widget_show (leave_time_entry); + gtk_widget_set_size_request (leave_time_entry, 64, -1); + gtk_box_pack_start (GTK_BOX (hbox1), leave_time_entry, FALSE, FALSE, 0); + + leave_time_label = gtk_label_new (_("days")); + gtk_widget_show (leave_time_label); + gtk_box_pack_start (GTK_BOX (hbox1), leave_time_label, FALSE, FALSE, 0); + + SET_TOGGLE_SENSITIVITY (rmmail_chkbtn, hbox1); + + PACK_VSPACER(vbox2, vbox3, VSPACING_NARROW_2); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0); + + hbox_spc = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox_spc); + gtk_box_pack_start (GTK_BOX (hbox1), hbox_spc, FALSE, FALSE, 0); + gtk_widget_set_size_request (hbox_spc, 12, -1); + + leave_time_label = gtk_label_new (_("0 days: remove immediately")); + gtk_widget_show (leave_time_label); + gtk_box_pack_start (GTK_BOX (hbox1), leave_time_label, FALSE, FALSE, 0); + gtkut_widget_set_small_font_size (leave_time_label); + + SET_TOGGLE_SENSITIVITY (rmmail_chkbtn, hbox1); + + PACK_VSPACER(vbox2, vbox3, VSPACING_NARROW_2); + + PACK_CHECK_BUTTON (vbox2, getall_chkbtn, + _("Download all messages (including already received) on server")); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (hbox1, size_limit_chkbtn, _("Receive size limit")); + + size_limit_entry = gtk_entry_new (); + gtk_widget_show (size_limit_entry); + gtk_widget_set_size_request (size_limit_entry, 64, -1); + gtk_box_pack_start (GTK_BOX (hbox1), size_limit_entry, FALSE, FALSE, 0); + + label = gtk_label_new (_("KB")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox1), label, FALSE, FALSE, 0); + + SET_TOGGLE_SENSITIVITY (size_limit_chkbtn, size_limit_entry); + + PACK_CHECK_BUTTON (vbox2, filter_on_recv_chkbtn, + _("Filter messages on receiving")); + + PACK_VSPACER(vbox2, vbox3, VSPACING_NARROW_2); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0); + + inbox_label = gtk_label_new (_("Default inbox")); + gtk_widget_show (inbox_label); + gtk_box_pack_start (GTK_BOX (hbox1), inbox_label, FALSE, FALSE, 0); + + inbox_entry = gtk_entry_new (); + gtk_widget_show (inbox_entry); + gtk_widget_set_size_request (inbox_entry, DEFAULT_ENTRY_WIDTH, -1); + gtk_box_pack_start (GTK_BOX (hbox1), inbox_entry, TRUE, TRUE, 0); + + inbox_btn = gtk_button_new_with_label (_(" Select... ")); + gtk_widget_show (inbox_btn); + gtk_box_pack_start (GTK_BOX (hbox1), inbox_btn, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (inbox_btn), "clicked", + G_CALLBACK (prefs_account_select_folder_cb), + inbox_entry); + + PACK_VSPACER(vbox2, vbox3, VSPACING_NARROW_2); + + PACK_SMALL_LABEL + (vbox2, label, + _("Unfiltered messages will be stored in this folder.")); + + PACK_FRAME (vbox1, imap_frame, _("IMAP4")); + + vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (imap_frame), vbox2); + gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0); + + label = gtk_label_new (_("Authentication method")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox1), label, FALSE, FALSE, 0); + + optmenu = gtk_option_menu_new (); + gtk_widget_show (optmenu); + gtk_box_pack_start (GTK_BOX (hbox1), optmenu, FALSE, FALSE, 0); + + optmenu_menu = gtk_menu_new (); + + MENUITEM_ADD (optmenu_menu, menuitem, _("Automatic"), 0); + MENUITEM_ADD (optmenu_menu, menuitem, "LOGIN", IMAP_AUTH_LOGIN); + MENUITEM_ADD (optmenu_menu, menuitem, "PLAIN", IMAP_AUTH_PLAIN); + MENUITEM_ADD (optmenu_menu, menuitem, "CRAM-MD5", IMAP_AUTH_CRAM_MD5); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (optmenu), optmenu_menu); + + PACK_CHECK_BUTTON (vbox2, imap_check_inbox_chkbtn, + _("Only check INBOX on receiving")); + PACK_CHECK_BUTTON (vbox2, imap_filter_inbox_chkbtn, + _("Filter new messages in INBOX on receiving")); + + PACK_FRAME (vbox1, nntp_frame, _("News")); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (nntp_frame), vbox2); + gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0); + + maxarticle_label = gtk_label_new + (_("Maximum number of articles to download")); + gtk_widget_show (maxarticle_label); + gtk_box_pack_start (GTK_BOX (hbox1), maxarticle_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (maxarticle_label), GTK_JUSTIFY_LEFT); + + maxarticle_spinbtn_adj = + gtk_adjustment_new (300, 0, 10000, 10, 100, 0); + maxarticle_spinbtn = gtk_spin_button_new + (GTK_ADJUSTMENT (maxarticle_spinbtn_adj), 10, 0); + gtk_widget_show (maxarticle_spinbtn); + gtk_box_pack_start (GTK_BOX (hbox1), maxarticle_spinbtn, + FALSE, FALSE, 0); + gtk_widget_set_size_request (maxarticle_spinbtn, 64, -1); + gtk_spin_button_set_numeric + (GTK_SPIN_BUTTON (maxarticle_spinbtn), TRUE); + + PACK_SMALL_LABEL (vbox2, maxarticle_desc_label, + _("No limit if 0 is specified.")); + + PACK_CHECK_BUTTON + (vbox1, recvatgetall_chkbtn, + _("`Get all' checks for new messages on this account")); + + receive.pop3_frame = frame1; + receive.use_apop_chkbtn = use_apop_chkbtn; + receive.rmmail_chkbtn = rmmail_chkbtn; + receive.leave_time_entry = leave_time_entry; + receive.getall_chkbtn = getall_chkbtn; + receive.size_limit_chkbtn = size_limit_chkbtn; + receive.size_limit_entry = size_limit_entry; + receive.filter_on_recv_chkbtn = filter_on_recv_chkbtn; + receive.inbox_label = inbox_label; + receive.inbox_entry = inbox_entry; + receive.inbox_btn = inbox_btn; + + receive.imap_frame = imap_frame; + receive.imap_auth_type_optmenu = optmenu; + receive.imap_check_inbox_chkbtn = imap_check_inbox_chkbtn; + receive.imap_filter_inbox_chkbtn = imap_filter_inbox_chkbtn; + + receive.nntp_frame = nntp_frame; + receive.maxarticle_spinbtn = maxarticle_spinbtn; + receive.maxarticle_spinbtn_adj = maxarticle_spinbtn_adj; + + receive.recvatgetall_chkbtn = recvatgetall_chkbtn; +} + +static void prefs_account_send_create(void) +{ + GtkWidget *vbox1; + GtkWidget *vbox2; + GtkWidget *frame; + GtkWidget *date_chkbtn; + GtkWidget *msgid_chkbtn; + GtkWidget *hbox; + GtkWidget *customhdr_chkbtn; + GtkWidget *customhdr_edit_btn; + GtkWidget *vbox3; + GtkWidget *smtp_auth_chkbtn; + GtkWidget *optmenu; + GtkWidget *optmenu_menu; + GtkWidget *menuitem; + GtkWidget *vbox4; + GtkWidget *hbox_spc; + GtkWidget *label; + GtkWidget *smtp_uid_entry; + GtkWidget *smtp_pass_entry; + GtkWidget *vbox_spc; + GtkWidget *pop_bfr_smtp_chkbtn; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER); + + PACK_FRAME (vbox1, frame, _("Header")); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (frame), vbox2); + gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); + + PACK_CHECK_BUTTON (vbox2, date_chkbtn, _("Add Date header field")); + PACK_CHECK_BUTTON (vbox2, msgid_chkbtn, _("Generate Message-ID")); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (hbox, customhdr_chkbtn, + _("Add user-defined header")); + + customhdr_edit_btn = gtk_button_new_with_label (_(" Edit... ")); + gtk_widget_show (customhdr_edit_btn); + gtk_box_pack_start (GTK_BOX (hbox), customhdr_edit_btn, + FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (customhdr_edit_btn), "clicked", + G_CALLBACK (prefs_account_edit_custom_header), + NULL); + + SET_TOGGLE_SENSITIVITY (customhdr_chkbtn, customhdr_edit_btn); + + PACK_FRAME (vbox1, frame, _("Authentication")); + + vbox3 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox3); + gtk_container_add (GTK_CONTAINER (frame), vbox3); + gtk_container_set_border_width (GTK_CONTAINER (vbox3), 8); + + PACK_CHECK_BUTTON (vbox3, smtp_auth_chkbtn, + _("SMTP Authentication (SMTP AUTH)")); + + vbox4 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox4); + gtk_box_pack_start (GTK_BOX (vbox3), vbox4, FALSE, FALSE, 0); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox4), hbox, FALSE, FALSE, 0); + + hbox_spc = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox_spc); + gtk_box_pack_start (GTK_BOX (hbox), hbox_spc, FALSE, FALSE, 0); + gtk_widget_set_size_request (hbox_spc, 12, -1); + + label = gtk_label_new (_("Authentication method")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + optmenu = gtk_option_menu_new (); + gtk_widget_show (optmenu); + gtk_box_pack_start (GTK_BOX (hbox), optmenu, FALSE, FALSE, 0); + + optmenu_menu = gtk_menu_new (); + + MENUITEM_ADD (optmenu_menu, menuitem, _("Automatic"), 0); + MENUITEM_ADD (optmenu_menu, menuitem, "PLAIN", SMTPAUTH_PLAIN); + MENUITEM_ADD (optmenu_menu, menuitem, "LOGIN", SMTPAUTH_LOGIN); + MENUITEM_ADD (optmenu_menu, menuitem, "CRAM-MD5", SMTPAUTH_CRAM_MD5); + MENUITEM_ADD (optmenu_menu, menuitem, "DIGEST-MD5", SMTPAUTH_DIGEST_MD5); + gtk_widget_set_sensitive (menuitem, FALSE); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (optmenu), optmenu_menu); + + PACK_VSPACER(vbox4, vbox_spc, VSPACING_NARROW_2); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox4), hbox, FALSE, FALSE, 0); + + hbox_spc = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox_spc); + gtk_box_pack_start (GTK_BOX (hbox), hbox_spc, FALSE, FALSE, 0); + gtk_widget_set_size_request (hbox_spc, 12, -1); + + label = gtk_label_new (_("User ID")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + smtp_uid_entry = gtk_entry_new (); + gtk_widget_show (smtp_uid_entry); + gtk_widget_set_size_request (smtp_uid_entry, DEFAULT_ENTRY_WIDTH, -1); + gtk_box_pack_start (GTK_BOX (hbox), smtp_uid_entry, TRUE, TRUE, 0); + + label = gtk_label_new (_("Password")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + smtp_pass_entry = gtk_entry_new (); + gtk_widget_show (smtp_pass_entry); + gtk_widget_set_size_request (smtp_pass_entry, DEFAULT_ENTRY_WIDTH, -1); + gtk_box_pack_start (GTK_BOX (hbox), smtp_pass_entry, TRUE, TRUE, 0); + gtk_entry_set_visibility (GTK_ENTRY (smtp_pass_entry), FALSE); + + PACK_VSPACER(vbox4, vbox_spc, VSPACING_NARROW_2); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox4), hbox, FALSE, FALSE, 0); + + hbox_spc = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox_spc); + gtk_box_pack_start (GTK_BOX (hbox), hbox_spc, FALSE, FALSE, 0); + gtk_widget_set_size_request (hbox_spc, 12, -1); + + label = gtk_label_new + (_("If you leave these entries empty, the same " + "user ID and password as receiving will be used.")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtkut_widget_set_small_font_size (label); + + SET_TOGGLE_SENSITIVITY (smtp_auth_chkbtn, vbox4); + + PACK_VSPACER(vbox3, vbox_spc, VSPACING_NARROW_2); + + PACK_CHECK_BUTTON (vbox3, pop_bfr_smtp_chkbtn, + _("Authenticate with POP3 before sending")); + + p_send.date_chkbtn = date_chkbtn; + p_send.msgid_chkbtn = msgid_chkbtn; + p_send.customhdr_chkbtn = customhdr_chkbtn; + + p_send.smtp_auth_chkbtn = smtp_auth_chkbtn; + p_send.smtp_auth_type_optmenu = optmenu; + p_send.smtp_uid_entry = smtp_uid_entry; + p_send.smtp_pass_entry = smtp_pass_entry; + p_send.pop_bfr_smtp_chkbtn = pop_bfr_smtp_chkbtn; +} + +static void prefs_account_compose_create(void) +{ + GtkWidget *vbox1; + GtkWidget *sig_vbox; + GtkWidget *sig_radiobtn; + GtkWidget *sig_hbox; + GtkWidget *sig_combo; + GtkWidget *signame_label; + GtkWidget *signame_entry; + GtkWidget *sigtext_label; + GtkWidget *sigtext_scrwin; + GtkWidget *sig_text; + GtkWidget *sigfile_radiobtn; + GtkWidget *sigcmd_radiobtn; + GtkWidget *sigpath_entry; + GtkWidget *sig_before_quote_chkbtn; + GtkWidget *frame; + GtkWidget *table; + GtkWidget *autocc_chkbtn; + GtkWidget *autocc_entry; + GtkWidget *autobcc_chkbtn; + GtkWidget *autobcc_entry; + GtkWidget *autoreplyto_chkbtn; + GtkWidget *autoreplyto_entry; + gint i; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER); + + PACK_FRAME (vbox1, frame, _("Signature")); + + sig_vbox = gtk_vbox_new (FALSE, VSPACING_NARROW_2); + gtk_widget_show (sig_vbox); + gtk_container_add (GTK_CONTAINER (frame), sig_vbox); + gtk_container_set_border_width (GTK_CONTAINER (sig_vbox), 8); + + sig_radiobtn = gtk_radio_button_new_with_label + (NULL, _("Direct input")); + gtk_widget_show (sig_radiobtn); + gtk_box_pack_start (GTK_BOX (sig_vbox), sig_radiobtn, FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (sig_radiobtn), MENU_VAL_ID, + GINT_TO_POINTER (SIG_DIRECT)); + + sig_hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (sig_hbox); + gtk_box_pack_start (GTK_BOX (sig_vbox), sig_hbox, FALSE, FALSE, 0); + + sig_combo = gtk_combo_box_new_text(); + gtk_widget_show (sig_combo); + gtk_box_pack_start (GTK_BOX (sig_hbox), sig_combo, FALSE, FALSE, 0); + + for (i = 0; i < 10; i++) { + gchar buf[256]; + g_snprintf(buf, sizeof(buf), _("Signature %d"), i + 1); + gtk_combo_box_append_text(GTK_COMBO_BOX(sig_combo), buf); + } + gtk_combo_box_set_active(GTK_COMBO_BOX(sig_combo), 0); + g_signal_connect(GTK_COMBO_BOX(sig_combo), "changed", + G_CALLBACK(prefs_account_sig_combo_changed_cb), NULL); + + signame_label = gtk_label_new(_("Name:")); + gtk_widget_show (signame_label); + gtk_box_pack_start (GTK_BOX (sig_hbox), signame_label, FALSE, FALSE, 0); + + signame_entry = gtk_entry_new (); + gtk_widget_show (signame_entry); + gtk_box_pack_start (GTK_BOX (sig_hbox), signame_entry, TRUE, TRUE, 0); + + sig_hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (sig_hbox); + gtk_box_pack_start (GTK_BOX (sig_vbox), sig_hbox, FALSE, FALSE, 0); + + sigtext_label = gtk_label_new + (_("'Signature 1' will be used by default.")); + gtk_widget_show (sigtext_label); + gtk_box_pack_start (GTK_BOX (sig_hbox), sigtext_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (sigtext_label), GTK_JUSTIFY_LEFT); + gtk_label_set_line_wrap (GTK_LABEL (sigtext_label), TRUE); + gtkut_widget_set_small_font_size (sigtext_label); + + sigtext_scrwin = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (sigtext_scrwin); + gtk_box_pack_start (GTK_BOX (sig_vbox), sigtext_scrwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy + (GTK_SCROLLED_WINDOW (sigtext_scrwin), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type + (GTK_SCROLLED_WINDOW (sigtext_scrwin), GTK_SHADOW_IN); + + sig_text = gtk_text_view_new (); + gtk_widget_show (sig_text); + gtk_container_add (GTK_CONTAINER (sigtext_scrwin), sig_text); + gtk_text_view_set_editable (GTK_TEXT_VIEW (sig_text), TRUE); + gtk_widget_set_size_request(sig_text, DEFAULT_ENTRY_WIDTH, 60); + + sig_hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (sig_hbox); + gtk_box_pack_start (GTK_BOX (sig_vbox), sig_hbox, FALSE, FALSE, 0); + + sigfile_radiobtn = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON(sig_radiobtn), _("File")); + gtk_widget_show (sigfile_radiobtn); + gtk_box_pack_start (GTK_BOX (sig_hbox), sigfile_radiobtn, + FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (sigfile_radiobtn), MENU_VAL_ID, + GINT_TO_POINTER (SIG_FILE)); + + sigcmd_radiobtn = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON(sig_radiobtn), _("Command output")); + gtk_widget_show (sigcmd_radiobtn); + gtk_box_pack_start (GTK_BOX (sig_hbox), sigcmd_radiobtn, + FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (sigcmd_radiobtn), MENU_VAL_ID, + GINT_TO_POINTER (SIG_COMMAND)); + + sigpath_entry = gtk_entry_new (); + gtk_widget_show (sigpath_entry); + gtk_box_pack_start (GTK_BOX (sig_vbox), sigpath_entry, TRUE, TRUE, 0); + + PACK_CHECK_BUTTON (sig_vbox, sig_before_quote_chkbtn, + _("Put signature before quote (not recommended)")); + + SET_TOGGLE_SENSITIVITY (sig_radiobtn, sig_text); + SET_TOGGLE_SENSITIVITY (sig_radiobtn, sig_combo); + SET_TOGGLE_SENSITIVITY (sig_radiobtn, signame_entry); + SET_TOGGLE_SENSITIVITY (sigfile_radiobtn, sigpath_entry); + SET_TOGGLE_SENSITIVITY (sigcmd_radiobtn, sigpath_entry); + SET_TOGGLE_SENSITIVITY_REV (sig_radiobtn, sigpath_entry); + SET_TOGGLE_SENSITIVITY_REV (sigfile_radiobtn, sig_text); + SET_TOGGLE_SENSITIVITY_REV (sigfile_radiobtn, sig_combo); + SET_TOGGLE_SENSITIVITY_REV (sigfile_radiobtn, signame_entry); + SET_TOGGLE_SENSITIVITY_REV (sigcmd_radiobtn, sig_text); + SET_TOGGLE_SENSITIVITY_REV (sigcmd_radiobtn, sig_combo); + SET_TOGGLE_SENSITIVITY_REV (sigcmd_radiobtn, signame_entry); + gtk_widget_set_sensitive (sig_text, TRUE); + gtk_widget_set_sensitive (signame_entry, TRUE); + gtk_widget_set_sensitive (sigpath_entry, FALSE); + + PACK_FRAME (vbox1, frame, + _("Automatically set the following addresses")); + + table = gtk_table_new (3, 2, FALSE); + gtk_widget_show (table); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_container_set_border_width (GTK_CONTAINER (table), 8); + gtk_table_set_row_spacings (GTK_TABLE (table), VSPACING_NARROW_2); + gtk_table_set_col_spacings (GTK_TABLE (table), 8); + + autocc_chkbtn = gtk_check_button_new_with_label (_("Cc")); + gtk_widget_show (autocc_chkbtn); + gtk_table_attach (GTK_TABLE (table), autocc_chkbtn, 0, 1, 0, 1, + GTK_FILL, 0, 0, 0); + + autocc_entry = gtk_entry_new (); + gtk_widget_show (autocc_entry); + gtk_table_attach (GTK_TABLE (table), autocc_entry, 1, 2, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + SET_TOGGLE_SENSITIVITY (autocc_chkbtn, autocc_entry); + + autobcc_chkbtn = gtk_check_button_new_with_label (_("Bcc")); + gtk_widget_show (autobcc_chkbtn); + gtk_table_attach (GTK_TABLE (table), autobcc_chkbtn, 0, 1, 1, 2, + GTK_FILL, 0, 0, 0); + + autobcc_entry = gtk_entry_new (); + gtk_widget_show (autobcc_entry); + gtk_table_attach (GTK_TABLE (table), autobcc_entry, 1, 2, 1, 2, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + SET_TOGGLE_SENSITIVITY (autobcc_chkbtn, autobcc_entry); + + autoreplyto_chkbtn = gtk_check_button_new_with_label (_("Reply-To")); + gtk_widget_show (autoreplyto_chkbtn); + gtk_table_attach (GTK_TABLE (table), autoreplyto_chkbtn, 0, 1, 2, 3, + GTK_FILL, 0, 0, 0); + + autoreplyto_entry = gtk_entry_new (); + gtk_widget_show (autoreplyto_entry); + gtk_table_attach (GTK_TABLE (table), autoreplyto_entry, 1, 2, 2, 3, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + SET_TOGGLE_SENSITIVITY (autoreplyto_chkbtn, autoreplyto_entry); + + compose.sig_radiobtn = sig_radiobtn; + compose.sig_combo = sig_combo; + compose.signame_entry = signame_entry; + compose.sig_text = sig_text; + compose.sigpath_entry = sigpath_entry; + + compose.sig_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(sig_text)); + + compose.sig_before_quote_chkbtn = sig_before_quote_chkbtn; + + compose.autocc_chkbtn = autocc_chkbtn; + compose.autocc_entry = autocc_entry; + compose.autobcc_chkbtn = autobcc_chkbtn; + compose.autobcc_entry = autobcc_entry; + compose.autoreplyto_chkbtn = autoreplyto_chkbtn; + compose.autoreplyto_entry = autoreplyto_entry; +} + +#if USE_GPGME +static void prefs_account_privacy_create(void) +{ + GtkWidget *vbox1; + GtkWidget *frame1; + GtkWidget *vbox2; + GtkWidget *hbox1; + GtkWidget *label; + GtkWidget *default_sign_chkbtn; + GtkWidget *default_encrypt_chkbtn; + GtkWidget *encrypt_reply_chkbtn; + GtkWidget *encrypt_to_self_chkbtn; + GtkWidget *ascii_armored_chkbtn; + GtkWidget *clearsign_chkbtn; + GtkWidget *defaultkey_radiobtn; + GtkWidget *emailkey_radiobtn; + GtkWidget *customkey_radiobtn; + GtkWidget *customkey_entry; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (vbox2, default_sign_chkbtn, + _("PGP sign message by default")); + PACK_CHECK_BUTTON (vbox2, default_encrypt_chkbtn, + _("PGP encrypt message by default")); + PACK_CHECK_BUTTON (vbox2, encrypt_reply_chkbtn, + _("Encrypt when replying to encrypted message")); + PACK_CHECK_BUTTON (vbox2, encrypt_to_self_chkbtn, + _("Add my own key to the recipients list on encryption")); + PACK_CHECK_BUTTON (vbox2, ascii_armored_chkbtn, + _("Use ASCII-armored format for encryption")); + PACK_CHECK_BUTTON (vbox2, clearsign_chkbtn, + _("Use clear text signature")); + g_signal_connect (G_OBJECT (ascii_armored_chkbtn), "toggled", + G_CALLBACK (prefs_account_ascii_armored_warning), + NULL); + + PACK_FRAME (vbox1, frame1, _("Sign / Encryption key")); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (frame1), vbox2); + gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); + + defaultkey_radiobtn = gtk_radio_button_new_with_label + (NULL, _("Use default GnuPG key")); + gtk_widget_show (defaultkey_radiobtn); + gtk_box_pack_start (GTK_BOX (vbox2), defaultkey_radiobtn, + FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (defaultkey_radiobtn), MENU_VAL_ID, + GINT_TO_POINTER (SIGN_KEY_DEFAULT)); + + emailkey_radiobtn = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON (defaultkey_radiobtn), + _("Select key by your email address")); + gtk_widget_show (emailkey_radiobtn); + gtk_box_pack_start (GTK_BOX (vbox2), emailkey_radiobtn, + FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (emailkey_radiobtn), MENU_VAL_ID, + GINT_TO_POINTER (SIGN_KEY_BY_FROM)); + + customkey_radiobtn = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON (defaultkey_radiobtn), + _("Specify key manually")); + gtk_widget_show (customkey_radiobtn); + gtk_box_pack_start (GTK_BOX (vbox2), customkey_radiobtn, + FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (customkey_radiobtn), MENU_VAL_ID, + GINT_TO_POINTER (SIGN_KEY_CUSTOM)); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0); + + label = gtk_label_new (""); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox1), label, FALSE, FALSE, 0); + gtk_widget_set_size_request (label, 16, -1); + + label = gtk_label_new (_("User or key ID:")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox1), label, FALSE, FALSE, 0); + + customkey_entry = gtk_entry_new (); + gtk_widget_show (customkey_entry); + gtk_box_pack_start (GTK_BOX (hbox1), customkey_entry, + TRUE, TRUE, 0); + + SET_TOGGLE_SENSITIVITY (customkey_radiobtn, customkey_entry); + + privacy.default_sign_chkbtn = default_sign_chkbtn; + privacy.default_encrypt_chkbtn = default_encrypt_chkbtn; + privacy.encrypt_reply_chkbtn = encrypt_reply_chkbtn; + privacy.encrypt_to_self_chkbtn = encrypt_to_self_chkbtn; + privacy.ascii_armored_chkbtn = ascii_armored_chkbtn; + privacy.clearsign_chkbtn = clearsign_chkbtn; + privacy.defaultkey_radiobtn = defaultkey_radiobtn; + privacy.emailkey_radiobtn = emailkey_radiobtn; + privacy.customkey_radiobtn = customkey_radiobtn; + privacy.customkey_entry = customkey_entry; +} +#endif /* USE_GPGME */ + +#define CREATE_RADIO_BUTTON(box, btn, btn_p, label, data) \ +{ \ + btn = gtk_radio_button_new_with_label_from_widget \ + (GTK_RADIO_BUTTON (btn_p), label); \ + gtk_widget_show (btn); \ + gtk_box_pack_start (GTK_BOX (box), btn, FALSE, FALSE, 0); \ + g_object_set_data (G_OBJECT (btn), MENU_VAL_ID, \ + GINT_TO_POINTER (data)); \ +} + +#define CREATE_RADIO_BUTTONS(box, \ + btn1, btn1_label, btn1_data, \ + btn2, btn2_label, btn2_data, \ + btn3, btn3_label, btn3_data) \ +{ \ + btn1 = gtk_radio_button_new_with_label(NULL, btn1_label); \ + gtk_widget_show (btn1); \ + gtk_box_pack_start (GTK_BOX (box), btn1, FALSE, FALSE, 0); \ + g_object_set_data (G_OBJECT (btn1), MENU_VAL_ID, \ + GINT_TO_POINTER (btn1_data)); \ + \ + CREATE_RADIO_BUTTON(box, btn2, btn1, btn2_label, btn2_data); \ + CREATE_RADIO_BUTTON(box, btn3, btn1, btn3_label, btn3_data); \ +} + +#if USE_SSL +static void pop_ssltunnel_toggled(GtkToggleButton *button, gpointer data) +{ + if (gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(advanced.popport_chkbtn))) + return; + + if (gtk_toggle_button_get_active(button)) { + gtk_entry_set_text(GTK_ENTRY(advanced.popport_entry), "995"); + } else { + gtk_entry_set_text(GTK_ENTRY(advanced.popport_entry), "110"); + } +} + +static void imap_ssltunnel_toggled(GtkToggleButton *button, gpointer data) +{ + if (gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(advanced.imapport_chkbtn))) + return; + + if (gtk_toggle_button_get_active(button)) { + gtk_entry_set_text(GTK_ENTRY(advanced.imapport_entry), "993"); + } else { + gtk_entry_set_text(GTK_ENTRY(advanced.imapport_entry), "143"); + } +} + +static void nntp_ssltunnel_toggled(GtkToggleButton *button, gpointer data) +{ + if (gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(advanced.nntpport_chkbtn))) + return; + + if (gtk_toggle_button_get_active(button)) { + gtk_entry_set_text(GTK_ENTRY(advanced.nntpport_entry), "563"); + } else { + gtk_entry_set_text(GTK_ENTRY(advanced.nntpport_entry), "119"); + } +} + +static void smtp_ssltunnel_toggled(GtkToggleButton *button, gpointer data) +{ + if (gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(advanced.smtpport_chkbtn))) + return; + + if (gtk_toggle_button_get_active(button)) { + gtk_entry_set_text(GTK_ENTRY(advanced.smtpport_entry), "465"); + } else { + gtk_entry_set_text(GTK_ENTRY(advanced.smtpport_entry), "25"); + } +} + +static void prefs_account_ssl_create(void) +{ + GtkWidget *vbox1; + + GtkWidget *pop_frame; + GtkWidget *vbox2; + GtkWidget *pop_nossl_radiobtn; + GtkWidget *pop_ssltunnel_radiobtn; + GtkWidget *pop_starttls_radiobtn; + + GtkWidget *imap_frame; + GtkWidget *vbox3; + GtkWidget *imap_nossl_radiobtn; + GtkWidget *imap_ssltunnel_radiobtn; + GtkWidget *imap_starttls_radiobtn; + + GtkWidget *nntp_frame; + GtkWidget *vbox4; + GtkWidget *nntp_nossl_radiobtn; + GtkWidget *nntp_ssltunnel_radiobtn; + + GtkWidget *send_frame; + GtkWidget *vbox5; + GtkWidget *smtp_nossl_radiobtn; + GtkWidget *smtp_ssltunnel_radiobtn; + GtkWidget *smtp_starttls_radiobtn; + + GtkWidget *vbox6; + GtkWidget *use_nonblocking_ssl_chkbtn; + GtkWidget *label; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER); + + PACK_FRAME (vbox1, pop_frame, _("POP3")); + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (pop_frame), vbox2); + gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); + + CREATE_RADIO_BUTTONS(vbox2, + pop_nossl_radiobtn, + _("Don't use SSL"), + SSL_NONE, + pop_ssltunnel_radiobtn, + _("Use SSL for POP3 connection"), + SSL_TUNNEL, + pop_starttls_radiobtn, + _("Use STARTTLS command to start SSL session"), + SSL_STARTTLS); + + g_signal_connect(G_OBJECT(pop_ssltunnel_radiobtn), "toggled", + G_CALLBACK(pop_ssltunnel_toggled), NULL); + + PACK_FRAME (vbox1, imap_frame, _("IMAP4")); + vbox3 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox3); + gtk_container_add (GTK_CONTAINER (imap_frame), vbox3); + gtk_container_set_border_width (GTK_CONTAINER (vbox3), 8); + + CREATE_RADIO_BUTTONS(vbox3, + imap_nossl_radiobtn, + _("Don't use SSL"), + SSL_NONE, + imap_ssltunnel_radiobtn, + _("Use SSL for IMAP4 connection"), + SSL_TUNNEL, + imap_starttls_radiobtn, + _("Use STARTTLS command to start SSL session"), + SSL_STARTTLS); + + g_signal_connect(G_OBJECT(imap_ssltunnel_radiobtn), "toggled", + G_CALLBACK(imap_ssltunnel_toggled), NULL); + + PACK_FRAME (vbox1, nntp_frame, _("NNTP")); + vbox4 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox4); + gtk_container_add (GTK_CONTAINER (nntp_frame), vbox4); + gtk_container_set_border_width (GTK_CONTAINER (vbox4), 8); + + nntp_nossl_radiobtn = + gtk_radio_button_new_with_label (NULL, _("Don't use SSL")); + gtk_widget_show (nntp_nossl_radiobtn); + gtk_box_pack_start (GTK_BOX (vbox4), nntp_nossl_radiobtn, + FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (nntp_nossl_radiobtn), MENU_VAL_ID, + GINT_TO_POINTER (SSL_NONE)); + + CREATE_RADIO_BUTTON(vbox4, nntp_ssltunnel_radiobtn, nntp_nossl_radiobtn, + _("Use SSL for NNTP connection"), SSL_TUNNEL); + + g_signal_connect(G_OBJECT(nntp_ssltunnel_radiobtn), "toggled", + G_CALLBACK(nntp_ssltunnel_toggled), NULL); + + PACK_FRAME (vbox1, send_frame, _("Send (SMTP)")); + vbox5 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox5); + gtk_container_add (GTK_CONTAINER (send_frame), vbox5); + gtk_container_set_border_width (GTK_CONTAINER (vbox5), 8); + + CREATE_RADIO_BUTTONS(vbox5, + smtp_nossl_radiobtn, + _("Don't use SSL"), + SSL_NONE, + smtp_ssltunnel_radiobtn, + _("Use SSL for SMTP connection"), + SSL_TUNNEL, + smtp_starttls_radiobtn, + _("Use STARTTLS command to start SSL session"), + SSL_STARTTLS); + + g_signal_connect(G_OBJECT(smtp_ssltunnel_radiobtn), "toggled", + G_CALLBACK(smtp_ssltunnel_toggled), NULL); + + vbox6 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox6); + gtk_box_pack_start (GTK_BOX (vbox1), vbox6, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON(vbox6, use_nonblocking_ssl_chkbtn, + _("Use non-blocking SSL")); + PACK_SMALL_LABEL + (vbox6, label, + _("Turn this off if you have problems in SSL connection.")); + + ssl.pop_frame = pop_frame; + ssl.pop_nossl_radiobtn = pop_nossl_radiobtn; + ssl.pop_ssltunnel_radiobtn = pop_ssltunnel_radiobtn; + ssl.pop_starttls_radiobtn = pop_starttls_radiobtn; + + ssl.imap_frame = imap_frame; + ssl.imap_nossl_radiobtn = imap_nossl_radiobtn; + ssl.imap_ssltunnel_radiobtn = imap_ssltunnel_radiobtn; + ssl.imap_starttls_radiobtn = imap_starttls_radiobtn; + + ssl.nntp_frame = nntp_frame; + ssl.nntp_nossl_radiobtn = nntp_nossl_radiobtn; + ssl.nntp_ssltunnel_radiobtn = nntp_ssltunnel_radiobtn; + + ssl.send_frame = send_frame; + ssl.smtp_nossl_radiobtn = smtp_nossl_radiobtn; + ssl.smtp_ssltunnel_radiobtn = smtp_ssltunnel_radiobtn; + ssl.smtp_starttls_radiobtn = smtp_starttls_radiobtn; + + ssl.use_nonblocking_ssl_chkbtn = use_nonblocking_ssl_chkbtn; +} + +#endif /* USE_SSL */ + +static void prefs_account_proxy_create(void) +{ + GtkWidget *vbox1; + GtkWidget *vbox2; + GtkWidget *socks_frame; + GtkWidget *socks_chkbtn; + GtkWidget *hbox2; + GtkWidget *label; + GtkWidget *socks4_radiobtn; + GtkWidget *socks5_radiobtn; + GtkWidget *socks_host_entry; + GtkWidget *socks_port_entry; + GtkWidget *vbox4; + GtkWidget *socks_auth_chkbtn; + GtkWidget *socks_name_entry; + GtkWidget *socks_pass_entry; + GtkWidget *socks_send_chkbtn; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER); + + PACK_FRAME_WITH_CHECK_BUTTON(vbox1, socks_frame, socks_chkbtn, + _("Use SOCKS proxy")); + + vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (socks_frame), vbox2); + gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); + + hbox2 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox2); + gtk_box_pack_start (GTK_BOX (vbox2), hbox2, FALSE, FALSE, 0); + + socks4_radiobtn = gtk_radio_button_new_with_label(NULL, "SOCKS4"); + gtk_widget_show(socks4_radiobtn); + gtk_box_pack_start (GTK_BOX (hbox2), socks4_radiobtn, FALSE, FALSE, 0); + g_object_set_data(G_OBJECT(socks4_radiobtn), MENU_VAL_ID, + GINT_TO_POINTER(SOCKS_SOCKS4)); + + CREATE_RADIO_BUTTON(hbox2, socks5_radiobtn, socks4_radiobtn, "SOCKS5", + SOCKS_SOCKS5); + + hbox2 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox2); + gtk_box_pack_start (GTK_BOX (vbox2), hbox2, FALSE, FALSE, 0); + + label = gtk_label_new(_("Hostname:")); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0); + + socks_host_entry = gtk_entry_new(); + gtk_widget_show(socks_host_entry); + gtk_widget_set_size_request(socks_host_entry, DEFAULT_ENTRY_WIDTH, -1); + gtk_box_pack_start(GTK_BOX(hbox2), socks_host_entry, TRUE, TRUE, 0); + + label = gtk_label_new(_("Port:")); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0); + + socks_port_entry = gtk_entry_new(); + gtk_widget_show(socks_port_entry); + gtk_widget_set_size_request(socks_port_entry, 64, -1); + gtk_box_pack_start(GTK_BOX(hbox2), socks_port_entry, FALSE, FALSE, 0); + + vbox4 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox4); + gtk_box_pack_start(GTK_BOX(vbox2), vbox4, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (vbox4, socks_auth_chkbtn, _("Use authentication")); + + hbox2 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox2); + gtk_box_pack_start (GTK_BOX (vbox4), hbox2, FALSE, FALSE, 0); + + label = gtk_label_new(_("Name:")); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0); + + socks_name_entry = gtk_entry_new(); + gtk_widget_show(socks_name_entry); + gtk_widget_set_size_request(socks_name_entry, DEFAULT_ENTRY_WIDTH, -1); + gtk_box_pack_start(GTK_BOX(hbox2), socks_name_entry, TRUE, TRUE, 0); + + label = gtk_label_new(_("Password:")); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0); + + socks_pass_entry = gtk_entry_new(); + gtk_widget_show(socks_pass_entry); + gtk_widget_set_size_request(socks_pass_entry, DEFAULT_ENTRY_WIDTH, -1); + gtk_entry_set_visibility(GTK_ENTRY(socks_pass_entry), FALSE); + gtk_box_pack_start(GTK_BOX(hbox2), socks_pass_entry, TRUE, TRUE, 0); + + PACK_CHECK_BUTTON(vbox2, socks_send_chkbtn, + _("Use SOCKS proxy on sending")); + + SET_TOGGLE_SENSITIVITY(socks_auth_chkbtn, hbox2); + SET_TOGGLE_SENSITIVITY(socks5_radiobtn, vbox4); + SET_TOGGLE_SENSITIVITY(socks_chkbtn, vbox2); + + p_proxy.socks_chkbtn = socks_chkbtn; + p_proxy.socks4_radiobtn = socks4_radiobtn; + p_proxy.socks5_radiobtn = socks5_radiobtn; + p_proxy.socks_host_entry = socks_host_entry; + p_proxy.socks_port_entry = socks_port_entry; + p_proxy.socks_auth_chkbtn = socks_auth_chkbtn; + p_proxy.socks_name_entry = socks_name_entry; + p_proxy.socks_pass_entry = socks_pass_entry; + p_proxy.socks_send_chkbtn = socks_send_chkbtn; +} + +static void prefs_account_advanced_create(void) +{ + GtkWidget *vbox1; + GtkWidget *vbox2; + GtkWidget *hbox1; + GtkWidget *checkbtn_smtpport; + GtkWidget *entry_smtpport; + GtkWidget *hbox_popport; + GtkWidget *checkbtn_popport; + GtkWidget *entry_popport; + GtkWidget *hbox_imapport; + GtkWidget *checkbtn_imapport; + GtkWidget *entry_imapport; + GtkWidget *hbox_nntpport; + GtkWidget *checkbtn_nntpport; + GtkWidget *entry_nntpport; + GtkWidget *checkbtn_domain; + GtkWidget *entry_domain; + GtkWidget *imap_frame; + GtkWidget *imapdir_label; + GtkWidget *imapdir_entry; + GtkWidget *clear_cache_chkbtn; + GtkWidget *desc_label; + GtkWidget *folder_frame; + GtkWidget *vbox3; + GtkWidget *table; + GtkWidget *sent_folder_chkbtn; + GtkWidget *sent_folder_entry; + GtkWidget *draft_folder_chkbtn; + GtkWidget *draft_folder_entry; + GtkWidget *queue_folder_chkbtn; + GtkWidget *queue_folder_entry; + GtkWidget *trash_folder_chkbtn; + GtkWidget *trash_folder_entry; + +#define PACK_HBOX(hbox) \ +{ \ + hbox = gtk_hbox_new (FALSE, 8); \ + gtk_widget_show (hbox); \ + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); \ +} + +#define PACK_PORT_ENTRY(box, entry) \ +{ \ + entry = gtk_entry_new (); \ + gtk_entry_set_max_length (GTK_ENTRY(entry), 5); \ + gtk_widget_show (entry); \ + gtk_box_pack_start (GTK_BOX (box), entry, FALSE, FALSE, 0); \ + gtk_widget_set_size_request (entry, 64, -1); \ +} + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER); + + vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW_2); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, FALSE, 0); + + PACK_HBOX (hbox1); + PACK_CHECK_BUTTON (hbox1, checkbtn_smtpport, _("Specify SMTP port")); + PACK_PORT_ENTRY (hbox1, entry_smtpport); + SET_TOGGLE_SENSITIVITY (checkbtn_smtpport, entry_smtpport); + + PACK_HBOX (hbox_popport); + PACK_CHECK_BUTTON (hbox_popport, checkbtn_popport, + _("Specify POP3 port")); + PACK_PORT_ENTRY (hbox_popport, entry_popport); + SET_TOGGLE_SENSITIVITY (checkbtn_popport, entry_popport); + + PACK_HBOX (hbox_imapport); + PACK_CHECK_BUTTON (hbox_imapport, checkbtn_imapport, + _("Specify IMAP4 port")); + PACK_PORT_ENTRY (hbox_imapport, entry_imapport); + SET_TOGGLE_SENSITIVITY (checkbtn_imapport, entry_imapport); + + PACK_HBOX (hbox_nntpport); + PACK_CHECK_BUTTON (hbox_nntpport, checkbtn_nntpport, + _("Specify NNTP port")); + PACK_PORT_ENTRY (hbox_nntpport, entry_nntpport); + SET_TOGGLE_SENSITIVITY (checkbtn_nntpport, entry_nntpport); + + PACK_HBOX (hbox1); + PACK_CHECK_BUTTON (hbox1, checkbtn_domain, _("Specify domain name")); + +#undef PACK_HBOX +#undef PACK_PORT_ENTRY + + entry_domain = gtk_entry_new (); + gtk_widget_show (entry_domain); + gtk_box_pack_start (GTK_BOX (hbox1), entry_domain, TRUE, TRUE, 0); + SET_TOGGLE_SENSITIVITY (checkbtn_domain, entry_domain); + + PACK_FRAME (vbox1, imap_frame, _("IMAP4")); + + vbox3 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox3); + gtk_container_add (GTK_CONTAINER (imap_frame), vbox3); + gtk_container_set_border_width (GTK_CONTAINER (vbox3), 8); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox3), hbox1, FALSE, FALSE, 0); + + imapdir_label = gtk_label_new (_("IMAP server directory")); + gtk_widget_show (imapdir_label); + gtk_box_pack_start (GTK_BOX (hbox1), imapdir_label, FALSE, FALSE, 0); + + imapdir_entry = gtk_entry_new(); + gtk_widget_show (imapdir_entry); + gtk_box_pack_start (GTK_BOX (hbox1), imapdir_entry, TRUE, TRUE, 0); + + PACK_SMALL_LABEL + (vbox3, desc_label, + _("Only the subfolders of this directory will be displayed.")); + + PACK_CHECK_BUTTON (vbox3, clear_cache_chkbtn, + _("Clear all message caches on exit")); + + /* special folder setting (maybe these options are redundant) */ + + PACK_FRAME (vbox1, folder_frame, _("Folder")); + + vbox3 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox3); + gtk_container_add (GTK_CONTAINER (folder_frame), vbox3); + gtk_container_set_border_width (GTK_CONTAINER (vbox3), 8); + + table = gtk_table_new (4, 3, FALSE); + gtk_widget_show (table); + gtk_container_add (GTK_CONTAINER (vbox3), table); + gtk_table_set_row_spacings (GTK_TABLE (table), VSPACING_NARROW_2); + gtk_table_set_col_spacings (GTK_TABLE (table), 4); + +#define SET_CHECK_BTN_AND_ENTRY(label, chkbtn, entry, n) \ +{ \ + GtkWidget *button; \ + \ + chkbtn = gtk_check_button_new_with_label (label); \ + gtk_widget_show (chkbtn); \ + gtk_table_attach (GTK_TABLE (table), chkbtn, \ + 0, 1, n, n + 1, GTK_FILL, 0, 0, 0); \ + \ + entry = gtk_entry_new (); \ + gtk_widget_show (entry); \ + gtk_table_attach (GTK_TABLE (table), entry, 1, 2, n, n + 1, \ + GTK_EXPAND | GTK_SHRINK | GTK_FILL, \ + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); \ + \ + button = gtk_button_new_with_label (_(" ... ")); \ + gtk_widget_show (button); \ + gtk_table_attach (GTK_TABLE (table), button, \ + 2, 3, n, n + 1, GTK_FILL, 0, 0, 0); \ + g_signal_connect \ + (G_OBJECT (button), "clicked", \ + G_CALLBACK (prefs_account_select_folder_cb), \ + entry); \ + \ + SET_TOGGLE_SENSITIVITY (chkbtn, entry); \ + SET_TOGGLE_SENSITIVITY (chkbtn, button); \ +} + + SET_CHECK_BTN_AND_ENTRY(_("Put sent messages in"), + sent_folder_chkbtn, sent_folder_entry, 0); + SET_CHECK_BTN_AND_ENTRY(_("Put draft messages in"), + draft_folder_chkbtn, draft_folder_entry, 1); + SET_CHECK_BTN_AND_ENTRY(_("Put queued messages in"), + queue_folder_chkbtn, queue_folder_entry, 2); + SET_CHECK_BTN_AND_ENTRY(_("Put deleted messages in"), + trash_folder_chkbtn, trash_folder_entry, 3); + + advanced.smtpport_chkbtn = checkbtn_smtpport; + advanced.smtpport_entry = entry_smtpport; + advanced.popport_hbox = hbox_popport; + advanced.popport_chkbtn = checkbtn_popport; + advanced.popport_entry = entry_popport; + advanced.imapport_hbox = hbox_imapport; + advanced.imapport_chkbtn = checkbtn_imapport; + advanced.imapport_entry = entry_imapport; + advanced.nntpport_hbox = hbox_nntpport; + advanced.nntpport_chkbtn = checkbtn_nntpport; + advanced.nntpport_entry = entry_nntpport; + advanced.domain_chkbtn = checkbtn_domain; + advanced.domain_entry = entry_domain; + + advanced.imap_frame = imap_frame; + advanced.imapdir_entry = imapdir_entry; + advanced.clear_cache_chkbtn = clear_cache_chkbtn; + + advanced.sent_folder_chkbtn = sent_folder_chkbtn; + advanced.sent_folder_entry = sent_folder_entry; + advanced.draft_folder_chkbtn = draft_folder_chkbtn; + advanced.draft_folder_entry = draft_folder_entry; + advanced.queue_folder_chkbtn = queue_folder_chkbtn; + advanced.queue_folder_entry = queue_folder_entry; + advanced.trash_folder_chkbtn = trash_folder_chkbtn; + advanced.trash_folder_entry = trash_folder_entry; +} + +static gint prefs_account_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + prefs_account_cancel(); + return TRUE; +} + +static gboolean prefs_account_key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + prefs_account_cancel(); + return FALSE; +} + +static void prefs_account_ok(void) +{ + if (prefs_account_apply() == 0) + gtk_main_quit(); +} + +static gint prefs_account_apply(void) +{ + RecvProtocol protocol; + GtkWidget *menu; + GtkWidget *menuitem; + PrefsAccount *tmp_ac_prefs; + gint i; + + menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(basic.protocol_optmenu)); + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + protocol = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID)); + + gtkut_entry_strip_text(GTK_ENTRY(basic.addr_entry)); + gtkut_entry_strip_text(GTK_ENTRY(basic.smtpserv_entry)); + gtkut_entry_strip_text(GTK_ENTRY(basic.recvserv_entry)); + gtkut_entry_strip_text(GTK_ENTRY(basic.nntpserv_entry)); + + if (*gtk_entry_get_text(GTK_ENTRY(basic.acname_entry)) == '\0') { + alertpanel_error(_("Account name is not entered.")); + return -1; + } + if (*gtk_entry_get_text(GTK_ENTRY(basic.addr_entry)) == '\0') { + alertpanel_error(_("Mail address is not entered.")); + return -1; + } + if ((protocol == A_POP3 || protocol == A_LOCAL) && + *gtk_entry_get_text(GTK_ENTRY(basic.smtpserv_entry)) == '\0') { + alertpanel_error(_("SMTP server is not entered.")); + return -1; + } + if ((protocol == A_POP3 || protocol == A_IMAP4) && + *gtk_entry_get_text(GTK_ENTRY(basic.uid_entry)) == '\0') { + alertpanel_error(_("User ID is not entered.")); + return -1; + } + if (protocol == A_POP3 && + *gtk_entry_get_text(GTK_ENTRY(basic.recvserv_entry)) == '\0') { + alertpanel_error(_("POP3 server is not entered.")); + return -1; + } + if (protocol == A_IMAP4 && + *gtk_entry_get_text(GTK_ENTRY(basic.recvserv_entry)) == '\0') { + alertpanel_error(_("IMAP4 server is not entered.")); + return -1; + } + if (protocol == A_NNTP && + *gtk_entry_get_text(GTK_ENTRY(basic.nntpserv_entry)) == '\0') { + alertpanel_error(_("NNTP server is not entered.")); + return -1; + } + + prefs_set_data_from_dialog(prefs_account_get_params()); + + prefs_account_sig_combo_changed_cb(compose.sig_combo, NULL); + + tmp_ac_prefs = prefs_account_get_tmp_prefs(); + g_free(tmp_ac_prefs->sig_text); + tmp_ac_prefs->sig_text = g_strdup(compose.sig_texts[0]); + for (i = 0; i < 10; i++) { + g_free(tmp_ac_prefs->sig_names[i]); + tmp_ac_prefs->sig_names[i] = g_strdup(compose.sig_names[i]); + g_free(tmp_ac_prefs->sig_texts[i]); + tmp_ac_prefs->sig_texts[i] = g_strdup(compose.sig_texts[i]); + } + + return 0; +} + +static void prefs_account_cancel(void) +{ + cancelled = TRUE; + gtk_main_quit(); +} + +static void prefs_account_select_folder_cb(GtkWidget *widget, gpointer data) +{ + FolderItem *item; + gchar *id; + + item = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL); + if (item && item->path) { + GtkEntry *entry = GTK_ENTRY(data); + + if (entry == GTK_ENTRY(advanced.queue_folder_entry) && + item->stype != F_QUEUE) { + alertpanel_error + (_("Specified folder is not a queue folder.")); + return; + } + + id = folder_item_get_identifier(item); + if (id) { + gtk_entry_set_text(entry, id); + g_free(id); + } + } +} + +static void prefs_account_edit_custom_header(void) +{ + prefs_custom_header_open(prefs_account_get_tmp_prefs()); +} + +static void prefs_account_name_entry_changed_cb(GtkWidget *widget, + gpointer data) +{ + PrefsAccount *tmp_ac_prefs; + + tmp_ac_prefs = prefs_account_get_tmp_prefs(); + + if (new_account && !compose.sig_modified) { + gchar *sig; + GtkTextIter iter; + + g_signal_handlers_block_by_func + (G_OBJECT(compose.sig_buffer), + G_CALLBACK(prefs_account_sig_changed_cb), NULL); + + sig = g_strdup_printf + ("%s <%s>\n", + gtk_entry_get_text(GTK_ENTRY(basic.name_entry)), + gtk_entry_get_text(GTK_ENTRY(basic.addr_entry))); + gtk_text_buffer_set_text(compose.sig_buffer, "", 0); + gtk_text_buffer_get_start_iter(compose.sig_buffer, &iter); + gtk_text_buffer_insert(compose.sig_buffer, &iter, sig, -1); + g_free(sig); + + g_signal_handlers_unblock_by_func + (G_OBJECT(compose.sig_buffer), + G_CALLBACK(prefs_account_sig_changed_cb), NULL); + + } +} + +static void prefs_account_sig_changed_cb(GtkWidget *widget, gpointer data) +{ + compose.sig_modified = TRUE; +} + +static void prefs_account_sig_combo_changed_cb(GtkWidget *widget, gpointer data) +{ + gint cur_page; + gint new_page; + + cur_page = compose.sig_selected; + new_page = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); + + /* Save current one */ + g_free(compose.sig_names[cur_page]); + compose.sig_names[cur_page] = + gtk_editable_get_chars(GTK_EDITABLE(compose.signame_entry), 0, -1); + g_free(compose.sig_texts[cur_page]); + compose.sig_texts[cur_page] = + prefs_get_escaped_str_from_text(compose.sig_text); + + /* Restore another one */ + if (cur_page != new_page) { + gtk_entry_set_text(GTK_ENTRY(compose.signame_entry), + compose.sig_names[new_page] ? compose.sig_names[new_page] : ""); + prefs_set_escaped_str_to_text(compose.sig_text, + compose.sig_texts[new_page]); + compose.sig_selected = new_page; + } +} + +static void prefs_account_enum_set_data_from_radiobtn(PrefParam *pparam) +{ + PrefsUIData *ui_data; + GtkRadioButton *radiobtn; + GSList *group; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + radiobtn = GTK_RADIO_BUTTON(*ui_data->widget); + group = gtk_radio_button_get_group(radiobtn); + while (group != NULL) { + GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data); + if (gtk_toggle_button_get_active(btn)) { + *((gint *)pparam->data) = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(btn), MENU_VAL_ID)); + break; + } + group = group->next; + } +} + +static void prefs_account_enum_set_radiobtn(PrefParam *pparam) +{ + PrefsUIData *ui_data; + GtkRadioButton *radiobtn; + GSList *group; + gpointer data; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + data = GINT_TO_POINTER(*((gint *)pparam->data)); + radiobtn = GTK_RADIO_BUTTON(*ui_data->widget); + group = gtk_radio_button_get_group(radiobtn); + while (group != NULL) { + GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data); + gpointer data1; + + data1 = g_object_get_data(G_OBJECT(btn), MENU_VAL_ID); + if (data1 == data) { + gtk_toggle_button_set_active(btn, TRUE); + break; + } + group = group->next; + } +} + + +#if USE_GPGME +static void prefs_account_ascii_armored_warning(GtkWidget *widget) +{ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)) && + gtk_notebook_get_current_page(GTK_NOTEBOOK(dialog.notebook)) > 0) + alertpanel_warning + (_("It's not recommended to use the old style ASCII-armored\n" + "mode for encrypted messages. It doesn't comply with the\n" + "RFC 3156 - MIME Security with OpenPGP.")); +} +#endif /* USE_GPGME */ + +static void prefs_account_protocol_set_data_from_optmenu(PrefParam *pparam) +{ + PrefsUIData *ui_data; + GtkWidget *menu; + GtkWidget *menuitem; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(*ui_data->widget)); + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + *((RecvProtocol *)pparam->data) = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID)); +} + +static void prefs_account_protocol_set_optmenu(PrefParam *pparam) +{ + PrefsUIData *ui_data; + RecvProtocol protocol; + GtkOptionMenu *optmenu; + GtkWidget *menu; + GtkWidget *menuitem; + gint index; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + optmenu = GTK_OPTION_MENU(*ui_data->widget); + + protocol = *((RecvProtocol *)pparam->data); + index = menu_find_option_menu_index + (optmenu, GINT_TO_POINTER(protocol), NULL); + if (index < 0) return; + gtk_option_menu_set_history(optmenu, index); + + menu = gtk_option_menu_get_menu(optmenu); + menu_set_insensitive_all(GTK_MENU_SHELL(menu)); + + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + gtk_widget_set_sensitive(menuitem, TRUE); + gtk_menu_item_activate(GTK_MENU_ITEM(menuitem)); +} + +static void prefs_account_imap_auth_type_set_data_from_optmenu(PrefParam *pparam) +{ + PrefsUIData *ui_data; + GtkWidget *menu; + GtkWidget *menuitem; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(*ui_data->widget)); + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + *((RecvProtocol *)pparam->data) = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID)); +} + +static void prefs_account_imap_auth_type_set_optmenu(PrefParam *pparam) +{ + PrefsUIData *ui_data; + IMAPAuthType type = *((IMAPAuthType *)pparam->data); + GtkOptionMenu *optmenu; + GtkWidget *menu; + GtkWidget *menuitem; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + optmenu = GTK_OPTION_MENU(*ui_data->widget); + + switch (type) { + case IMAP_AUTH_LOGIN: + gtk_option_menu_set_history(optmenu, 1); + break; + case IMAP_AUTH_PLAIN: + gtk_option_menu_set_history(optmenu, 2); + break; + case IMAP_AUTH_CRAM_MD5: + gtk_option_menu_set_history(optmenu, 3); + break; + case 0: + default: + gtk_option_menu_set_history(optmenu, 0); + } + + menu = gtk_option_menu_get_menu(optmenu); + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + gtk_menu_item_activate(GTK_MENU_ITEM(menuitem)); +} + +static void prefs_account_smtp_auth_type_set_data_from_optmenu(PrefParam *pparam) +{ + PrefsUIData *ui_data; + GtkWidget *menu; + GtkWidget *menuitem; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(*ui_data->widget)); + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + *((RecvProtocol *)pparam->data) = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID)); +} + +static void prefs_account_smtp_auth_type_set_optmenu(PrefParam *pparam) +{ + PrefsUIData *ui_data; + SMTPAuthType type = *((SMTPAuthType *)pparam->data); + GtkOptionMenu *optmenu; + GtkWidget *menu; + GtkWidget *menuitem; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + optmenu = GTK_OPTION_MENU(*ui_data->widget); + + switch (type) { + case SMTPAUTH_PLAIN: + gtk_option_menu_set_history(optmenu, 1); + break; + case SMTPAUTH_LOGIN: + gtk_option_menu_set_history(optmenu, 2); + break; + case SMTPAUTH_CRAM_MD5: + gtk_option_menu_set_history(optmenu, 3); + break; + case SMTPAUTH_DIGEST_MD5: + gtk_option_menu_set_history(optmenu, 4); + break; + case 0: + default: + gtk_option_menu_set_history(optmenu, 0); + } + + menu = gtk_option_menu_get_menu(optmenu); + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + gtk_menu_item_activate(GTK_MENU_ITEM(menuitem)); +} + +static void prefs_account_protocol_activated(GtkMenuItem *menuitem) +{ + RecvProtocol protocol; + gboolean active; + PrefsAccount *tmp_ac_prefs; + + tmp_ac_prefs = prefs_account_get_tmp_prefs(); + + protocol = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID)); + + switch(protocol) { + case A_NNTP: + gtk_widget_show(basic.nntpserv_label); + gtk_widget_show(basic.nntpserv_entry); + gtk_widget_show(basic.nntpauth_chkbtn); + gtk_widget_hide(basic.recvserv_label); + gtk_widget_hide(basic.recvserv_entry); + gtk_widget_hide(basic.smtpserv_label); + gtk_widget_hide(basic.smtpserv_entry); + active = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(basic.nntpauth_chkbtn)); + gtk_widget_set_sensitive(basic.uid_label, active); + gtk_widget_set_sensitive(basic.pass_label, active); + gtk_widget_set_sensitive(basic.uid_entry, active); + gtk_widget_set_sensitive(basic.pass_entry, active); + gtk_widget_hide(receive.pop3_frame); + gtk_widget_hide(receive.imap_frame); + gtk_widget_show(receive.nntp_frame); + gtk_widget_set_sensitive(receive.recvatgetall_chkbtn, TRUE); + + gtk_widget_set_sensitive(p_send.pop_bfr_smtp_chkbtn, FALSE); + + if (new_account) { + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON(receive.recvatgetall_chkbtn), + FALSE); + } + + gtk_widget_set_sensitive(p_send.pop_bfr_smtp_chkbtn, FALSE); + +#if USE_SSL + gtk_widget_hide(ssl.pop_frame); + gtk_widget_hide(ssl.imap_frame); + gtk_widget_show(ssl.nntp_frame); + gtk_widget_hide(ssl.send_frame); +#endif + gtk_widget_hide(advanced.popport_hbox); + gtk_widget_hide(advanced.imapport_hbox); + gtk_widget_show(advanced.nntpport_hbox); + gtk_widget_hide(advanced.imap_frame); + break; + case A_LOCAL: + gtk_widget_hide(basic.nntpserv_label); + gtk_widget_hide(basic.nntpserv_entry); + gtk_widget_hide(basic.nntpauth_chkbtn); + gtk_widget_set_sensitive(basic.recvserv_label, FALSE); + gtk_widget_set_sensitive(basic.recvserv_entry, FALSE); + gtk_widget_show(basic.recvserv_label); + gtk_widget_show(basic.recvserv_entry); + gtk_widget_show(basic.smtpserv_label); + gtk_widget_show(basic.smtpserv_entry); + gtk_widget_set_sensitive(basic.uid_label, FALSE); + gtk_widget_set_sensitive(basic.pass_label, FALSE); + gtk_widget_set_sensitive(basic.uid_entry, FALSE); + gtk_widget_set_sensitive(basic.pass_entry, FALSE); + gtk_widget_hide(receive.pop3_frame); + gtk_widget_hide(receive.imap_frame); + gtk_widget_hide(receive.nntp_frame); + gtk_widget_set_sensitive(receive.recvatgetall_chkbtn, FALSE); + + if (new_account) { + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON(receive.recvatgetall_chkbtn), + TRUE); + } + + gtk_widget_set_sensitive(p_send.pop_bfr_smtp_chkbtn, FALSE); + +#if USE_SSL + gtk_widget_hide(ssl.pop_frame); + gtk_widget_hide(ssl.imap_frame); + gtk_widget_hide(ssl.nntp_frame); + gtk_widget_show(ssl.send_frame); +#endif + gtk_widget_hide(advanced.popport_hbox); + gtk_widget_hide(advanced.imapport_hbox); + gtk_widget_hide(advanced.nntpport_hbox); + gtk_widget_hide(advanced.imap_frame); + break; + case A_IMAP4: + gtk_widget_hide(basic.nntpserv_label); + gtk_widget_hide(basic.nntpserv_entry); + gtk_widget_hide(basic.nntpauth_chkbtn); + gtk_widget_set_sensitive(basic.recvserv_label, TRUE); + gtk_widget_set_sensitive(basic.recvserv_entry, TRUE); + gtk_widget_show(basic.recvserv_label); + gtk_widget_show(basic.recvserv_entry); + gtk_widget_show(basic.smtpserv_label); + gtk_widget_show(basic.smtpserv_entry); + gtk_widget_set_sensitive(basic.uid_label, TRUE); + gtk_widget_set_sensitive(basic.pass_label, TRUE); + gtk_widget_set_sensitive(basic.uid_entry, TRUE); + gtk_widget_set_sensitive(basic.pass_entry, TRUE); + gtk_widget_hide(receive.pop3_frame); + gtk_widget_show(receive.imap_frame); + gtk_widget_hide(receive.nntp_frame); + gtk_widget_set_sensitive(receive.recvatgetall_chkbtn, TRUE); + + if (new_account) { + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON(receive.recvatgetall_chkbtn), + FALSE); + } + + gtk_widget_set_sensitive(p_send.pop_bfr_smtp_chkbtn, FALSE); + +#if USE_SSL + gtk_widget_hide(ssl.pop_frame); + gtk_widget_show(ssl.imap_frame); + gtk_widget_hide(ssl.nntp_frame); + gtk_widget_show(ssl.send_frame); +#endif + gtk_widget_hide(advanced.popport_hbox); + gtk_widget_show(advanced.imapport_hbox); + gtk_widget_hide(advanced.nntpport_hbox); + gtk_widget_show(advanced.imap_frame); + break; + case A_POP3: + default: + gtk_widget_hide(basic.nntpserv_label); + gtk_widget_hide(basic.nntpserv_entry); + gtk_widget_hide(basic.nntpauth_chkbtn); + gtk_widget_set_sensitive(basic.recvserv_label, TRUE); + gtk_widget_set_sensitive(basic.recvserv_entry, TRUE); + gtk_widget_show(basic.recvserv_label); + gtk_widget_show(basic.recvserv_entry); + gtk_widget_show(basic.smtpserv_label); + gtk_widget_show(basic.smtpserv_entry); + gtk_widget_set_sensitive(basic.uid_label, TRUE); + gtk_widget_set_sensitive(basic.pass_label, TRUE); + gtk_widget_set_sensitive(basic.uid_entry, TRUE); + gtk_widget_set_sensitive(basic.pass_entry, TRUE); + gtk_widget_show(receive.pop3_frame); + gtk_widget_hide(receive.imap_frame); + gtk_widget_hide(receive.nntp_frame); + gtk_widget_set_sensitive(receive.recvatgetall_chkbtn, TRUE); + + if (new_account) { + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON(receive.recvatgetall_chkbtn), + TRUE); + } + + gtk_widget_set_sensitive(p_send.pop_bfr_smtp_chkbtn, TRUE); + +#if USE_SSL + gtk_widget_show(ssl.pop_frame); + gtk_widget_hide(ssl.imap_frame); + gtk_widget_hide(ssl.nntp_frame); + gtk_widget_show(ssl.send_frame); +#endif + gtk_widget_show(advanced.popport_hbox); + gtk_widget_hide(advanced.imapport_hbox); + gtk_widget_hide(advanced.nntpport_hbox); + gtk_widget_hide(advanced.imap_frame); + break; + } + + gtk_widget_queue_resize(basic.serv_frame); +} diff --git a/src/prefs_account_dialog.h b/src/prefs_account_dialog.h new file mode 100644 index 0000000..0fbea98 --- /dev/null +++ b/src/prefs_account_dialog.h @@ -0,0 +1,29 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2007 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PREFS_ACCOUNT_DIALOG_H__ +#define __PREFS_ACCOUNT_DIALOG_H__ + +#include + +#include "prefs_account.h" + +PrefsAccount *prefs_account_open (PrefsAccount *ac_prefs); + +#endif /* __PREFS_ACCOUNT_DIALOG_H__ */ diff --git a/src/prefs_actions.c b/src/prefs_actions.c new file mode 100644 index 0000000..4718493 --- /dev/null +++ b/src/prefs_actions.c @@ -0,0 +1,674 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2015 Hiroyuki Yamamoto & The Sylpheed Claws Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prefs.h" +#include "inc.h" +#include "utils.h" +#include "gtkutils.h" +#include "manage_window.h" +#include "mainwindow.h" +#include "prefs_common.h" +#include "alertpanel.h" +#include "prefs_actions.h" +#include "action.h" + +static struct Actions +{ + GtkWidget *window; + + GtkWidget *confirm_area; + GtkWidget *ok_btn; + + GtkWidget *name_entry; + GtkWidget *cmd_entry; + + GtkWidget *actions_clist; +} actions; + +/* widget creating functions */ +static void prefs_actions_create (MainWindow *mainwin); +static void prefs_actions_set_dialog (void); +static gint prefs_actions_clist_set_row (gint row); + +/* callback functions */ +static void prefs_actions_help_cb (GtkWidget *w, + gpointer data); +static void prefs_actions_register_cb (GtkWidget *w, + gpointer data); +static void prefs_actions_substitute_cb (GtkWidget *w, + gpointer data); +static void prefs_actions_delete_cb (GtkWidget *w, + gpointer data); +static void prefs_actions_up (GtkWidget *w, + gpointer data); +static void prefs_actions_down (GtkWidget *w, + gpointer data); +static void prefs_actions_select (GtkCList *clist, + gint row, + gint column, + GdkEvent *event); +static void prefs_actions_row_move (GtkCList *clist, + gint source_row, + gint dest_row); +static gint prefs_actions_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer *data); +static gboolean prefs_actions_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); +static void prefs_actions_cancel (GtkWidget *w, + gpointer data); +static void prefs_actions_ok (GtkWidget *w, + gpointer data); + + +void prefs_actions_open(MainWindow *mainwin) +{ + inc_lock(); + + if (!actions.window) + prefs_actions_create(mainwin); + + gtkut_box_set_reverse_order(GTK_BOX(actions.confirm_area), + !prefs_common.comply_gnome_hig); + manage_window_set_transient(GTK_WINDOW(actions.window)); + gtk_widget_grab_focus(actions.ok_btn); + + prefs_actions_set_dialog(); + + gtk_widget_show(actions.window); +} + +static void prefs_actions_create(MainWindow *mainwin) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *confirm_area; + + GtkWidget *vbox1; + + GtkWidget *entry_vbox; + GtkWidget *hbox; + GtkWidget *name_label; + GtkWidget *name_entry; + GtkWidget *cmd_label; + GtkWidget *cmd_entry; + + GtkWidget *reg_hbox; + GtkWidget *btn_hbox; + GtkWidget *arrow; + GtkWidget *reg_btn; + GtkWidget *subst_btn; + GtkWidget *del_btn; + + GtkWidget *cond_hbox; + GtkWidget *cond_scrolledwin; + GtkWidget *cond_clist; + + GtkWidget *help_vbox; + GtkWidget *help_label; + GtkWidget *help_toggle; + + GtkWidget *btn_vbox; + GtkWidget *up_btn; + GtkWidget *down_btn; + + gchar *title[1]; + + debug_print("Creating actions configuration window...\n"); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + gtk_container_set_border_width(GTK_CONTAINER (window), 8); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, TRUE); + gtk_window_set_default_size(GTK_WINDOW(window), 400, -1); + + vbox = gtk_vbox_new(FALSE, 6); + gtk_widget_show(vbox); + gtk_container_add(GTK_CONTAINER(window), vbox); + + gtkut_stock_button_set_create(&confirm_area, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_widget_show(confirm_area); + gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_btn); + + gtk_window_set_title(GTK_WINDOW(window), _("Actions configuration")); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(prefs_actions_deleted), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(prefs_actions_key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(prefs_actions_ok), mainwin); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(prefs_actions_cancel), NULL); + + vbox1 = gtk_vbox_new(FALSE, 8); + gtk_widget_show(vbox1); + gtk_box_pack_start(GTK_BOX(vbox), vbox1, TRUE, TRUE, 0); + gtk_container_set_border_width(GTK_CONTAINER(vbox1), 2); + + entry_vbox = gtk_vbox_new(FALSE, 4); + gtk_box_pack_start(GTK_BOX(vbox1), entry_vbox, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(entry_vbox), hbox, FALSE, FALSE, 0); + + name_label = gtk_label_new(_("Menu name:")); + gtk_box_pack_start(GTK_BOX(hbox), name_label, FALSE, FALSE, 0); + + name_entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(hbox), name_entry, TRUE, TRUE, 0); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(entry_vbox), hbox, TRUE, TRUE, 0); + + cmd_label = gtk_label_new(_("Command line:")); + gtk_box_pack_start(GTK_BOX(hbox), cmd_label, FALSE, FALSE, 0); + + cmd_entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(hbox), cmd_entry, TRUE, TRUE, 0); + + gtk_widget_show_all(entry_vbox); + + help_vbox = gtk_vbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(vbox1), help_vbox, FALSE, FALSE, 0); + + help_label = gtk_label_new + (_("Menu name:\n" + " Use / in menu name to make submenus.\n" + "Command line:\n" + " Begin with:\n" + " | to send message body or selection to command\n" + " > to send user provided text to command\n" + " * to send user provided hidden text to command\n" + " End with:\n" + " | to replace message body or selection with command output\n" + " > to insert command's output without replacing old text\n" + " & to run command asynchronously\n" + " Use:\n" + " %f for message file name\n" + " %F for the list of the file names of selected messages\n" + " %p for the selected message part\n" + " %u for a user provided argument\n" + " %h for a user provided hidden argument\n" + " %s for the text selection")); + gtk_misc_set_alignment(GTK_MISC(help_label), 0, 0.5); + gtk_label_set_justify(GTK_LABEL(help_label), GTK_JUSTIFY_LEFT); + gtk_widget_show(help_label); + gtk_box_pack_start(GTK_BOX(help_vbox), help_label, FALSE, FALSE, 0); + gtk_widget_hide(help_vbox); + + /* register / substitute / delete */ + + reg_hbox = gtk_hbox_new(FALSE, 4); + gtk_widget_show(reg_hbox); + gtk_box_pack_start(GTK_BOX(vbox1), reg_hbox, FALSE, FALSE, 0); + + arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT); + gtk_widget_show(arrow); + gtk_box_pack_start(GTK_BOX(reg_hbox), arrow, FALSE, FALSE, 0); + gtk_widget_set_size_request(arrow, -1, 16); + + btn_hbox = gtk_hbox_new(TRUE, 4); + gtk_widget_show(btn_hbox); + gtk_box_pack_start(GTK_BOX(reg_hbox), btn_hbox, FALSE, FALSE, 0); + + reg_btn = gtk_button_new_with_label(_("Add")); + gtk_widget_show(reg_btn); + gtk_box_pack_start(GTK_BOX(btn_hbox), reg_btn, FALSE, TRUE, 0); + g_signal_connect(G_OBJECT(reg_btn), "clicked", + G_CALLBACK(prefs_actions_register_cb), NULL); + + subst_btn = gtk_button_new_with_label(_(" Replace ")); + gtk_widget_show(subst_btn); + gtk_box_pack_start(GTK_BOX(btn_hbox), subst_btn, FALSE, TRUE, 0); + g_signal_connect(G_OBJECT(subst_btn), "clicked", + G_CALLBACK(prefs_actions_substitute_cb), NULL); + + del_btn = gtk_button_new_with_label(_("Delete")); + gtk_widget_show(del_btn); + gtk_box_pack_start(GTK_BOX(btn_hbox), del_btn, FALSE, TRUE, 0); + g_signal_connect(G_OBJECT(del_btn), "clicked", + G_CALLBACK(prefs_actions_delete_cb), NULL); + + help_toggle = gtk_toggle_button_new_with_label(_(" Syntax help ")); + gtk_widget_show(help_toggle); + gtk_box_pack_end(GTK_BOX(reg_hbox), help_toggle, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(help_toggle), "toggled", + G_CALLBACK(prefs_actions_help_cb), help_vbox); + + cond_hbox = gtk_hbox_new(FALSE, 8); + gtk_widget_show(cond_hbox); + gtk_box_pack_start(GTK_BOX(vbox1), cond_hbox, TRUE, TRUE, 0); + + cond_scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(cond_scrolledwin); + gtk_widget_set_size_request(cond_scrolledwin, -1, + 150 * gtkut_get_dpi_multiplier()); + gtk_box_pack_start(GTK_BOX(cond_hbox), cond_scrolledwin, + TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (cond_scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + title[0] = _("Registered actions"); + cond_clist = gtk_clist_new_with_titles(1, title); + gtk_widget_show(cond_clist); + gtk_container_add(GTK_CONTAINER (cond_scrolledwin), cond_clist); + gtk_clist_set_column_width(GTK_CLIST (cond_clist), 0, 80); + gtk_clist_set_selection_mode(GTK_CLIST (cond_clist), + GTK_SELECTION_BROWSE); + gtkut_clist_set_redraw(GTK_CLIST(cond_clist)); + GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(cond_clist)->column[0].button, + GTK_CAN_FOCUS); + g_signal_connect(G_OBJECT(cond_clist), "select_row", + G_CALLBACK(prefs_actions_select), NULL); + g_signal_connect_after(G_OBJECT(cond_clist), "row_move", + G_CALLBACK(prefs_actions_row_move), NULL); + + btn_vbox = gtk_vbox_new(FALSE, 8); + gtk_widget_show(btn_vbox); + gtk_box_pack_start(GTK_BOX(cond_hbox), btn_vbox, FALSE, FALSE, 0); + + up_btn = gtk_button_new_with_label(_("Up")); + gtk_widget_show(up_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox), up_btn, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(up_btn), "clicked", + G_CALLBACK(prefs_actions_up), NULL); + + down_btn = gtk_button_new_with_label(_("Down")); + gtk_widget_show(down_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox), down_btn, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(down_btn), "clicked", + G_CALLBACK(prefs_actions_down), NULL); + + gtk_widget_show(window); + + actions.window = window; + + actions.confirm_area = confirm_area; + actions.ok_btn = ok_btn; + + actions.name_entry = name_entry; + actions.cmd_entry = cmd_entry; + + actions.actions_clist = cond_clist; +} + +static void prefs_actions_help_cb(GtkWidget *w, gpointer data) +{ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) + gtk_widget_show(GTK_WIDGET(data)); + else + gtk_widget_hide(GTK_WIDGET(data)); +} + +void prefs_actions_read_config(void) +{ + gchar *rcpath; + FILE *fp; + gchar buf[PREFSBUFSIZE]; + gchar *act; + + debug_print("Reading actions configurations...\n"); + + rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ACTIONS_RC, NULL); + if ((fp = g_fopen(rcpath, "rb")) == NULL) { + if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen"); + g_free(rcpath); + return; + } + g_free(rcpath); + + while (prefs_common.actions_list != NULL) { + act = (gchar *)prefs_common.actions_list->data; + prefs_common.actions_list = + g_slist_remove(prefs_common.actions_list, act); + g_free(act); + } + + while (fgets(buf, sizeof(buf), fp) != NULL) { + g_strchomp(buf); + act = strstr(buf, ": "); + if (act && act[2] && + action_get_type(&act[2]) != ACTION_ERROR) + prefs_common.actions_list = + g_slist_append(prefs_common.actions_list, + g_strdup(buf)); + } + fclose(fp); +} + +void prefs_actions_write_config(void) +{ + gchar *rcpath; + PrefFile *pfile; + GSList *cur; + + debug_print("Writing actions configuration...\n"); + + rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ACTIONS_RC, NULL); + if ((pfile= prefs_file_open(rcpath)) == NULL) { + g_warning("failed to write configuration to file\n"); + g_free(rcpath); + return; + } + + for (cur = prefs_common.actions_list; cur != NULL; cur = cur->next) { + gchar *act = (gchar *)cur->data; + if (fputs(act, pfile->fp) == EOF || + fputc('\n', pfile->fp) == EOF) { + FILE_OP_ERROR(rcpath, "fputs || fputc"); + prefs_file_close_revert(pfile); + g_free(rcpath); + return; + } + } + + g_free(rcpath); + + if (prefs_file_close(pfile) < 0) { + g_warning("failed to write configuration to file\n"); + return; + } +} + +static void prefs_actions_set_dialog(void) +{ + GtkCList *clist = GTK_CLIST(actions.actions_clist); + GSList *cur; + gchar *action_str[1]; + gint row; + + gtk_clist_freeze(clist); + gtk_clist_clear(clist); + + action_str[0] = _("(New)"); + row = gtk_clist_append(clist, action_str); + gtk_clist_set_row_data(clist, row, NULL); + + for (cur = prefs_common.actions_list; cur != NULL; cur = cur->next) { + gchar *action[1]; + + action[0] = (gchar *)cur->data; + row = gtk_clist_append(clist, action); + gtk_clist_set_row_data(clist, row, action[0]); + } + + gtk_clist_thaw(clist); +} + +static void prefs_actions_set_list(void) +{ + gint row = 1; + gchar *action; + + g_slist_free(prefs_common.actions_list); + prefs_common.actions_list = NULL; + + while ((action = (gchar *)gtk_clist_get_row_data + (GTK_CLIST(actions.actions_clist), row)) != NULL) { + prefs_common.actions_list = + g_slist_append(prefs_common.actions_list, action); + row++; + } +} + +#define GET_ENTRY(entry) \ + entry_text = gtk_entry_get_text(GTK_ENTRY(entry)) + +static gint prefs_actions_clist_set_row(gint row) +{ + GtkCList *clist = GTK_CLIST(actions.actions_clist); + const gchar *entry_text; + gint len; + gchar action[PREFSBUFSIZE]; + gchar *buf[1]; + + g_return_val_if_fail(row != 0, -1); + + GET_ENTRY(actions.name_entry); + if (entry_text[0] == '\0') { + alertpanel_error(_("Menu name is not set.")); + return -1; + } + + if (strchr(entry_text, ':')) { + alertpanel_error(_("Colon ':' is not allowed in the menu name.")); + return -1; + } + + strncpy(action, entry_text, PREFSBUFSIZE - 1); + g_strstrip(action); + + /* Keep space for the ': ' delimiter */ + len = strlen(action) + 2; + if (len >= PREFSBUFSIZE - 1) { + alertpanel_error(_("Menu name is too long.")); + return -1; + } + + strcat(action, ": "); + + GET_ENTRY(actions.cmd_entry); + + if (entry_text[0] == '\0') { + alertpanel_error(_("Command line not set.")); + return -1; + } + + if (len + strlen(entry_text) >= PREFSBUFSIZE - 1) { + alertpanel_error(_("Menu name and command are too long.")); + return -1; + } + + if (action_get_type(entry_text) == ACTION_ERROR) { + alertpanel_error(_("The command\n%s\nhas a syntax error."), + entry_text); + return -1; + } + + strcat(action, entry_text); + + buf[0] = action; + if (row < 0) + row = gtk_clist_append(clist, buf); + else { + gchar *old_action; + gtk_clist_set_text(clist, row, 0, action); + old_action = (gchar *) gtk_clist_get_row_data(clist, row); + if (old_action) + g_free(old_action); + } + + buf[0] = g_strdup(action); + + gtk_clist_set_row_data(clist, row, buf[0]); + + prefs_actions_set_list(); + + return 0; +} + +/* callback functions */ + +static void prefs_actions_register_cb(GtkWidget *w, gpointer data) +{ + prefs_actions_clist_set_row(-1); +} + +static void prefs_actions_substitute_cb(GtkWidget *w, gpointer data) +{ + GtkCList *clist = GTK_CLIST(actions.actions_clist); + gchar *action; + gint row; + + if (!clist->selection) return; + + row = GPOINTER_TO_INT(clist->selection->data); + if (row == 0) return; + + action = gtk_clist_get_row_data(clist, row); + if (!action) return; + + prefs_actions_clist_set_row(row); +} + +static void prefs_actions_delete_cb(GtkWidget *w, gpointer data) +{ + GtkCList *clist = GTK_CLIST(actions.actions_clist); + gchar *action; + gint row; + + if (!clist->selection) return; + row = GPOINTER_TO_INT(clist->selection->data); + if (row == 0) return; + + if (alertpanel(_("Delete action"), + _("Do you really want to delete this action?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL) != G_ALERTDEFAULT) + return; + + action = gtk_clist_get_row_data(clist, row); + g_free(action); + gtk_clist_remove(clist, row); + prefs_common.actions_list = g_slist_remove(prefs_common.actions_list, + action); +} + +static void prefs_actions_up(GtkWidget *w, gpointer data) +{ + GtkCList *clist = GTK_CLIST(actions.actions_clist); + gint row; + + if (!clist->selection) return; + + row = GPOINTER_TO_INT(clist->selection->data); + if (row > 1) + gtk_clist_row_move(clist, row, row - 1); +} + +static void prefs_actions_down(GtkWidget *w, gpointer data) +{ + GtkCList *clist = GTK_CLIST(actions.actions_clist); + gint row; + + if (!clist->selection) return; + + row = GPOINTER_TO_INT(clist->selection->data); + if (row > 0 && row < clist->rows - 1) + gtk_clist_row_move(clist, row, row + 1); +} + +#define ENTRY_SET_TEXT(entry, str) \ + gtk_entry_set_text(GTK_ENTRY(entry), str ? str : "") + +static void prefs_actions_select(GtkCList *clist, gint row, gint column, + GdkEvent *event) +{ + gchar *action; + gchar *cmd; + gchar buf[PREFSBUFSIZE]; + action = gtk_clist_get_row_data(clist, row); + + if (!action) { + gtk_entry_set_text(GTK_ENTRY(actions.name_entry), ""); + gtk_entry_set_text(GTK_ENTRY(actions.cmd_entry), ""); + return; + } + + strncpy(buf, action, PREFSBUFSIZE - 1); + buf[PREFSBUFSIZE - 1] = '\0'; + cmd = strstr(buf, ": "); + + if (cmd && cmd[2]) { + gtk_entry_set_text(GTK_ENTRY(actions.cmd_entry), &cmd[2]); + *cmd = '\0'; + gtk_entry_set_text(GTK_ENTRY(actions.name_entry), buf); + } +} + +static void prefs_actions_row_move(GtkCList *clist, + gint source_row, gint dest_row) +{ + prefs_actions_set_list(); + if (gtk_clist_row_is_visible(clist, dest_row) != GTK_VISIBILITY_FULL) { + gtk_clist_moveto(clist, dest_row, -1, + source_row < dest_row ? 1.0 : 0.0, 0.0); + } +} + +static gint prefs_actions_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer *data) +{ + prefs_actions_cancel(widget, data); + return TRUE; +} + +static gboolean prefs_actions_key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + prefs_actions_cancel(widget, data); + return FALSE; +} + +static void prefs_actions_cancel(GtkWidget *w, gpointer data) +{ + prefs_actions_read_config(); + gtk_widget_hide(actions.window); + main_window_popup(main_window_get()); + inc_unlock(); +} + +static void prefs_actions_ok(GtkWidget *widget, gpointer data) +{ + GtkItemFactory *ifactory; + MainWindow *mainwin = (MainWindow *)data; + + prefs_actions_write_config(); + ifactory = gtk_item_factory_from_widget(mainwin->menubar); + action_update_mainwin_menu(ifactory, mainwin); + gtk_widget_hide(actions.window); + main_window_popup(main_window_get()); + inc_unlock(); +} + diff --git a/src/prefs_actions.h b/src/prefs_actions.h new file mode 100644 index 0000000..f20f623 --- /dev/null +++ b/src/prefs_actions.h @@ -0,0 +1,29 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2003 Hiroyuki Yamamoto & The Sylpheed Claws Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PREFS_ACTIONS_H__ +#define __PREFS_ACTIONS_H__ + +#include "mainwindow.h" + +void prefs_actions_read_config (void); +void prefs_actions_write_config (void); +void prefs_actions_open (MainWindow *mainwin); + +#endif /* __PREFS_ACTIONS_H__ */ diff --git a/src/prefs_common_dialog.c b/src/prefs_common_dialog.c new file mode 100644 index 0000000..b46c9b7 --- /dev/null +++ b/src/prefs_common_dialog.c @@ -0,0 +1,4807 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "prefs.h" +#include "prefs_ui.h" +#include "prefs_common.h" +#include "prefs_common_dialog.h" +#include "prefs_display_header.h" +#include "prefs_summary_column.h" +#include "mainwindow.h" +#include "summaryview.h" +#include "messageview.h" +#include "manage_window.h" +#include "foldersel.h" +#include "filesel.h" +#include "inc.h" +#include "menu.h" +#include "colorlabel.h" +#include "codeconv.h" +#include "utils.h" +#include "gtkutils.h" +#include "alertpanel.h" +#include "folder.h" +#include "socket.h" +#include "plugin.h" + +static PrefsDialog dialog; + +static struct Receive { + GtkWidget *checkbtn_autochk; + GtkWidget *spinbtn_autochk; + GtkObject *spinbtn_autochk_adj; + + GtkWidget *checkbtn_chkonstartup; + GtkWidget *checkbtn_scan_after_inc; + + GtkWidget *checkbtn_newmsg_notify_window; + GtkWidget *spinbtn_notifywin; + GtkObject *spinbtn_notifywin_adj; +#ifdef G_OS_WIN32 + GtkWidget *checkbtn_newmsg_sound; + GtkWidget *entry_newmsg_sound; +#endif + GtkWidget *checkbtn_newmsg_notify; + GtkWidget *entry_newmsg_notify; + +#ifndef G_OS_WIN32 + GtkWidget *checkbtn_local; + GtkWidget *checkbtn_filter_on_inc; + GtkWidget *entry_spool; +#endif +} receive; + +static struct Send { + GtkWidget *checkbtn_savemsg; + GtkWidget *checkbtn_filter_sent; + GtkWidget *checkbtn_recipients_autoreg; + GtkWidget *checkbtn_show_send_dialog; + + GtkWidget *optmenu_encoding_method; + GtkWidget *optmenu_mime_fencoding_method; + + GtkWidget *checkbtn_check_attach; + GtkWidget *entry_check_attach_str; + GtkWidget *checkbtn_check_recp; + GtkWidget *entry_check_recp_excl; +} p_send; + +static struct Compose { + GtkWidget *checkbtn_autosig; + GtkWidget *entry_sigsep; + + GtkWidget *checkbtn_autoextedit; + GtkWidget *spinbtn_undolevel; + GtkObject *spinbtn_undolevel_adj; + GtkWidget *spinbtn_linewrap; + GtkObject *spinbtn_linewrap_adj; + GtkWidget *checkbtn_wrapquote; + GtkWidget *checkbtn_autowrap; + + GtkWidget *checkbtn_autosave; + GtkWidget *spinbtn_autosave; + GtkObject *spinbtn_autosave_adj; + + GtkWidget *checkbtn_reply_account_autosel; + GtkWidget *checkbtn_quote; + GtkWidget *checkbtn_default_reply_list; + GtkWidget *checkbtn_inherit_recipient_on_self_reply; + GtkWidget *checkbtn_reply_address_only; +} compose; + +static struct Quote { + GtkWidget *entry_quotemark; + GtkWidget *text_quotefmt; + + GtkWidget *entry_fw_quotemark; + GtkWidget *text_fw_quotefmt; +} quote; + +#if USE_GTKSPELL +static struct Spell { + GtkWidget *chkbtn_enable_spell; + GtkWidget *entry_spell_lang; +} spell; +#endif + +static struct Display { + GtkWidget *fontbtn_textfont; + + GtkWidget *chkbtn_folder_unread; + GtkWidget *chkbtn_folder_col_new; + GtkWidget *chkbtn_folder_col_unread; + GtkWidget *chkbtn_folder_col_total; + GtkWidget *entry_ng_abbrev_len; + GtkWidget *spinbtn_ng_abbrev_len; + GtkObject *spinbtn_ng_abbrev_len_adj; + + GtkWidget *chkbtn_swapfrom; + GtkWidget *chkbtn_expand_thread; + GtkWidget *entry_datefmt; + + GtkWidget *optmenu_dispencoding; + GtkWidget *optmenu_outencoding; +} display; + +static struct Message { + GtkWidget *chkbtn_enablecol; + GtkWidget *button_edit_col; + GtkWidget *chkbtn_disphdrpane; + GtkWidget *chkbtn_disphdr; + GtkWidget *chkbtn_html; + GtkWidget *chkbtn_prefer_html; + GtkWidget *chkbtn_htmlonly; + GtkWidget *spinbtn_linespc; + GtkObject *spinbtn_linespc_adj; + + GtkWidget *chkbtn_smoothscroll; + GtkWidget *spinbtn_scrollstep; + GtkObject *spinbtn_scrollstep_adj; + GtkWidget *chkbtn_halfpage; +} message; + +static struct Attach { + GtkWidget *radiobtn_attach_toolbtn_pos; + GtkWidget *chkbtn_show_attach_tab; + GtkWidget *chkbtn_show_files_first; + + GtkWidget *chkbtn_resize_image; + GtkWidget *chkbtn_inline_image; +} attach; + +static struct ColorLabel { + GtkWidget *entry_color[7]; +} colorlabel; + +static struct JunkMail { + GtkWidget *chkbtn_enable_junk; + GtkWidget *entry_junk_learncmd; + GtkWidget *entry_nojunk_learncmd; + GtkWidget *entry_classify_cmd; + GtkWidget *entry_junkfolder; + GtkWidget *chkbtn_filter_on_recv; + GtkWidget *chkbtn_filter_before; + GtkWidget *chkbtn_delete_on_recv; + GtkWidget *chkbtn_nofilter_in_book; + GtkWidget *chkbtn_mark_as_read; +} junk; + +#if USE_GPGME +static struct Privacy { + GtkWidget *checkbtn_auto_check_signatures; + GtkWidget *checkbtn_gpg_signature_popup; + GtkWidget *checkbtn_store_passphrase; + GtkWidget *spinbtn_store_passphrase; + GtkObject *spinbtn_store_passphrase_adj; +#ifndef G_OS_WIN32 + GtkWidget *checkbtn_passphrase_grab; +#endif + GtkWidget *checkbtn_gpg_warning; +} privacy; +#endif + +static struct Interface { + GtkWidget *checkbtn_always_show_msg; + GtkWidget *checkbtn_always_mark_read; + GtkWidget *checkbtn_openunread; + GtkWidget *checkbtn_remember_lastsel; + /* GtkWidget *checkbtn_mark_as_read_on_newwin; */ + GtkWidget *checkbtn_openinbox; + GtkWidget *checkbtn_openinbox_startup; + GtkWidget *checkbtn_change_account_on_folder_sel; + GtkWidget *checkbtn_immedexec; +#ifndef G_OS_WIN32 + GtkWidget *checkbtn_comply_gnome_hig; +#endif + GtkWidget *checkbtn_show_trayicon; + GtkWidget *checkbtn_minimize_to_tray; + GtkWidget *checkbtn_tray_toggle_window; +} iface; + +static struct Other { + GtkWidget *optmenu_recvdialog; + GtkWidget *checkbtn_no_recv_err_panel; + GtkWidget *checkbtn_close_recv_dialog; + + GtkWidget *checkbtn_addaddrbyclick; + GtkWidget *checkbtn_add_address_only; + GtkWidget *radiobtn_addr_compl; + + GtkWidget *checkbtn_confonexit; + GtkWidget *checkbtn_cleanonexit; + GtkWidget *checkbtn_askonclean; + GtkWidget *checkbtn_warnqueued; + + GtkWidget *radiobtn_online_mode; +} other; + +static struct Extcmd { + GtkWidget *uri_combo; + GtkWidget *uri_entry; + GtkWidget *exteditor_combo; + GtkWidget *exteditor_entry; + + GtkWidget *checkbtn_printcmd; + GtkWidget *printcmd_entry; + + GtkWidget *checkbtn_incext; + GtkWidget *entry_incext; + GtkWidget *button_incext; + + GtkWidget *checkbtn_extsend; + GtkWidget *entry_extsend; + GtkWidget *button_extsend; +} extcmd; + +#if USE_UPDATE_CHECK +static struct UpdateCheck { + GtkWidget *checkbtn_autoupdate; + GtkWidget *checkbtn_useproxy; + GtkWidget *entry_proxyhost; +} update_check; +#endif + +static struct Advanced { + GtkWidget *checkbtn_strict_cache_check; + + GtkWidget *spinbtn_iotimeout; + GtkObject *spinbtn_iotimeout_adj; +} advanced; + +static struct MessageColorButtons { + GtkWidget *quote_level1_btn; + GtkWidget *quote_level2_btn; + GtkWidget *quote_level3_btn; + GtkWidget *uri_btn; +} color_buttons; + +static struct KeybindDialog { + GtkWidget *window; + GtkWidget *combo; +} keybind; + +static GtkWidget *quote_desc_win; +static GtkWidget *quote_color_win; +static GtkWidget *color_dialog; + +static void prefs_common_charset_set_data_from_optmenu (PrefParam *pparam); +static void prefs_common_charset_set_optmenu (PrefParam *pparam); +static void prefs_common_recv_dialog_set_optmenu (PrefParam *pparam); +static void prefs_common_uri_set_data_from_entry (PrefParam *pparam); +static void prefs_common_uri_set_entry (PrefParam *pparam); + +static void prefs_common_addr_compl_set_data_from_radiobtn (PrefParam *pparam); +static void prefs_common_addr_compl_set_radiobtn (PrefParam *pparam); + +static void prefs_common_attach_toolbtn_pos_set_data_from_radiobtn (PrefParam *pparam); +static void prefs_common_attach_toolbtn_pos_set_radiobtn (PrefParam *pparam); + +static void prefs_common_online_mode_set_data_from_radiobtn(PrefParam *pparam); +static void prefs_common_online_mode_set_radiobtn (PrefParam *pparam); + +static PrefsUIData ui_data[] = { + /* Receive */ + {"autochk_newmail", &receive.checkbtn_autochk, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"autochk_interval", &receive.spinbtn_autochk, + prefs_set_data_from_spinbtn, prefs_set_spinbtn}, + {"check_on_startup", &receive.checkbtn_chkonstartup, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"scan_all_after_inc", &receive.checkbtn_scan_after_inc, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"enable_newmsg_notify", &receive.checkbtn_newmsg_notify, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"newmsg_notify_command", &receive.entry_newmsg_notify, + prefs_set_data_from_entry, prefs_set_entry}, +#ifdef G_OS_WIN32 + {"enable_newmsg_notify_sound", &receive.checkbtn_newmsg_sound, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"newmsg_notify_sound", &receive.entry_newmsg_sound, + prefs_set_data_from_entry, prefs_set_entry}, +#endif + {"enable_newmsg_notify_window", &receive.checkbtn_newmsg_notify_window, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"notify_window_period", &receive.spinbtn_notifywin, + prefs_set_data_from_spinbtn, prefs_set_spinbtn}, + +#ifndef G_OS_WIN32 + {"inc_local", &receive.checkbtn_local, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"filter_on_inc_local", &receive.checkbtn_filter_on_inc, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"spool_path", &receive.entry_spool, + prefs_set_data_from_entry, prefs_set_entry}, +#endif + + /* Send */ + {"save_message", &p_send.checkbtn_savemsg, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"filter_sent_message", &p_send.checkbtn_filter_sent, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"recipients_autoreg", &p_send.checkbtn_recipients_autoreg, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"show_send_dialog", &p_send.checkbtn_show_send_dialog, + prefs_set_data_from_toggle, prefs_set_toggle}, + + {"encoding_method", &p_send.optmenu_encoding_method, + prefs_set_data_from_optmenu, prefs_set_optmenu}, + {"mime_filename_encoding_method", &p_send.optmenu_mime_fencoding_method, + prefs_set_data_from_optmenu, prefs_set_optmenu}, + {"check_attach", &p_send.checkbtn_check_attach, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"check_attach_str", &p_send.entry_check_attach_str, + prefs_set_data_from_entry, prefs_set_entry}, + {"check_recipients", &p_send.checkbtn_check_recp, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"check_recp_exclude", &p_send.entry_check_recp_excl, + prefs_set_data_from_entry, prefs_set_entry}, + + /* {"allow_jisx0201_kana", NULL, NULL, NULL}, */ + + /* Compose */ + {"auto_signature", &compose.checkbtn_autosig, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"signature_separator", &compose.entry_sigsep, + prefs_set_data_from_entry, prefs_set_entry}, + + {"auto_ext_editor", &compose.checkbtn_autoextedit, + prefs_set_data_from_toggle, prefs_set_toggle}, + + {"undo_level", &compose.spinbtn_undolevel, + prefs_set_data_from_spinbtn, prefs_set_spinbtn}, + + {"linewrap_length", &compose.spinbtn_linewrap, + prefs_set_data_from_spinbtn, prefs_set_spinbtn}, + {"linewrap_quotation", &compose.checkbtn_wrapquote, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"linewrap_auto", &compose.checkbtn_autowrap, + prefs_set_data_from_toggle, prefs_set_toggle}, + + {"enable_autosave", &compose.checkbtn_autosave, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"autosave_interval", &compose.spinbtn_autosave, + prefs_set_data_from_spinbtn, prefs_set_spinbtn}, + + {"reply_with_quote", &compose.checkbtn_quote, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"reply_account_autoselect", + &compose.checkbtn_reply_account_autosel, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"default_reply_list", &compose.checkbtn_default_reply_list, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"inherit_recipient_on_self_reply", + &compose.checkbtn_inherit_recipient_on_self_reply, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"reply_address_only", + &compose.checkbtn_reply_address_only, + prefs_set_data_from_toggle, prefs_set_toggle}, + + /* {"show_ruler", NULL, NULL, NULL}, */ + + /* Quote */ + {"reply_quote_mark", "e.entry_quotemark, + prefs_set_data_from_entry, prefs_set_entry}, + {"reply_quote_format", "e.text_quotefmt, + prefs_set_data_from_text, prefs_set_text}, + + {"forward_quote_mark", "e.entry_fw_quotemark, + prefs_set_data_from_entry, prefs_set_entry}, + {"forward_quote_format", "e.text_fw_quotefmt, + prefs_set_data_from_text, prefs_set_text}, + +#if USE_GTKSPELL + /* Spelling */ + {"check_spell", &spell.chkbtn_enable_spell, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"spell_lang", &spell.entry_spell_lang, + prefs_set_data_from_entry, prefs_set_entry}, +#endif + + /* Display */ + {"message_font_name", &display.fontbtn_textfont, + prefs_set_data_from_fontbtn, prefs_set_fontbtn}, + + {"display_folder_unread_num", &display.chkbtn_folder_unread, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"folder_col_show_new", &display.chkbtn_folder_col_new, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"folder_col_show_unread", &display.chkbtn_folder_col_unread, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"folder_col_show_total", &display.chkbtn_folder_col_total, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"newsgroup_abbrev_len", &display.spinbtn_ng_abbrev_len, + prefs_set_data_from_spinbtn, prefs_set_spinbtn}, + + /* Display: Summary View */ + {"enable_swap_from", &display.chkbtn_swapfrom, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"date_format", &display.entry_datefmt, + prefs_set_data_from_entry, prefs_set_entry}, + {"expand_thread", &display.chkbtn_expand_thread, + prefs_set_data_from_toggle, prefs_set_toggle}, + +#if 0 + {"enable_rules_hint", NULL, NULL, NULL}, +#endif /* 0 */ + + /* Message */ + {"enable_color", &message.chkbtn_enablecol, + prefs_set_data_from_toggle, prefs_set_toggle}, + +#if 0 + {"convert_mb_alnum", &message.chkbtn_mbalnum, + prefs_set_data_from_toggle, prefs_set_toggle}, +#endif + {"display_header_pane", &message.chkbtn_disphdrpane, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"display_header", &message.chkbtn_disphdr, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"render_html", &message.chkbtn_html, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"alt_prefer_html", &message.chkbtn_prefer_html, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"html_only_as_attach", &message.chkbtn_htmlonly, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"line_space", &message.spinbtn_linespc, + prefs_set_data_from_spinbtn, prefs_set_spinbtn}, + + /* {"textview_cursor_visible", NULL, NULL, NULL}, */ + + {"enable_smooth_scroll", &message.chkbtn_smoothscroll, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"scroll_step", &message.spinbtn_scrollstep, + prefs_set_data_from_spinbtn, prefs_set_spinbtn}, + {"scroll_half_page", &message.chkbtn_halfpage, + prefs_set_data_from_toggle, prefs_set_toggle}, + + /* Attachment */ + {"attach_toolbutton_pos", &attach.radiobtn_attach_toolbtn_pos, + prefs_common_attach_toolbtn_pos_set_data_from_radiobtn, + prefs_common_attach_toolbtn_pos_set_radiobtn}, + {"show_attach_tab", &attach.chkbtn_show_attach_tab, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"show_attached_files_first", &attach.chkbtn_show_files_first, + prefs_set_data_from_toggle, prefs_set_toggle}, + + {"resize_image", &attach.chkbtn_resize_image, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"inline_image", &attach.chkbtn_inline_image, + prefs_set_data_from_toggle, prefs_set_toggle}, + + /* Encoding */ + {"default_encoding", &display.optmenu_dispencoding, + prefs_common_charset_set_data_from_optmenu, + prefs_common_charset_set_optmenu}, + {"outgoing_charset", &display.optmenu_outencoding, + prefs_common_charset_set_data_from_optmenu, + prefs_common_charset_set_optmenu}, + + /* Junk mail */ + {"enable_junk", &junk.chkbtn_enable_junk, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"junk_learn_command", &junk.entry_junk_learncmd, + prefs_set_data_from_entry, prefs_set_entry}, + {"nojunk_learn_command", &junk.entry_nojunk_learncmd, + prefs_set_data_from_entry, prefs_set_entry}, + {"junk_classify_command", &junk.entry_classify_cmd, + prefs_set_data_from_entry, prefs_set_entry}, + {"junk_folder", &junk.entry_junkfolder, + prefs_set_data_from_entry, prefs_set_entry}, + {"filter_junk_on_receive", &junk.chkbtn_filter_on_recv, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"filter_junk_before", &junk.chkbtn_filter_before, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"delete_junk_on_receive", &junk.chkbtn_delete_on_recv, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"nofilter_junk_sender_in_book", &junk.chkbtn_nofilter_in_book, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"mark_junk_as_read", &junk.chkbtn_mark_as_read, + prefs_set_data_from_toggle, prefs_set_toggle}, + +#if USE_GPGME + /* Privacy */ + {"auto_check_signatures", + &privacy.checkbtn_auto_check_signatures, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"gpg_signature_popup", &privacy.checkbtn_gpg_signature_popup, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"store_passphrase", &privacy.checkbtn_store_passphrase, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"store_passphrase_timeout", &privacy.spinbtn_store_passphrase, + prefs_set_data_from_spinbtn, prefs_set_spinbtn}, +#ifndef G_OS_WIN32 + {"passphrase_grab", &privacy.checkbtn_passphrase_grab, + prefs_set_data_from_toggle, prefs_set_toggle}, +#endif /* G_OS_WIN32 */ + {"show_gpg_warning", &privacy.checkbtn_gpg_warning, + prefs_set_data_from_toggle, prefs_set_toggle}, +#endif /* USE_GPGME */ + + /* Interface */ + {"always_show_message_when_selected", + &iface.checkbtn_always_show_msg, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"always_mark_read_on_show_msg", &iface.checkbtn_always_mark_read, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"open_unread_on_enter", &iface.checkbtn_openunread, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"remember_last_selected", &iface.checkbtn_remember_lastsel, + prefs_set_data_from_toggle, prefs_set_toggle}, +#if 0 + {"mark_as_read_on_new_window", + &iface.checkbtn_mark_as_read_on_newwin, + prefs_set_data_from_toggle, prefs_set_toggle}, +#endif + {"open_inbox_on_inc", &iface.checkbtn_openinbox, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"open_inbox_on_startup", &iface.checkbtn_openinbox_startup, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"change_account_on_folder_selection", + &iface.checkbtn_change_account_on_folder_sel, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"immediate_execution", &iface.checkbtn_immedexec, + prefs_set_data_from_toggle, prefs_set_toggle}, + +#ifndef G_OS_WIN32 + {"comply_gnome_hig", &iface.checkbtn_comply_gnome_hig, + prefs_set_data_from_toggle, prefs_set_toggle}, +#endif + {"show_trayicon", &iface.checkbtn_show_trayicon, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"minimize_to_tray", &iface.checkbtn_minimize_to_tray, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"toggle_window_on_trayicon_click", + &iface.checkbtn_tray_toggle_window, + prefs_set_data_from_toggle, prefs_set_toggle}, + + /* Other */ + {"receive_dialog_mode", &other.optmenu_recvdialog, + prefs_set_data_from_optmenu, prefs_common_recv_dialog_set_optmenu}, + {"no_receive_error_panel", &other.checkbtn_no_recv_err_panel, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"close_receive_dialog", &other.checkbtn_close_recv_dialog, + prefs_set_data_from_toggle, prefs_set_toggle}, + + {"add_address_by_click", &other.checkbtn_addaddrbyclick, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"always_add_address_only", &other.checkbtn_add_address_only, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"enable_address_completion", &other.radiobtn_addr_compl, + prefs_common_addr_compl_set_data_from_radiobtn, + prefs_common_addr_compl_set_radiobtn}, + + {"confirm_on_exit", &other.checkbtn_confonexit, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"clean_trash_on_exit", &other.checkbtn_cleanonexit, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"ask_on_cleaning", &other.checkbtn_askonclean, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"warn_queued_on_exit", &other.checkbtn_warnqueued, + prefs_set_data_from_toggle, prefs_set_toggle}, + + {"startup_online_mode", &other.radiobtn_online_mode, + prefs_common_online_mode_set_data_from_radiobtn, + prefs_common_online_mode_set_radiobtn}, + + /* {"logwindow_line_limit", NULL, NULL, NULL}, */ + + /* External commands */ + {"uri_open_command", &extcmd.uri_entry, + prefs_common_uri_set_data_from_entry, prefs_common_uri_set_entry}, + {"ext_editor_command", &extcmd.exteditor_entry, + prefs_set_data_from_entry, prefs_set_entry}, + + {"use_print_command", &extcmd.checkbtn_printcmd, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"print_command", &extcmd.printcmd_entry, + prefs_set_data_from_entry, prefs_set_entry}, + +#ifndef G_OS_WIN32 + {"use_ext_inc", &extcmd.checkbtn_incext, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"ext_inc_path", &extcmd.entry_incext, + prefs_set_data_from_entry, prefs_set_entry}, +#endif + {"use_ext_sendmail", &extcmd.checkbtn_extsend, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"ext_sendmail_cmd", &extcmd.entry_extsend, + prefs_set_data_from_entry, prefs_set_entry}, + + /* Update check */ +#if USE_UPDATE_CHECK + {"auto_update_check", &update_check.checkbtn_autoupdate, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"use_http_proxy", &update_check.checkbtn_useproxy, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"http_proxy_host", &update_check.entry_proxyhost, + prefs_set_data_from_entry, prefs_set_entry}, +#endif + + /* Advanced */ + {"strict_cache_check", &advanced.checkbtn_strict_cache_check, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"io_timeout_secs", &advanced.spinbtn_iotimeout, + prefs_set_data_from_spinbtn, prefs_set_spinbtn}, + + {NULL, NULL, NULL, NULL} +}; + +/* widget creating functions */ +static void prefs_common_create (void); +static void prefs_receive_create (void); +static void prefs_send_create (void); +static void prefs_compose_create (void); +static GtkWidget *prefs_quote_create (void); +#if USE_GTKSPELL +static GtkWidget *prefs_spell_create (void); +#endif +static void prefs_display_create (void); +static GtkWidget *prefs_message_create (void); +static GtkWidget *prefs_attach_create (void); + +static GtkWidget *prefs_colorlabel_create (void); +static void prefs_common_colorlabel_set_dialog (void); +static void prefs_common_colorlabel_update (void); + +static void prefs_junk_create (void); +#if USE_GPGME +static void prefs_privacy_create (void); +#endif +static void prefs_details_create (void); +static GtkWidget *prefs_other_create (void); +static GtkWidget *prefs_extcmd_create (void); +#if USE_UPDATE_CHECK +static GtkWidget *prefs_update_create (void); +#endif +static GtkWidget *prefs_advanced_create (void); + +static void prefs_common_set_encoding_optmenu (GtkOptionMenu *optmenu, + gboolean outgoing); + +static void date_format_ok_btn_clicked (GtkButton *button, + GtkWidget **widget); +static void date_format_cancel_btn_clicked (GtkButton *button, + GtkWidget **widget); +static gboolean date_format_key_pressed (GtkWidget *keywidget, + GdkEventKey *event, + GtkWidget **widget); +static gboolean date_format_on_delete (GtkWidget *dialogwidget, + GdkEventAny *event, + GtkWidget **widget); +static void date_format_entry_on_change (GtkEditable *editable, + GtkLabel *example); +static void date_format_select_row (GtkWidget *date_format_list, + gint row, + gint column, + GdkEventButton *event, + GtkWidget *date_format); +static GtkWidget *date_format_create (GtkButton *button, + void *data); + +static void prefs_quote_description_create (void); +static gboolean prefs_quote_description_key_pressed + (GtkWidget *widget, + GdkEventKey *event, + gpointer data); +static gboolean prefs_quote_description_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); + +static void prefs_quote_colors_dialog (void); +static void prefs_quote_colors_dialog_create (void); +static gint prefs_quote_colors_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean prefs_quote_colors_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); +static void quote_color_set_dialog (GtkWidget *widget, + gpointer data); +static void quote_colors_set_dialog_ok (GtkWidget *widget, + gpointer data); +static void quote_colors_set_dialog_cancel (GtkWidget *widget, + gpointer data); +static gboolean quote_colors_set_dialog_key_pressed + (GtkWidget *widget, + GdkEventKey *event, + gpointer data); +static void set_button_bg_color (GtkWidget *widget, + gint color); +static void prefs_enable_message_color_toggled (void); +static void prefs_recycle_colors_toggled (GtkWidget *widget); + +static void prefs_keybind_select (void); +static gint prefs_keybind_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean prefs_keybind_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); +static void prefs_keybind_cancel (void); +static void prefs_keybind_apply_clicked (GtkWidget *widget); + +static void prefs_common_dispitem_clicked (void); + +static void prefs_common_select_folder_cb (GtkWidget *widget, + gpointer data); + +#ifdef G_OS_WIN32 +static void prefs_common_file_selection_cb (GtkButton *button, + gpointer data); +#endif + +static gint prefs_common_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean prefs_common_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); +static void prefs_common_ok (void); +static void prefs_common_apply (void); +static void prefs_common_cancel (void); + + +void prefs_common_open(void) +{ + static gboolean ui_registered = FALSE; + + inc_lock(); + + if (!ui_registered) { + prefs_register_ui(prefs_common_get_params(), ui_data); + ui_registered = TRUE; + } + + if (!dialog.window) { + prefs_common_create(); + } + + gtkut_box_set_reverse_order(GTK_BOX(dialog.confirm_area), + !prefs_common.comply_gnome_hig); + manage_window_set_transient(GTK_WINDOW(dialog.window)); + gtk_notebook_set_current_page(GTK_NOTEBOOK(dialog.notebook), 0); + gtk_widget_grab_focus(dialog.ok_btn); + + prefs_set_dialog(prefs_common_get_params()); + prefs_common_colorlabel_set_dialog(); + + gtk_widget_show(dialog.window); + + syl_plugin_signal_emit("prefs-common-open", dialog.window); +} + +static void prefs_common_create(void) +{ + gint page = 0; + + debug_print(_("Creating common preferences window...\n")); + + prefs_dialog_create(&dialog); + gtk_window_set_title (GTK_WINDOW(dialog.window), + _("Common Preferences")); + g_signal_connect (G_OBJECT(dialog.window), "delete_event", + G_CALLBACK(prefs_common_deleted), NULL); + g_signal_connect (G_OBJECT(dialog.window), "key_press_event", + G_CALLBACK(prefs_common_key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(dialog.window); + + g_signal_connect (G_OBJECT(dialog.ok_btn), "clicked", + G_CALLBACK(prefs_common_ok), NULL); + g_signal_connect (G_OBJECT(dialog.apply_btn), "clicked", + G_CALLBACK(prefs_common_apply), NULL); + g_signal_connect (G_OBJECT(dialog.cancel_btn), "clicked", + G_CALLBACK(prefs_common_cancel), NULL); + + /* create all widgets on notebook */ + prefs_receive_create(); + SET_NOTEBOOK_LABEL(dialog.notebook, _("Receive"), page++); + prefs_send_create(); + SET_NOTEBOOK_LABEL(dialog.notebook, _("Send"), page++); + prefs_compose_create(); + SET_NOTEBOOK_LABEL(dialog.notebook, _("Compose"), page++); + prefs_display_create(); + SET_NOTEBOOK_LABEL(dialog.notebook, _("Display"), page++); + prefs_junk_create(); + SET_NOTEBOOK_LABEL(dialog.notebook, _("Junk mail"), page++); +#if USE_GPGME + prefs_privacy_create(); + SET_NOTEBOOK_LABEL(dialog.notebook, _("Privacy"), page++); +#endif + prefs_details_create(); + SET_NOTEBOOK_LABEL(dialog.notebook, _("Details"), page++); + + gtk_widget_show_all(dialog.window); +} + +static void prefs_receive_create(void) +{ + GtkWidget *vbox1; + GtkWidget *vbox2; + GtkWidget *vbox3; + GtkWidget *vbox4; + GtkWidget *hbox; + + GtkWidget *hbox_autochk; + GtkWidget *checkbtn_autochk; + GtkWidget *label_autochk1; + GtkObject *spinbtn_autochk_adj; + GtkWidget *spinbtn_autochk; + GtkWidget *label_autochk2; + GtkWidget *checkbtn_chkonstartup; + GtkWidget *checkbtn_scan_after_inc; + + GtkWidget *frame_notify; + GtkWidget *checkbtn_newmsg_notify_window; + GtkWidget *hbox_notifywin; + GtkWidget *hbox_spc; + GtkWidget *label_notifywin; + GtkObject *spinbtn_notifywin_adj; + GtkWidget *spinbtn_notifywin; + GtkWidget *checkbtn_newmsg_notify; + GtkWidget *label_newmsg_notify; + GtkWidget *entry_newmsg_notify; + GtkWidget *label_notify_cmd_desc; +#ifdef G_OS_WIN32 + GtkWidget *checkbtn_newmsg_sound; + GtkWidget *label_newmsg_sound; + GtkWidget *entry_newmsg_sound; + GtkWidget *btn_filesel; +#endif + +#ifndef G_OS_WIN32 + GtkWidget *frame_spool; + GtkWidget *checkbtn_local; + GtkWidget *checkbtn_filter_on_inc; + GtkWidget *label_spool; + GtkWidget *entry_spool; +#endif + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, FALSE, 0); + + /* Auto-checking */ + hbox_autochk = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox_autochk); + gtk_box_pack_start (GTK_BOX (vbox2), hbox_autochk, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (hbox_autochk, checkbtn_autochk, + _("Auto-check new mail")); + + label_autochk1 = gtk_label_new (_("every")); + gtk_widget_show (label_autochk1); + gtk_box_pack_start (GTK_BOX (hbox_autochk), label_autochk1, FALSE, FALSE, 0); + + spinbtn_autochk_adj = gtk_adjustment_new (5, 1, 1000, 1, 10, 0); + spinbtn_autochk = gtk_spin_button_new + (GTK_ADJUSTMENT (spinbtn_autochk_adj), 1, 0); + gtk_widget_show (spinbtn_autochk); + gtk_box_pack_start (GTK_BOX (hbox_autochk), spinbtn_autochk, FALSE, FALSE, 0); + gtk_widget_set_size_request (spinbtn_autochk, 64, -1); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_autochk), TRUE); + + label_autochk2 = gtk_label_new (_("minute(s)")); + gtk_widget_show (label_autochk2); + gtk_box_pack_start (GTK_BOX (hbox_autochk), label_autochk2, FALSE, FALSE, 0); + + SET_TOGGLE_SENSITIVITY(checkbtn_autochk, label_autochk1); + SET_TOGGLE_SENSITIVITY(checkbtn_autochk, spinbtn_autochk); + SET_TOGGLE_SENSITIVITY(checkbtn_autochk, label_autochk2); + + PACK_CHECK_BUTTON (vbox2, checkbtn_chkonstartup, + _("Check new mail on startup")); + PACK_CHECK_BUTTON (vbox2, checkbtn_scan_after_inc, + _("Update all local folders after incorporation")); + + /* New message notify */ + PACK_FRAME(vbox1, frame_notify, _("New message notification")); + + vbox3 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox3); + gtk_container_add (GTK_CONTAINER (frame_notify), vbox3); + gtk_container_set_border_width (GTK_CONTAINER (vbox3), 8); + + vbox4 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox4); + gtk_box_pack_start (GTK_BOX (vbox3), vbox4, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON + (vbox4, checkbtn_newmsg_notify_window, + _("Show notification window when new messages arrive")); + + hbox_notifywin = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox_notifywin); + gtk_box_pack_start (GTK_BOX (vbox4), hbox_notifywin, FALSE, FALSE, 0); + + hbox_spc = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox_spc); + gtk_box_pack_start (GTK_BOX (hbox_notifywin), hbox_spc, FALSE, FALSE, 0); + gtk_widget_set_size_request (hbox_spc, 12, -1); + + label_notifywin = gtk_label_new (_("Display window for")); + gtk_widget_show (label_notifywin); + gtk_box_pack_start (GTK_BOX (hbox_notifywin), label_notifywin, FALSE, FALSE, 0); + + spinbtn_notifywin_adj = gtk_adjustment_new (10, 0, 1000, 1, 10, 0); + spinbtn_notifywin = gtk_spin_button_new + (GTK_ADJUSTMENT (spinbtn_notifywin_adj), 1, 0); + gtk_widget_show (spinbtn_notifywin); + gtk_box_pack_start (GTK_BOX (hbox_notifywin), spinbtn_notifywin, FALSE, FALSE, 0); + gtk_widget_set_size_request (spinbtn_notifywin, 64, -1); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_notifywin), TRUE); + + label_notifywin = gtk_label_new (_("second(s)")); + gtk_widget_show (label_notifywin); + gtk_box_pack_start (GTK_BOX (hbox_notifywin), label_notifywin, FALSE, FALSE, 0); + + SET_TOGGLE_SENSITIVITY(checkbtn_newmsg_notify_window, hbox_notifywin); + + hbox_notifywin = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox_notifywin); + gtk_box_pack_start (GTK_BOX (vbox4), hbox_notifywin, FALSE, FALSE, 0); + + hbox_spc = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox_spc); + gtk_box_pack_start (GTK_BOX (hbox_notifywin), hbox_spc, FALSE, FALSE, 0); + gtk_widget_set_size_request (hbox_spc, 12, -1); + + label_notifywin = gtk_label_new (_("0: don't auto-close")); + gtk_widget_show (label_notifywin); + gtk_box_pack_start (GTK_BOX (hbox_notifywin), label_notifywin, FALSE, FALSE, 0); + gtkut_widget_set_small_font_size (label_notifywin); + + SET_TOGGLE_SENSITIVITY(checkbtn_newmsg_notify_window, hbox_notifywin); + +#ifdef G_OS_WIN32 + PACK_CHECK_BUTTON + (vbox4, checkbtn_newmsg_sound, + _("Play sound when new messages arrive")); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox3), hbox, FALSE, FALSE, 0); + + label_newmsg_sound = gtk_label_new (_("Sound file")); + gtk_widget_show (label_newmsg_sound); + gtk_box_pack_start (GTK_BOX (hbox), label_newmsg_sound, + FALSE, FALSE, 0); + + entry_newmsg_sound = gtk_entry_new (); + gtk_widget_show (entry_newmsg_sound); + gtk_box_pack_start (GTK_BOX (hbox), entry_newmsg_sound, TRUE, TRUE, 0); + + btn_filesel = gtk_button_new_with_label("..."); + gtk_box_pack_start (GTK_BOX (hbox), btn_filesel, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(btn_filesel), "clicked", + G_CALLBACK(prefs_common_file_selection_cb), + entry_newmsg_sound); + + SET_TOGGLE_SENSITIVITY (checkbtn_newmsg_sound, hbox); + + PACK_CHECK_BUTTON + (vbox3, checkbtn_newmsg_notify, + _("Execute command when new messages arrive")); +#else + PACK_CHECK_BUTTON + (vbox4, checkbtn_newmsg_notify, + _("Execute command when new messages arrive")); +#endif + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox3), hbox, FALSE, FALSE, 0); + + label_newmsg_notify = gtk_label_new (_("Command")); + gtk_widget_show (label_newmsg_notify); + gtk_box_pack_start (GTK_BOX (hbox), label_newmsg_notify, + FALSE, FALSE, 0); + + entry_newmsg_notify = gtk_entry_new (); + gtk_widget_show (entry_newmsg_notify); + gtk_box_pack_start (GTK_BOX (hbox), entry_newmsg_notify, TRUE, TRUE, 0); + + SET_TOGGLE_SENSITIVITY (checkbtn_newmsg_notify, hbox); + + PACK_SMALL_LABEL + (vbox3, label_notify_cmd_desc, + _("`%d' will be replaced with the number of new messages.")); + +#ifndef G_OS_WIN32 + PACK_FRAME_WITH_CHECK_BUTTON(vbox1, frame_spool, checkbtn_local, + _("Incorporate from local spool")); + + vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (frame_spool), vbox2); + gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); + SET_TOGGLE_SENSITIVITY (checkbtn_local, vbox2); + + hbox = gtk_hbox_new (FALSE, 32); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (hbox, checkbtn_filter_on_inc, + _("Filter on incorporation")); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + + label_spool = gtk_label_new (_("Spool path")); + gtk_widget_show (label_spool); + gtk_box_pack_start (GTK_BOX (hbox), label_spool, FALSE, FALSE, 0); + + entry_spool = gtk_entry_new (); + gtk_widget_show (entry_spool); + gtk_box_pack_start (GTK_BOX (hbox), entry_spool, TRUE, TRUE, 0); +#endif /* !G_OS_WIN32 */ + + receive.checkbtn_autochk = checkbtn_autochk; + receive.spinbtn_autochk = spinbtn_autochk; + receive.spinbtn_autochk_adj = spinbtn_autochk_adj; + + receive.checkbtn_chkonstartup = checkbtn_chkonstartup; + receive.checkbtn_scan_after_inc = checkbtn_scan_after_inc; + + receive.checkbtn_newmsg_notify_window = checkbtn_newmsg_notify_window; + receive.spinbtn_notifywin = spinbtn_notifywin; + receive.spinbtn_notifywin_adj = spinbtn_notifywin_adj; +#ifdef G_OS_WIN32 + receive.checkbtn_newmsg_sound = checkbtn_newmsg_sound; + receive.entry_newmsg_sound = entry_newmsg_sound; +#endif + receive.checkbtn_newmsg_notify = checkbtn_newmsg_notify; + receive.entry_newmsg_notify = entry_newmsg_notify; + +#ifndef G_OS_WIN32 + receive.checkbtn_local = checkbtn_local; + receive.checkbtn_filter_on_inc = checkbtn_filter_on_inc; + receive.entry_spool = entry_spool; +#endif +} + +static void prefs_send_create(void) +{ + GtkWidget *vbox1; + + GtkWidget *notebook; + GtkWidget *vbox_tab; + + GtkWidget *vbox2; + GtkWidget *hbox1; + GtkWidget *checkbtn_savemsg; + GtkWidget *checkbtn_filter_sent; + GtkWidget *checkbtn_recipients_autoreg; + GtkWidget *checkbtn_show_send_dialog; + GtkWidget *label; + GtkWidget *checkbtn_check_attach; + GtkWidget *entry_check_attach_str; + GtkWidget *checkbtn_check_recp; + GtkWidget *entry_check_recp_excl; + + GtkWidget *optmenu_trencoding; + GtkWidget *optmenu_menu; + GtkWidget *menuitem; + GtkWidget *optmenu_fencoding; + GtkWidget *label_encoding; + GtkWidget *label_encoding_desc; + GtkWidget *label_fencoding; + GtkWidget *label_fencoding_desc; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER); + + notebook = gtk_notebook_new(); + gtk_widget_show(notebook); + gtk_box_pack_start(GTK_BOX(vbox1), notebook, TRUE, TRUE, 0); + + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("General")); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox_tab), vbox2, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (vbox2, checkbtn_savemsg, + _("Save sent messages to outbox")); + PACK_CHECK_BUTTON (vbox2, checkbtn_filter_sent, + _("Apply filter rules to sent messages")); + SET_TOGGLE_SENSITIVITY (checkbtn_savemsg, checkbtn_filter_sent); + PACK_CHECK_BUTTON (vbox2, checkbtn_recipients_autoreg, + _("Automatically add recipients to address book")); + + PACK_CHECK_BUTTON (vbox2, checkbtn_show_send_dialog, + _("Display send dialog")); + + vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox_tab), vbox2, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (vbox2, checkbtn_check_attach, + _("Notify for missing attachments when the following strings (comma-separated) are found in the message body")); + gtk_label_set_line_wrap(GTK_LABEL(GTK_BIN(checkbtn_check_attach)->child), TRUE); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0); + + entry_check_attach_str = gtk_entry_new (); + gtk_widget_show (entry_check_attach_str); + gtk_box_pack_start (GTK_BOX (hbox1), entry_check_attach_str, + TRUE, TRUE, 0); + label = gtk_label_new (_("(Ex: attach)")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox1), label, FALSE, FALSE, 0); + + SET_TOGGLE_SENSITIVITY(checkbtn_check_attach, entry_check_attach_str); + + vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox_tab), vbox2, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (vbox2, checkbtn_check_recp, + _("Confirm recipients before sending")); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0); + label = gtk_label_new + (_("Excluded addresses/domains (comma-separated):")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox1), label, FALSE, FALSE, 0); + + entry_check_recp_excl = gtk_entry_new (); + gtk_widget_show (entry_check_recp_excl); + gtk_box_pack_start (GTK_BOX (vbox2), entry_check_recp_excl, + FALSE, FALSE, 0); + + SET_TOGGLE_SENSITIVITY(checkbtn_check_recp, entry_check_recp_excl); + + /* Encoding */ + + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("Encoding")); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox_tab), hbox1, FALSE, FALSE, 0); + + label_encoding = gtk_label_new (_("Transfer encoding")); + gtk_widget_show (label_encoding); + gtk_box_pack_start (GTK_BOX (hbox1), label_encoding, FALSE, FALSE, 0); + + optmenu_trencoding = gtk_option_menu_new (); + gtk_widget_show (optmenu_trencoding); + gtk_box_pack_start (GTK_BOX (hbox1), optmenu_trencoding, + FALSE, FALSE, 0); + + optmenu_menu = gtk_menu_new(); + +#define SET_MENUITEM(str, data) \ + MENUITEM_ADD(optmenu_menu, menuitem, str, data) + + SET_MENUITEM(_("Automatic"), CTE_AUTO); + SET_MENUITEM("base64", CTE_BASE64); + SET_MENUITEM("quoted-printable", CTE_QUOTED_PRINTABLE); + SET_MENUITEM("8bit", CTE_8BIT); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (optmenu_trencoding), + optmenu_menu); + + PACK_SMALL_LABEL (vbox_tab, label_encoding_desc, + _("Specify Content-Transfer-Encoding used when " + "message body contains non-ASCII characters.")); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox_tab), hbox1, FALSE, FALSE, 0); + + label_fencoding = gtk_label_new (_("MIME filename encoding")); + gtk_widget_show (label_fencoding); + gtk_box_pack_start (GTK_BOX (hbox1), label_fencoding, FALSE, FALSE, 0); + + optmenu_fencoding = gtk_option_menu_new (); + gtk_widget_show (optmenu_fencoding); + gtk_box_pack_start (GTK_BOX (hbox1), optmenu_fencoding, + FALSE, FALSE, 0); + + optmenu_menu = gtk_menu_new(); + + SET_MENUITEM(_("MIME header"), FENC_MIME); + SET_MENUITEM("RFC 2231", FENC_RFC2231); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (optmenu_fencoding), + optmenu_menu); + +#undef SET_MENUITEM + + PACK_SMALL_LABEL + (vbox_tab, label_fencoding_desc, + _("Specify encoding method for MIME filename with " + "non-ASCII characters.\n" + "MIME header: most popular, but violates RFC 2047\n" + "RFC 2231: conforms to standard, but not popular")); + + + p_send.checkbtn_savemsg = checkbtn_savemsg; + p_send.checkbtn_filter_sent = checkbtn_filter_sent; + p_send.checkbtn_recipients_autoreg = checkbtn_recipients_autoreg; + p_send.checkbtn_show_send_dialog = checkbtn_show_send_dialog; + + p_send.optmenu_encoding_method = optmenu_trencoding; + p_send.optmenu_mime_fencoding_method = optmenu_fencoding; + + p_send.checkbtn_check_attach = checkbtn_check_attach; + p_send.entry_check_attach_str = entry_check_attach_str; + p_send.checkbtn_check_recp = checkbtn_check_recp; + p_send.entry_check_recp_excl = entry_check_recp_excl; +} + +static void prefs_compose_create(void) +{ + GtkWidget *vbox1; + + GtkWidget *notebook; + GtkWidget *vbox_tab; + + GtkWidget *hbox1; + GtkWidget *hbox2; + + GtkWidget *frame_sig; + GtkWidget *checkbtn_autosig; + GtkWidget *label_sigsep; + GtkWidget *entry_sigsep; + + GtkWidget *vbox2; + GtkWidget *checkbtn_autoextedit; + GtkWidget *vbox3; + GtkWidget *hbox3; + GtkWidget *hbox4; + GtkWidget *label_undolevel; + GtkObject *spinbtn_undolevel_adj; + GtkWidget *spinbtn_undolevel; + GtkWidget *label_linewrap; + GtkObject *spinbtn_linewrap_adj; + GtkWidget *spinbtn_linewrap; + GtkWidget *checkbtn_wrapquote; + GtkWidget *checkbtn_autowrap; + + GtkWidget *hbox_autosave; + GtkWidget *checkbtn_autosave; + GtkWidget *label_autosave1; + GtkObject *spinbtn_autosave_adj; + GtkWidget *spinbtn_autosave; + GtkWidget *label_autosave2; + + GtkWidget *frame_reply; + GtkWidget *checkbtn_reply_account_autosel; + GtkWidget *checkbtn_quote; + GtkWidget *checkbtn_default_reply_list; + GtkWidget *checkbtn_inherit_recipient_on_self_reply; + GtkWidget *checkbtn_reply_address_only; + + GtkWidget *quote_wid; +#if USE_GTKSPELL + GtkWidget *spell_wid; +#endif + + vbox1 = gtk_vbox_new(FALSE, VSPACING); + gtk_widget_show(vbox1); + gtk_container_add(GTK_CONTAINER(dialog.notebook), vbox1); + gtk_container_set_border_width(GTK_CONTAINER(vbox1), VBOX_BORDER); + + notebook = gtk_notebook_new(); + gtk_widget_show(notebook); + gtk_box_pack_start(GTK_BOX(vbox1), notebook, TRUE, TRUE, 0); + + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("General")); + + /* signature */ + + PACK_FRAME(vbox_tab, frame_sig, _("Signature")); + + hbox1 = gtk_hbox_new (FALSE, 32); + gtk_widget_show (hbox1); + gtk_container_add (GTK_CONTAINER (frame_sig), hbox1); + gtk_container_set_border_width (GTK_CONTAINER (hbox1), 8); + + hbox2 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox2); + gtk_box_pack_start (GTK_BOX (hbox1), hbox2, FALSE, FALSE, 0); + + label_sigsep = gtk_label_new (_("Signature separator")); + gtk_widget_show (label_sigsep); + gtk_box_pack_start (GTK_BOX (hbox2), label_sigsep, FALSE, FALSE, 0); + + entry_sigsep = gtk_entry_new (); + gtk_widget_show (entry_sigsep); + gtk_box_pack_start (GTK_BOX (hbox2), entry_sigsep, FALSE, FALSE, 0); + gtk_widget_set_size_request (entry_sigsep, 64, -1); + + PACK_CHECK_BUTTON (hbox1, checkbtn_autosig, _("Insert automatically")); + + PACK_FRAME(vbox_tab, frame_reply, _("Reply")); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (frame_reply), vbox2); + gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); + + PACK_CHECK_BUTTON (vbox2, checkbtn_reply_account_autosel, + _("Automatically select account for replies")); + PACK_CHECK_BUTTON (vbox2, checkbtn_quote, + _("Quote message when replying")); + PACK_CHECK_BUTTON (vbox2, checkbtn_default_reply_list, + _("Reply to mailing list by Reply button")); + PACK_CHECK_BUTTON (vbox2, checkbtn_inherit_recipient_on_self_reply, + _("Inherit recipients on reply to self messages")); + PACK_CHECK_BUTTON (vbox2, checkbtn_reply_address_only, + _("Set only mail address of recipients when replying")); + + /* editor */ + + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("Editor")); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (vbox_tab), vbox2); + + PACK_CHECK_BUTTON (vbox2, checkbtn_autoextedit, + _("Automatically launch the external editor")); + + PACK_VSPACER (vbox2, vbox3, VSPACING_NARROW_2); + + /* undo */ + + hbox3 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox3); + gtk_box_pack_start (GTK_BOX (vbox2), hbox3, FALSE, FALSE, 0); + + label_undolevel = gtk_label_new (_("Undo level")); + gtk_widget_show (label_undolevel); + gtk_box_pack_start (GTK_BOX (hbox3), label_undolevel, FALSE, FALSE, 0); + + spinbtn_undolevel_adj = gtk_adjustment_new (50, 0, 100, 1, 10, 0); + spinbtn_undolevel = gtk_spin_button_new + (GTK_ADJUSTMENT (spinbtn_undolevel_adj), 1, 0); + gtk_widget_show (spinbtn_undolevel); + gtk_box_pack_start (GTK_BOX (hbox3), spinbtn_undolevel, FALSE, FALSE, 0); + gtk_widget_set_size_request (spinbtn_undolevel, 64, -1); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_undolevel), TRUE); + + PACK_VSPACER (vbox2, vbox3, VSPACING_NARROW_2); + + /* line-wrapping */ + + hbox3 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox3); + gtk_box_pack_start (GTK_BOX (vbox2), hbox3, FALSE, FALSE, 0); + + label_linewrap = gtk_label_new (_("Wrap messages at")); + gtk_widget_show (label_linewrap); + gtk_box_pack_start (GTK_BOX (hbox3), label_linewrap, FALSE, FALSE, 0); + + spinbtn_linewrap_adj = gtk_adjustment_new (72, 20, 1024, 1, 10, 0); + spinbtn_linewrap = gtk_spin_button_new + (GTK_ADJUSTMENT (spinbtn_linewrap_adj), 1, 0); + gtk_widget_show (spinbtn_linewrap); + gtk_box_pack_start (GTK_BOX (hbox3), spinbtn_linewrap, FALSE, FALSE, 0); + gtk_widget_set_size_request (spinbtn_linewrap, 64, -1); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_linewrap), TRUE); + + label_linewrap = gtk_label_new (_("characters")); + gtk_widget_show (label_linewrap); + gtk_box_pack_start (GTK_BOX (hbox3), label_linewrap, FALSE, FALSE, 0); + + PACK_VSPACER (vbox2, vbox3, VSPACING_NARROW_2); + + hbox4 = gtk_hbox_new (FALSE, 32); + gtk_widget_show (hbox4); + gtk_box_pack_start (GTK_BOX (vbox2), hbox4, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (hbox4, checkbtn_wrapquote, _("Wrap quotation")); + + hbox4 = gtk_hbox_new (FALSE, 32); + gtk_widget_show (hbox4); + gtk_box_pack_start (GTK_BOX (vbox2), hbox4, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (hbox4, checkbtn_autowrap, _("Wrap on input")); + + PACK_VSPACER (vbox2, vbox3, VSPACING_NARROW_2); + + hbox_autosave = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox_autosave); + gtk_box_pack_start (GTK_BOX (vbox2), hbox_autosave, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (hbox_autosave, checkbtn_autosave, + _("Auto-save to draft")); + + label_autosave1 = gtk_label_new (_("every")); + gtk_widget_show (label_autosave1); + gtk_box_pack_start (GTK_BOX (hbox_autosave), label_autosave1, + FALSE, FALSE, 0); + + spinbtn_autosave_adj = gtk_adjustment_new (5, 1, 100, 1, 10, 0); + spinbtn_autosave = gtk_spin_button_new + (GTK_ADJUSTMENT (spinbtn_autosave_adj), 1, 0); + gtk_widget_show (spinbtn_autosave); + gtk_box_pack_start (GTK_BOX (hbox_autosave), spinbtn_autosave, + FALSE, FALSE, 0); + gtk_widget_set_size_request (spinbtn_autosave, 64, -1); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_autosave), TRUE); + + label_autosave2 = gtk_label_new (_("minute(s)")); + gtk_widget_show (label_autosave2); + gtk_box_pack_start (GTK_BOX (hbox_autosave), label_autosave2, + FALSE, FALSE, 0); + + SET_TOGGLE_SENSITIVITY(checkbtn_autosave, label_autosave1); + SET_TOGGLE_SENSITIVITY(checkbtn_autosave, spinbtn_autosave); + SET_TOGGLE_SENSITIVITY(checkbtn_autosave, label_autosave2); + + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("Format")); + quote_wid = prefs_quote_create(); + gtk_box_pack_start(GTK_BOX(vbox_tab), quote_wid, FALSE, FALSE, 0); + +#if USE_GTKSPELL + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("Spell checking")); + spell_wid = prefs_spell_create(); + gtk_box_pack_start(GTK_BOX(vbox_tab), spell_wid, FALSE, FALSE, 0); +#endif + + compose.checkbtn_autosig = checkbtn_autosig; + compose.entry_sigsep = entry_sigsep; + + compose.checkbtn_autoextedit = checkbtn_autoextedit; + + compose.spinbtn_undolevel = spinbtn_undolevel; + compose.spinbtn_undolevel_adj = spinbtn_undolevel_adj; + + compose.spinbtn_linewrap = spinbtn_linewrap; + compose.spinbtn_linewrap_adj = spinbtn_linewrap_adj; + compose.checkbtn_wrapquote = checkbtn_wrapquote; + compose.checkbtn_autowrap = checkbtn_autowrap; + + compose.checkbtn_autosave = checkbtn_autosave; + compose.spinbtn_autosave = spinbtn_autosave; + compose.spinbtn_autosave_adj = spinbtn_autosave_adj; + + compose.checkbtn_quote = checkbtn_quote; + compose.checkbtn_reply_account_autosel = + checkbtn_reply_account_autosel; + compose.checkbtn_default_reply_list = checkbtn_default_reply_list; + compose.checkbtn_inherit_recipient_on_self_reply = + checkbtn_inherit_recipient_on_self_reply; + compose.checkbtn_reply_address_only = checkbtn_reply_address_only; +} + +static GtkWidget *prefs_quote_create(void) +{ + GtkWidget *vbox1; + GtkWidget *frame_quote; + GtkWidget *vbox_quote; + GtkWidget *hbox1; + GtkWidget *hbox2; + GtkWidget *label_quotemark; + GtkWidget *entry_quotemark; + GtkWidget *scrolledwin_quotefmt; + GtkWidget *text_quotefmt; + + GtkWidget *entry_fw_quotemark; + GtkWidget *text_fw_quotefmt; + + GtkWidget *btn_quotedesc; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + + /* reply */ + + PACK_FRAME (vbox1, frame_quote, _("Reply format")); + + vbox_quote = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox_quote); + gtk_container_add (GTK_CONTAINER (frame_quote), vbox_quote); + gtk_container_set_border_width (GTK_CONTAINER (vbox_quote), 8); + + hbox1 = gtk_hbox_new (FALSE, 32); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox_quote), hbox1, FALSE, FALSE, 0); + + hbox2 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox2); + gtk_box_pack_start (GTK_BOX (hbox1), hbox2, FALSE, FALSE, 0); + + label_quotemark = gtk_label_new (_("Quotation mark")); + gtk_widget_show (label_quotemark); + gtk_box_pack_start (GTK_BOX (hbox2), label_quotemark, FALSE, FALSE, 0); + + entry_quotemark = gtk_entry_new (); + gtk_widget_show (entry_quotemark); + gtk_box_pack_start (GTK_BOX (hbox2), entry_quotemark, FALSE, FALSE, 0); + gtk_widget_set_size_request (entry_quotemark, 64, -1); + + scrolledwin_quotefmt = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwin_quotefmt); + gtk_box_pack_start (GTK_BOX (vbox_quote), scrolledwin_quotefmt, + TRUE, TRUE, 0); + gtk_scrolled_window_set_policy + (GTK_SCROLLED_WINDOW (scrolledwin_quotefmt), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type + (GTK_SCROLLED_WINDOW (scrolledwin_quotefmt), GTK_SHADOW_IN); + + text_quotefmt = gtk_text_view_new (); + gtk_widget_show (text_quotefmt); + gtk_container_add(GTK_CONTAINER(scrolledwin_quotefmt), text_quotefmt); + gtk_text_view_set_editable (GTK_TEXT_VIEW (text_quotefmt), TRUE); + gtk_widget_set_size_request(text_quotefmt, DEFAULT_ENTRY_WIDTH, 60); + + /* forward */ + + PACK_FRAME (vbox1, frame_quote, _("Forward format")); + + vbox_quote = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox_quote); + gtk_container_add (GTK_CONTAINER (frame_quote), vbox_quote); + gtk_container_set_border_width (GTK_CONTAINER (vbox_quote), 8); + + hbox1 = gtk_hbox_new (FALSE, 32); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox_quote), hbox1, FALSE, FALSE, 0); + + hbox2 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox2); + gtk_box_pack_start (GTK_BOX (hbox1), hbox2, FALSE, FALSE, 0); + + label_quotemark = gtk_label_new (_("Quotation mark")); + gtk_widget_show (label_quotemark); + gtk_box_pack_start (GTK_BOX (hbox2), label_quotemark, FALSE, FALSE, 0); + + entry_fw_quotemark = gtk_entry_new (); + gtk_widget_show (entry_fw_quotemark); + gtk_box_pack_start (GTK_BOX (hbox2), entry_fw_quotemark, + FALSE, FALSE, 0); + gtk_widget_set_size_request (entry_fw_quotemark, 64, -1); + + scrolledwin_quotefmt = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwin_quotefmt); + gtk_box_pack_start (GTK_BOX (vbox_quote), scrolledwin_quotefmt, + TRUE, TRUE, 0); + gtk_scrolled_window_set_policy + (GTK_SCROLLED_WINDOW (scrolledwin_quotefmt), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type + (GTK_SCROLLED_WINDOW (scrolledwin_quotefmt), GTK_SHADOW_IN); + + text_fw_quotefmt = gtk_text_view_new (); + gtk_widget_show (text_fw_quotefmt); + gtk_container_add(GTK_CONTAINER(scrolledwin_quotefmt), + text_fw_quotefmt); + gtk_text_view_set_editable (GTK_TEXT_VIEW (text_fw_quotefmt), TRUE); + gtk_widget_set_size_request (text_fw_quotefmt, DEFAULT_ENTRY_WIDTH, 60); + + hbox1 = gtk_hbox_new (FALSE, 32); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, FALSE, 0); + + btn_quotedesc = + gtk_button_new_with_label (_(" Description of symbols ")); + gtk_widget_show (btn_quotedesc); + gtk_box_pack_start (GTK_BOX (hbox1), btn_quotedesc, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(btn_quotedesc), "clicked", + G_CALLBACK(prefs_quote_description), NULL); + + quote.entry_quotemark = entry_quotemark; + quote.text_quotefmt = text_quotefmt; + quote.entry_fw_quotemark = entry_fw_quotemark; + quote.text_fw_quotefmt = text_fw_quotefmt; + + return vbox1; +} + +#if USE_GTKSPELL +static GtkWidget *prefs_spell_create(void) +{ + GtkWidget *vbox1; + GtkWidget *vbox2; + GtkWidget *frame; + GtkWidget *hbox; + GtkWidget *chkbtn_enable_spell; + GtkWidget *label; + GtkWidget *entry_spell_lang; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + + PACK_FRAME_WITH_CHECK_BUTTON(vbox1, frame, chkbtn_enable_spell, + _("Enable Spell checking")); + + vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (frame), vbox2); + gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); + SET_TOGGLE_SENSITIVITY (chkbtn_enable_spell, vbox2); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Default language:")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + entry_spell_lang = gtk_entry_new (); + gtk_widget_show (entry_spell_lang); + gtk_box_pack_start (GTK_BOX (hbox), entry_spell_lang, TRUE, TRUE, 0); + + spell.chkbtn_enable_spell = chkbtn_enable_spell; + spell.entry_spell_lang = entry_spell_lang; + + return vbox1; +} +#endif /* USE_GTKSPELL */ + +static void prefs_display_create(void) +{ + GtkWidget *vbox1; + + GtkWidget *notebook; + GtkWidget *vbox_tab; + + GtkWidget *table1; + GtkWidget *label_textfont; + GtkWidget *fontbtn_textfont; + GtkWidget *frame_folder; + GtkWidget *chkbtn_folder_unread; + GtkWidget *label_folder_cols; + GtkWidget *hbox1; + GtkWidget *hbox_spc; + GtkWidget *chkbtn_folder_col_new; + GtkWidget *chkbtn_folder_col_unread; + GtkWidget *chkbtn_folder_col_total; + GtkWidget *label_ng_abbrev; + GtkWidget *spinbtn_ng_abbrev_len; + GtkObject *spinbtn_ng_abbrev_len_adj; + GtkWidget *frame_summary; + GtkWidget *vbox2; + GtkWidget *chkbtn_swapfrom; + GtkWidget *chkbtn_expand_thread; + GtkWidget *vbox3; + GtkWidget *label_datefmt; + GtkWidget *button_datefmt; + GtkWidget *entry_datefmt; + GtkWidget *button_dispitem; + + GtkWidget *msg_wid; + GtkWidget *att_wid; + GtkWidget *clabel_wid; + + GtkWidget *label_dispencoding; + GtkWidget *optmenu_dispencoding; + GtkWidget *label_outencoding; + GtkWidget *optmenu_outencoding; + GtkWidget *label_encoding_desc; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER); + + notebook = gtk_notebook_new(); + gtk_widget_show(notebook); + gtk_box_pack_start(GTK_BOX(vbox1), notebook, TRUE, TRUE, 0); + + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("General")); + + table1 = gtk_table_new (1, 2, FALSE); + gtk_widget_show (table1); + gtk_box_pack_start(GTK_BOX(vbox_tab), table1, FALSE, FALSE, 0); + gtk_table_set_row_spacings (GTK_TABLE (table1), 8); + gtk_table_set_col_spacings (GTK_TABLE (table1), 8); + + label_textfont = gtk_label_new (_("Text font")); + gtk_widget_show (label_textfont); + gtk_table_attach (GTK_TABLE (table1), label_textfont, 0, 1, 0, 1, + GTK_FILL, (GTK_EXPAND | GTK_FILL), 0, 0); + + fontbtn_textfont = gtk_font_button_new (); + gtk_widget_show (fontbtn_textfont); + gtk_table_attach (GTK_TABLE (table1), fontbtn_textfont, 1, 2, 0, 1, + (GTK_EXPAND | GTK_FILL), 0, 0, 0); + + /* ---- Folder View ---- */ + + PACK_FRAME(vbox_tab, frame_folder, _("Folder View")); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (frame_folder), vbox2); + gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); + + PACK_CHECK_BUTTON (vbox2, chkbtn_folder_unread, + _("Display unread number next to folder name")); + + PACK_VSPACER(vbox2, vbox3, VSPACING_NARROW_2); + label_folder_cols = gtk_label_new + (_("Displaying message number columns in the folder view:")); + gtk_widget_show(label_folder_cols); + gtk_box_pack_start(GTK_BOX(vbox2), label_folder_cols, FALSE, TRUE, 0); + gtk_misc_set_alignment(GTK_MISC(label_folder_cols), 0, 0.5); + + PACK_VSPACER(vbox2, vbox3, VSPACING_NARROW_2); + hbox1 = gtk_hbox_new(FALSE, 8); + gtk_widget_show(hbox1); + gtk_box_pack_start(GTK_BOX(vbox2), hbox1, FALSE, TRUE, 0); + hbox_spc = gtk_hbox_new(FALSE, 0); + gtk_widget_show(hbox_spc); + gtk_box_pack_start(GTK_BOX(hbox1), hbox_spc, FALSE, FALSE, 0); + gtk_widget_set_size_request(hbox_spc, 8, -1); + PACK_CHECK_BUTTON(hbox1, chkbtn_folder_col_new, _("New")); + PACK_CHECK_BUTTON(hbox1, chkbtn_folder_col_unread, _("Unread")); + PACK_CHECK_BUTTON(hbox1, chkbtn_folder_col_total, _("Total")); + + PACK_VSPACER(vbox2, vbox3, VSPACING_NARROW_2); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, TRUE, 0); + + label_ng_abbrev = gtk_label_new + (_("Abbreviate newsgroups longer than")); + gtk_widget_show (label_ng_abbrev); + gtk_box_pack_start (GTK_BOX (hbox1), label_ng_abbrev, FALSE, FALSE, 0); + + spinbtn_ng_abbrev_len_adj = gtk_adjustment_new (16, 0, 999, 1, 10, 0); + spinbtn_ng_abbrev_len = gtk_spin_button_new + (GTK_ADJUSTMENT (spinbtn_ng_abbrev_len_adj), 1, 0); + gtk_widget_show (spinbtn_ng_abbrev_len); + gtk_box_pack_start (GTK_BOX (hbox1), spinbtn_ng_abbrev_len, + FALSE, FALSE, 0); + gtk_widget_set_size_request (spinbtn_ng_abbrev_len, 56, -1); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_ng_abbrev_len), + TRUE); + + label_ng_abbrev = gtk_label_new + (_("letters")); + gtk_widget_show (label_ng_abbrev); + gtk_box_pack_start (GTK_BOX (hbox1), label_ng_abbrev, FALSE, FALSE, 0); + + /* ---- Summary ---- */ + + PACK_FRAME(vbox_tab, frame_summary, _("Summary View")); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (frame_summary), vbox2); + gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); + + PACK_CHECK_BUTTON + (vbox2, chkbtn_swapfrom, + _("Display recipient on `From' column if sender is yourself")); + PACK_CHECK_BUTTON + (vbox2, chkbtn_expand_thread, _("Expand threads")); + + PACK_VSPACER(vbox2, vbox3, VSPACING_NARROW_2); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, TRUE, 0); + + label_datefmt = gtk_label_new (_("Date format")); + gtk_widget_show (label_datefmt); + gtk_box_pack_start (GTK_BOX (hbox1), label_datefmt, FALSE, FALSE, 0); + + entry_datefmt = gtk_entry_new (); + gtk_widget_show (entry_datefmt); + gtk_box_pack_start (GTK_BOX (hbox1), entry_datefmt, TRUE, TRUE, 0); + + button_datefmt = gtk_button_new_with_label ("... "); + gtk_widget_show (button_datefmt); + gtk_box_pack_start (GTK_BOX (hbox1), button_datefmt, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (button_datefmt), "clicked", + G_CALLBACK (date_format_create), NULL); + + PACK_VSPACER(vbox2, vbox3, VSPACING_NARROW); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, TRUE, 0); + + button_dispitem = gtk_button_new_with_label + (_(" Set display item of summary... ")); + gtk_widget_show (button_dispitem); + gtk_box_pack_start (GTK_BOX (hbox1), button_dispitem, FALSE, TRUE, 0); + g_signal_connect (G_OBJECT (button_dispitem), "clicked", + G_CALLBACK (prefs_common_dispitem_clicked), NULL); + + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("Message")); + msg_wid = prefs_message_create(); + gtk_box_pack_start(GTK_BOX(vbox_tab), msg_wid, FALSE, FALSE, 0); + + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("Attachment")); + att_wid = prefs_attach_create(); + gtk_box_pack_start(GTK_BOX(vbox_tab), att_wid, FALSE, FALSE, 0); + + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("Color label")); + clabel_wid = prefs_colorlabel_create(); + gtk_box_pack_start(GTK_BOX(vbox_tab), clabel_wid, FALSE, FALSE, 0); + + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("Encoding")); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox_tab), hbox1, FALSE, FALSE, 0); + + label_dispencoding = gtk_label_new (_("Default character encoding")); + gtk_widget_show (label_dispencoding); + gtk_box_pack_start (GTK_BOX (hbox1), label_dispencoding, + FALSE, FALSE, 0); + + optmenu_dispencoding = gtk_option_menu_new (); + gtk_widget_show (optmenu_dispencoding); + gtk_box_pack_start (GTK_BOX (hbox1), optmenu_dispencoding, + FALSE, FALSE, 0); + + prefs_common_set_encoding_optmenu + (GTK_OPTION_MENU (optmenu_dispencoding), FALSE); + + PACK_SMALL_LABEL (vbox_tab, label_encoding_desc, + _("This is used when displaying messages with missing character encoding.")); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox_tab), hbox1, FALSE, FALSE, 0); + + label_outencoding = gtk_label_new (_("Outgoing character encoding")); + gtk_widget_show (label_outencoding); + gtk_box_pack_start (GTK_BOX (hbox1), label_outencoding, + FALSE, FALSE, 0); + + optmenu_outencoding = gtk_option_menu_new (); + gtk_widget_show (optmenu_outencoding); + gtk_box_pack_start (GTK_BOX (hbox1), optmenu_outencoding, + FALSE, FALSE, 0); + + prefs_common_set_encoding_optmenu + (GTK_OPTION_MENU (optmenu_outencoding), TRUE); + + PACK_SMALL_LABEL (vbox_tab, label_encoding_desc, + _("If `Automatic' is selected, the optimal encoding " + "for the current locale will be used.")); + + display.fontbtn_textfont = fontbtn_textfont; + + display.chkbtn_folder_unread = chkbtn_folder_unread; + display.chkbtn_folder_col_new = chkbtn_folder_col_new; + display.chkbtn_folder_col_unread = chkbtn_folder_col_unread; + display.chkbtn_folder_col_total = chkbtn_folder_col_total; + display.spinbtn_ng_abbrev_len = spinbtn_ng_abbrev_len; + display.spinbtn_ng_abbrev_len_adj = spinbtn_ng_abbrev_len_adj; + + display.chkbtn_swapfrom = chkbtn_swapfrom; + display.chkbtn_expand_thread = chkbtn_expand_thread; + display.entry_datefmt = entry_datefmt; + + display.optmenu_dispencoding = optmenu_dispencoding; + display.optmenu_outencoding = optmenu_outencoding; +} + +static GtkWidget *prefs_message_create(void) +{ + GtkWidget *vbox1; + GtkWidget *vbox2; + GtkWidget *vbox3; + GtkWidget *hbox1; + GtkWidget *chkbtn_enablecol; + GtkWidget *button_edit_col; + GtkWidget *chkbtn_disphdrpane; + GtkWidget *chkbtn_disphdr; + GtkWidget *button_edit_disphdr; + GtkWidget *chkbtn_html; + GtkWidget *chkbtn_prefer_html; + GtkWidget *chkbtn_htmlonly; + GtkWidget *hbox_linespc; + GtkWidget *label_linespc; + GtkObject *spinbtn_linespc_adj; + GtkWidget *spinbtn_linespc; + + GtkWidget *frame_scr; + GtkWidget *vbox_scr; + GtkWidget *chkbtn_smoothscroll; + GtkWidget *hbox_scr; + GtkWidget *label_scr; + GtkObject *spinbtn_scrollstep_adj; + GtkWidget *spinbtn_scrollstep; + GtkWidget *chkbtn_halfpage; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, FALSE, 0); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, TRUE, 0); + + PACK_CHECK_BUTTON (hbox1, chkbtn_enablecol, + _("Enable coloration of message")); + g_signal_connect(G_OBJECT(chkbtn_enablecol), "toggled", + G_CALLBACK(prefs_enable_message_color_toggled), NULL); + + button_edit_col = gtk_button_new_with_label (_(" Edit... ")); + gtk_widget_show (button_edit_col); + gtk_box_pack_end (GTK_BOX (hbox1), button_edit_col, FALSE, TRUE, 0); + g_signal_connect (G_OBJECT (button_edit_col), "clicked", + G_CALLBACK (prefs_quote_colors_dialog), NULL); + + SET_TOGGLE_SENSITIVITY(chkbtn_enablecol, button_edit_col); + +#if 0 + PACK_CHECK_BUTTON + (vbox2, chkbtn_mbalnum, + _("Display multi-byte alphabet and numeric as\n" + "ASCII character (Japanese only)")); + gtk_label_set_justify (GTK_LABEL (GTK_BIN(chkbtn_mbalnum)->child), + GTK_JUSTIFY_LEFT); +#endif + + PACK_CHECK_BUTTON(vbox2, chkbtn_disphdrpane, + _("Display header pane above message view")); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, TRUE, 0); + + PACK_CHECK_BUTTON(hbox1, chkbtn_disphdr, + _("Display short headers on message view")); + + button_edit_disphdr = gtk_button_new_with_label (_(" Edit... ")); + gtk_widget_show (button_edit_disphdr); + gtk_box_pack_end (GTK_BOX (hbox1), button_edit_disphdr, + FALSE, TRUE, 0); + g_signal_connect (G_OBJECT (button_edit_disphdr), "clicked", + G_CALLBACK (prefs_display_header_open), NULL); + + SET_TOGGLE_SENSITIVITY(chkbtn_disphdr, button_edit_disphdr); + + PACK_CHECK_BUTTON(vbox2, chkbtn_html, + _("Render HTML messages as text")); + PACK_CHECK_BUTTON(vbox2, chkbtn_prefer_html, + _("Prefer HTML in multipart/alternative for display")); + PACK_CHECK_BUTTON(vbox2, chkbtn_htmlonly, + _("Treat HTML only messages as attachment")); + +#if 0 + PACK_CHECK_BUTTON(vbox2, chkbtn_cursor, + _("Display cursor in message view")); +#endif + + PACK_VSPACER(vbox2, vbox3, VSPACING_NARROW_2); + + hbox1 = gtk_hbox_new (FALSE, 32); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, TRUE, 0); + + hbox_linespc = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (hbox1), hbox_linespc, FALSE, TRUE, 0); + + label_linespc = gtk_label_new (_("Line space")); + gtk_widget_show (label_linespc); + gtk_box_pack_start (GTK_BOX (hbox_linespc), label_linespc, + FALSE, FALSE, 0); + + spinbtn_linespc_adj = gtk_adjustment_new (2, 0, 16, 1, 1, 0); + spinbtn_linespc = gtk_spin_button_new + (GTK_ADJUSTMENT (spinbtn_linespc_adj), 1, 0); + gtk_widget_show (spinbtn_linespc); + gtk_box_pack_start (GTK_BOX (hbox_linespc), spinbtn_linespc, + FALSE, FALSE, 0); + gtk_widget_set_size_request (spinbtn_linespc, 64, -1); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_linespc), TRUE); + + label_linespc = gtk_label_new (_("pixel(s)")); + gtk_widget_show (label_linespc); + gtk_box_pack_start (GTK_BOX (hbox_linespc), label_linespc, + FALSE, FALSE, 0); + + PACK_FRAME(vbox1, frame_scr, _("Scroll")); + + vbox_scr = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox_scr); + gtk_container_add (GTK_CONTAINER (frame_scr), vbox_scr); + gtk_container_set_border_width (GTK_CONTAINER (vbox_scr), 8); + + PACK_CHECK_BUTTON(vbox_scr, chkbtn_halfpage, _("Half page")); + + hbox1 = gtk_hbox_new (FALSE, 32); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox_scr), hbox1, FALSE, TRUE, 0); + + PACK_CHECK_BUTTON(hbox1, chkbtn_smoothscroll, _("Smooth scroll")); + + hbox_scr = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox_scr); + gtk_box_pack_start (GTK_BOX (hbox1), hbox_scr, FALSE, FALSE, 0); + + label_scr = gtk_label_new (_("Step")); + gtk_widget_show (label_scr); + gtk_box_pack_start (GTK_BOX (hbox_scr), label_scr, FALSE, FALSE, 0); + + spinbtn_scrollstep_adj = gtk_adjustment_new (1, 1, 100, 1, 10, 0); + spinbtn_scrollstep = gtk_spin_button_new + (GTK_ADJUSTMENT (spinbtn_scrollstep_adj), 1, 0); + gtk_widget_show (spinbtn_scrollstep); + gtk_box_pack_start (GTK_BOX (hbox_scr), spinbtn_scrollstep, + FALSE, FALSE, 0); + gtk_widget_set_size_request (spinbtn_scrollstep, 64, -1); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_scrollstep), + TRUE); + + label_scr = gtk_label_new (_("pixel(s)")); + gtk_widget_show (label_scr); + gtk_box_pack_start (GTK_BOX (hbox_scr), label_scr, FALSE, FALSE, 0); + + SET_TOGGLE_SENSITIVITY (chkbtn_smoothscroll, hbox_scr) + + message.chkbtn_enablecol = chkbtn_enablecol; + message.button_edit_col = button_edit_col; + message.chkbtn_disphdrpane = chkbtn_disphdrpane; + message.chkbtn_disphdr = chkbtn_disphdr; + message.chkbtn_html = chkbtn_html; + message.chkbtn_prefer_html = chkbtn_prefer_html; + message.chkbtn_htmlonly = chkbtn_htmlonly; + message.spinbtn_linespc = spinbtn_linespc; + + message.chkbtn_smoothscroll = chkbtn_smoothscroll; + message.spinbtn_scrollstep = spinbtn_scrollstep; + message.spinbtn_scrollstep_adj = spinbtn_scrollstep_adj; + message.chkbtn_halfpage = chkbtn_halfpage; + + return vbox1; +} + +static GtkWidget *prefs_attach_create(void) +{ + GtkWidget *vbox1; + GtkWidget *vbox2; + GtkWidget *hbox1; + GtkWidget *label; + GtkWidget *radiobtn_attach_toolbtn_pos; + GtkWidget *radiobtn_attach_toolbtn_pos2; + GtkWidget *chkbtn_show_attach_tab; + GtkWidget *chkbtn_show_files_first; + GtkWidget *frame_image; + GtkWidget *vbox_image; + GtkWidget *chkbtn_resize_image; + GtkWidget *chkbtn_inline_image; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, FALSE, 0); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0); + + label = gtk_label_new (_("Position of attachment tool button:")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox1), label, FALSE, FALSE, 0); + + radiobtn_attach_toolbtn_pos = gtk_radio_button_new_with_label + (NULL, _("Left")); + gtk_widget_show (radiobtn_attach_toolbtn_pos); + gtk_box_pack_start (GTK_BOX(hbox1), radiobtn_attach_toolbtn_pos, + FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (radiobtn_attach_toolbtn_pos), MENU_VAL_ID, + GINT_TO_POINTER (0)); + + radiobtn_attach_toolbtn_pos2 = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON (radiobtn_attach_toolbtn_pos), _("Right")); + gtk_widget_show (radiobtn_attach_toolbtn_pos2); + gtk_box_pack_start (GTK_BOX (hbox1), radiobtn_attach_toolbtn_pos2, + FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (radiobtn_attach_toolbtn_pos2), MENU_VAL_ID, + GINT_TO_POINTER (1)); + + PACK_CHECK_BUTTON (vbox2, chkbtn_show_attach_tab, + _("Toggle attachment list view with tab")); + SET_TOGGLE_SENSITIVITY_REV (chkbtn_show_attach_tab, hbox1); + + PACK_CHECK_BUTTON (vbox2, chkbtn_show_files_first, + _("Show attached files first on message view")); + + PACK_FRAME(vbox1, frame_image, _("Images")); + + vbox_image = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox_image); + gtk_container_add (GTK_CONTAINER (frame_image), vbox_image); + gtk_container_set_border_width (GTK_CONTAINER (vbox_image), 8); + + PACK_CHECK_BUTTON(vbox_image, chkbtn_resize_image, + _("Resize attached large images to fit in the window")); + PACK_CHECK_BUTTON(vbox_image, chkbtn_inline_image, + _("Display images as inline")); + + attach.chkbtn_show_attach_tab = chkbtn_show_attach_tab; + attach.radiobtn_attach_toolbtn_pos = radiobtn_attach_toolbtn_pos; + attach.chkbtn_show_files_first = chkbtn_show_files_first; + + attach.chkbtn_resize_image = chkbtn_resize_image; + attach.chkbtn_inline_image = chkbtn_inline_image; + + return vbox1; +} + +static GtkWidget *prefs_colorlabel_create(void) +{ + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *table; + GtkWidget *clabel; + GtkWidget *entry; + gint i; + + vbox = gtk_vbox_new(FALSE, VSPACING); + gtk_widget_show(vbox); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 8); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_widget_show(hbox); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); + + label = gtk_label_new(_("You can specify label names for each color (Work, TODO etc.).")); + gtk_widget_show(label); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + table = gtk_table_new(7, 2, FALSE); + gtk_widget_show(table); + gtk_container_add(GTK_CONTAINER(vbox), table); + gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + + for (i = 0; i < 7; i++) { + clabel = colorlabel_create_color_widget + (colorlabel_get_color(i)); + gtk_widget_show(clabel); + gtk_table_attach(GTK_TABLE(table), clabel, 0, 1, i, i + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 6); + entry = gtk_entry_new(); + gtk_widget_show(entry); + gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i, i + 1, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + colorlabel.entry_color[i] = entry; + } + + return vbox; +} + +static void prefs_common_colorlabel_set_dialog(void) +{ + gint i; + const gchar *text; + + for (i = 0; i < 7; i++) { + if ((text = colorlabel_get_custom_color_text(i))) { + gtk_entry_set_text(GTK_ENTRY(colorlabel.entry_color[i]), + text); + } else { + gtk_entry_set_text(GTK_ENTRY(colorlabel.entry_color[i]), + ""); + } + } +} + +static void prefs_common_colorlabel_update(void) +{ + gint i; + const gchar *text; + + for (i = 0; i < 7; i++) { + text = gtk_entry_get_text(GTK_ENTRY(colorlabel.entry_color[i])); + if (text && text[0] != '\0') + colorlabel_set_color_text(i, text); + else + colorlabel_set_color_text(i, NULL); + } + colorlabel_update_menu(); +} + +static const struct { + gchar *junk_cmd; + gchar *nojunk_cmd; + gchar *classify_cmd; +} junk_presets[] = { +#ifdef G_OS_WIN32 + {"bogofilter -N -s -I", "bogofilter -n -S -I", "bogofilter -I"}, + {"bsfilterw -C -s -u", "bsfilterw -c -S -u", "bsfilterw"}, + {"sylfilter -j", "sylfilter -c", "sylfilter"} +#else + {"bogofilter -N -s -I", "bogofilter -n -S -I", "bogofilter -I"}, + {"bsfilter -C -s -u", "bsfilter -c -S -u", "bsfilter"}, + {"sylfilter -j", "sylfilter -c", "sylfilter"} +#endif +}; + +enum +{ + JUNK_NONE, + JUNK_BOGOFILTER, + JUNK_BSFILTER, + JUNK_SYLFILTER, + + N_JUNK_ITEMS +}; + +static void prefs_junk_preset_activated(GtkMenuItem *menuitem, gpointer data) +{ + gint i; + + i = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID)); + if (i > JUNK_NONE && i < N_JUNK_ITEMS) { + i--; + gtk_entry_set_text(GTK_ENTRY(junk.entry_junk_learncmd), + junk_presets[i].junk_cmd); + gtk_entry_set_text(GTK_ENTRY(junk.entry_nojunk_learncmd), + junk_presets[i].nojunk_cmd); + gtk_entry_set_text(GTK_ENTRY(junk.entry_classify_cmd), + junk_presets[i].classify_cmd); + } +} + +static void prefs_junk_create(void) +{ + GtkWidget *vbox1; + GtkWidget *vbox2; + GtkWidget *frame; + GtkWidget *hbox; + GtkWidget *chkbtn_enable_junk; + GtkWidget *label; + GtkWidget *optmenu_preset; + GtkWidget *menu; + GtkWidget *menuitem; + GtkWidget *entry_junk_learncmd; + GtkWidget *entry_nojunk_learncmd; + GtkWidget *entry_classify_cmd; + GtkWidget *vbox3; + GtkWidget *entry_junkfolder; + GtkWidget *btn_folder; + GtkWidget *chkbtn_filter_on_recv; + GtkWidget *chkbtn_filter_before; + GtkWidget *chkbtn_delete_on_recv; + GtkWidget *chkbtn_nofilter_in_book; + GtkWidget *chkbtn_mark_as_read; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER); + + PACK_FRAME_WITH_CHECK_BUTTON(vbox1, frame, chkbtn_enable_junk, + _("Enable Junk mail control")); + + vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (frame), vbox2); + gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); + SET_TOGGLE_SENSITIVITY (chkbtn_enable_junk, vbox2); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Learning command:")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + optmenu_preset = gtk_option_menu_new (); + gtk_widget_show (optmenu_preset); + gtk_box_pack_end (GTK_BOX (hbox), optmenu_preset, FALSE, FALSE, 0); + + menu = gtk_menu_new (); + MENUITEM_ADD (menu, menuitem, _("(Select preset)"), 0); + MENUITEM_ADD (menu, menuitem, "bogofilter", JUNK_BOGOFILTER); + g_signal_connect (G_OBJECT (menuitem), "activate", + G_CALLBACK (prefs_junk_preset_activated), NULL); + MENUITEM_ADD (menu, menuitem, "bsfilter", JUNK_BSFILTER); + g_signal_connect (G_OBJECT (menuitem), "activate", + G_CALLBACK (prefs_junk_preset_activated), NULL); + MENUITEM_ADD (menu, menuitem, "sylfilter", JUNK_SYLFILTER); + g_signal_connect (G_OBJECT (menuitem), "activate", + G_CALLBACK (prefs_junk_preset_activated), NULL); + gtk_option_menu_set_menu (GTK_OPTION_MENU (optmenu_preset), menu); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Junk")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + entry_junk_learncmd = gtk_entry_new (); + gtk_widget_show (entry_junk_learncmd); + gtk_box_pack_start (GTK_BOX (hbox), entry_junk_learncmd, TRUE, TRUE, 0); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Not Junk")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + entry_nojunk_learncmd = gtk_entry_new (); + gtk_widget_show (entry_nojunk_learncmd); + gtk_box_pack_start (GTK_BOX (hbox), entry_nojunk_learncmd, + TRUE, TRUE, 0); + + PACK_VSPACER(vbox2, vbox3, 0); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Classifying command")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + entry_classify_cmd = gtk_entry_new (); + gtk_widget_show (entry_classify_cmd); + gtk_box_pack_start (GTK_BOX (hbox), entry_classify_cmd, TRUE, TRUE, 0); + + PACK_VSPACER(vbox2, vbox3, 0); + + PACK_SMALL_LABEL (vbox2, label, + _("To classify junk mails automatically, both junk " + "and not junk mails must be learned manually to " + "a certain extent.")); + + PACK_VSPACER(vbox2, vbox3, 0); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Junk folder")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + entry_junkfolder = gtk_entry_new (); + gtk_widget_show (entry_junkfolder); + gtk_box_pack_start (GTK_BOX (hbox), entry_junkfolder, TRUE, TRUE, 0); + + btn_folder = gtk_button_new_with_label (_(" ... ")); + gtk_widget_show (btn_folder); + gtk_box_pack_start (GTK_BOX (hbox), btn_folder, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (btn_folder), "clicked", + G_CALLBACK (prefs_common_select_folder_cb), + entry_junkfolder); + + PACK_VSPACER(vbox2, vbox3, 0); + + PACK_SMALL_LABEL (vbox2, label, + _("The messages which are set as junk mail " + "will be moved to this folder. If empty, " + "the default junk folder will be used.")); + + PACK_VSPACER(vbox2, vbox3, 0); + + vbox3 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox3); + gtk_box_pack_start (GTK_BOX(vbox2), vbox3, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON + (vbox3, chkbtn_filter_on_recv, + _("Filter messages classified as junk on receiving")); + PACK_CHECK_BUTTON + (vbox3, chkbtn_filter_before, + _("Filter junk mails before normal filtering")); + PACK_CHECK_BUTTON + (vbox3, chkbtn_delete_on_recv, + _("Delete junk mails from server on receiving")); + SET_TOGGLE_SENSITIVITY (chkbtn_filter_on_recv, chkbtn_filter_before); + SET_TOGGLE_SENSITIVITY (chkbtn_filter_on_recv, chkbtn_delete_on_recv); + + PACK_CHECK_BUTTON + (vbox3, chkbtn_nofilter_in_book, + _("Do not classify message as junk if sender is in the address book")); + PACK_CHECK_BUTTON (vbox3, chkbtn_mark_as_read, + _("Mark filtered junk mails as read")); + + junk.chkbtn_enable_junk = chkbtn_enable_junk; + junk.entry_junk_learncmd = entry_junk_learncmd; + junk.entry_nojunk_learncmd = entry_nojunk_learncmd; + junk.entry_classify_cmd = entry_classify_cmd; + junk.entry_junkfolder = entry_junkfolder; + junk.chkbtn_filter_on_recv = chkbtn_filter_on_recv; + junk.chkbtn_filter_before = chkbtn_filter_before; + junk.chkbtn_delete_on_recv = chkbtn_delete_on_recv; + junk.chkbtn_nofilter_in_book = chkbtn_nofilter_in_book; + junk.chkbtn_mark_as_read = chkbtn_mark_as_read; +} + +#if USE_GPGME +static void prefs_privacy_create(void) +{ + GtkWidget *vbox1; + GtkWidget *vbox2; + GtkWidget *vbox3; + GtkWidget *hbox1; + GtkWidget *hbox_spc; + GtkWidget *label; + GtkWidget *checkbtn_auto_check_signatures; + GtkWidget *checkbtn_gpg_signature_popup; + GtkWidget *checkbtn_store_passphrase; + GtkObject *spinbtn_store_passphrase_adj; + GtkWidget *spinbtn_store_passphrase; +#ifndef G_OS_WIN32 + GtkWidget *checkbtn_passphrase_grab; +#endif + GtkWidget *checkbtn_gpg_warning; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (vbox2, checkbtn_auto_check_signatures, + _("Automatically check signatures")); + + PACK_CHECK_BUTTON (vbox2, checkbtn_gpg_signature_popup, + _("Show signature check result in a popup window")); + + PACK_CHECK_BUTTON (vbox2, checkbtn_store_passphrase, + _("Store passphrase in memory temporarily")); + + vbox3 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox3); + gtk_box_pack_start (GTK_BOX (vbox2), vbox3, FALSE, FALSE, 0); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox3), hbox1, FALSE, FALSE, 0); + + hbox_spc = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox_spc); + gtk_box_pack_start (GTK_BOX (hbox1), hbox_spc, FALSE, FALSE, 0); + gtk_widget_set_size_request (hbox_spc, 12, -1); + + label = gtk_label_new (_("Expired after")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox1), label, FALSE, FALSE, 0); + + spinbtn_store_passphrase_adj = gtk_adjustment_new (0, 0, 1440, 1, 5, 0); + spinbtn_store_passphrase = gtk_spin_button_new + (GTK_ADJUSTMENT (spinbtn_store_passphrase_adj), 1, 0); + gtk_widget_show (spinbtn_store_passphrase); + gtk_box_pack_start (GTK_BOX (hbox1), spinbtn_store_passphrase, FALSE, FALSE, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_store_passphrase), + TRUE); + gtk_widget_set_size_request (spinbtn_store_passphrase, 64, -1); + + label = gtk_label_new (_("minute(s) ")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox1), label, FALSE, FALSE, 0); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox3), hbox1, FALSE, FALSE, 0); + + hbox_spc = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox_spc); + gtk_box_pack_start (GTK_BOX (hbox1), hbox_spc, FALSE, FALSE, 0); + gtk_widget_set_size_request (hbox_spc, 12, -1); + + PACK_SMALL_LABEL (hbox1, label, + _("Setting to '0' will store the passphrase " + "for the whole session.")); + + SET_TOGGLE_SENSITIVITY (checkbtn_store_passphrase, vbox3); + + PACK_VSPACER (vbox2, vbox3, VSPACING_NARROW_2); + +#ifndef G_OS_WIN32 + PACK_CHECK_BUTTON (vbox2, checkbtn_passphrase_grab, + _("Grab input while entering a passphrase")); +#endif + + PACK_CHECK_BUTTON + (vbox2, checkbtn_gpg_warning, + _("Display warning on startup if GnuPG doesn't work")); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, FALSE, 0); + + privacy.checkbtn_auto_check_signatures + = checkbtn_auto_check_signatures; + privacy.checkbtn_gpg_signature_popup + = checkbtn_gpg_signature_popup; + privacy.checkbtn_store_passphrase = checkbtn_store_passphrase; + privacy.spinbtn_store_passphrase = spinbtn_store_passphrase; + privacy.spinbtn_store_passphrase_adj = spinbtn_store_passphrase_adj; +#ifndef G_OS_WIN32 + privacy.checkbtn_passphrase_grab = checkbtn_passphrase_grab; +#endif + privacy.checkbtn_gpg_warning = checkbtn_gpg_warning; +} +#endif /* USE_GPGME */ + +static void prefs_details_create(void) +{ + GtkWidget *vbox1; + + GtkWidget *notebook; + GtkWidget *vbox_tab; + + GtkWidget *vbox2; + GtkWidget *vbox3; + GtkWidget *checkbtn_always_show_msg; + GtkWidget *checkbtn_always_mark_read; + GtkWidget *checkbtn_openunread; + GtkWidget *checkbtn_remember_lastsel; + /* GtkWidget *checkbtn_mark_as_read_on_newwin; */ + GtkWidget *checkbtn_openinbox; + GtkWidget *checkbtn_openinbox_startup; + GtkWidget *checkbtn_change_account_on_folder_sel; + GtkWidget *checkbtn_immedexec; + GtkWidget *hbox1; + GtkWidget *hbox_spc; + GtkWidget *label; +#ifndef G_OS_WIN32 + GtkWidget *checkbtn_comply_gnome_hig; +#endif + GtkWidget *checkbtn_show_trayicon; + GtkWidget *checkbtn_minimize_to_tray; + GtkWidget *checkbtn_tray_toggle_window; + + GtkWidget *button_keybind; + + GtkWidget *other_wid; + GtkWidget *extcmd_wid; +#if USE_UPDATE_CHECK + GtkWidget *update_wid; +#endif + GtkWidget *advanced_wid; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (dialog.notebook), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER); + + notebook = gtk_notebook_new(); + gtk_widget_show(notebook); + gtk_box_pack_start(GTK_BOX(vbox1), notebook, TRUE, TRUE, 0); + + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("Interface")); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox_tab), vbox2, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON + (vbox2, checkbtn_always_show_msg, + _("Always open messages in summary when selected")); + + PACK_CHECK_BUTTON + (vbox2, checkbtn_always_mark_read, + _("Always mark as read when a message is opened")); + SET_TOGGLE_SENSITIVITY + (checkbtn_always_show_msg, checkbtn_always_mark_read); + + PACK_CHECK_BUTTON + (vbox2, checkbtn_openunread, + _("Open first unread message when a folder is opened")); + SET_TOGGLE_SENSITIVITY_REV + (checkbtn_always_show_msg, checkbtn_openunread); + + PACK_CHECK_BUTTON + (vbox2, checkbtn_remember_lastsel, + _("Remember last selected message")); + +#if 0 + PACK_CHECK_BUTTON + (vbox2, checkbtn_mark_as_read_on_newwin, + _("Only mark message as read when opened in new window")); +#endif + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0); + PACK_CHECK_BUTTON + (hbox1, checkbtn_openinbox, + _("Open inbox after receiving new mail")); + PACK_CHECK_BUTTON + (hbox1, checkbtn_openinbox_startup, _("Open inbox on startup")); + + PACK_CHECK_BUTTON + (vbox2, checkbtn_change_account_on_folder_sel, + _("Change current account on folder open")); + + vbox3 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox3); + gtk_box_pack_start (GTK_BOX (vbox2), vbox3, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON + (vbox3, checkbtn_immedexec, + _("Execute immediately when moving or deleting messages")); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox3), hbox1, FALSE, FALSE, 0); + + hbox_spc = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox_spc); + gtk_box_pack_start (GTK_BOX (hbox1), hbox_spc, FALSE, FALSE, 0); + gtk_widget_set_size_request (hbox_spc, 12, -1); + + PACK_SMALL_LABEL (hbox1, label, + _("Messages will be marked until execution " + "if this is turned off.")); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox_tab), vbox2, FALSE, FALSE, 0); + +#ifndef G_OS_WIN32 + PACK_CHECK_BUTTON (vbox2, checkbtn_comply_gnome_hig, + _("Make the order of buttons comply with GNOME HIG")); +#endif + PACK_CHECK_BUTTON (vbox2, checkbtn_show_trayicon, + _("Display tray icon")); + PACK_CHECK_BUTTON (vbox2, checkbtn_minimize_to_tray, + _("Minimize to tray icon")); + PACK_CHECK_BUTTON (vbox2, checkbtn_tray_toggle_window, + _("Toggle window on trayicon click")); + SET_TOGGLE_SENSITIVITY (checkbtn_show_trayicon, + checkbtn_minimize_to_tray); + SET_TOGGLE_SENSITIVITY (checkbtn_show_trayicon, + checkbtn_tray_toggle_window); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox_tab), hbox1, FALSE, FALSE, 0); + + button_keybind = gtk_button_new_with_label (_(" Set key bindings... ")); + gtk_widget_show (button_keybind); + gtk_box_pack_start (GTK_BOX (hbox1), button_keybind, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (button_keybind), "clicked", + G_CALLBACK (prefs_keybind_select), NULL); + + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("Other")); + other_wid = prefs_other_create(); + gtk_box_pack_start(GTK_BOX(vbox_tab), other_wid, FALSE, FALSE, 0); + + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("External commands")); + extcmd_wid = prefs_extcmd_create(); + gtk_box_pack_start(GTK_BOX(vbox_tab), extcmd_wid, FALSE, FALSE, 0); + +#if USE_UPDATE_CHECK + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("Update")); + update_wid = prefs_update_create(); + gtk_box_pack_start(GTK_BOX(vbox_tab), update_wid, FALSE, FALSE, 0); +#endif + + APPEND_SUB_NOTEBOOK(notebook, vbox_tab, _("Advanced")); + advanced_wid = prefs_advanced_create(); + gtk_box_pack_start(GTK_BOX(vbox_tab), advanced_wid, FALSE, FALSE, 0); + + iface.checkbtn_always_show_msg = checkbtn_always_show_msg; + iface.checkbtn_always_mark_read = checkbtn_always_mark_read; + iface.checkbtn_openunread = checkbtn_openunread; + iface.checkbtn_remember_lastsel = checkbtn_remember_lastsel; +#if 0 + iface.checkbtn_mark_as_read_on_newwin + = checkbtn_mark_as_read_on_newwin; +#endif + iface.checkbtn_openinbox = checkbtn_openinbox; + iface.checkbtn_openinbox_startup = checkbtn_openinbox_startup; + iface.checkbtn_change_account_on_folder_sel = + checkbtn_change_account_on_folder_sel; + iface.checkbtn_immedexec = checkbtn_immedexec; + +#ifndef G_OS_WIN32 + iface.checkbtn_comply_gnome_hig = checkbtn_comply_gnome_hig; +#endif + iface.checkbtn_show_trayicon = checkbtn_show_trayicon; + iface.checkbtn_minimize_to_tray = checkbtn_minimize_to_tray; + iface.checkbtn_tray_toggle_window = checkbtn_tray_toggle_window; +} + +static GtkWidget *prefs_other_create(void) +{ + GtkWidget *vbox1; + + GtkWidget *frame_recv; + GtkWidget *vbox_recv; + GtkWidget *hbox1; + GtkWidget *label; + GtkWidget *optmenu_recvdialog; + GtkWidget *menu; + GtkWidget *menuitem; + GtkWidget *checkbtn_no_recv_err_panel; + GtkWidget *checkbtn_close_recv_dialog; + + GtkWidget *frame_addr; + GtkWidget *vbox_addr; + GtkWidget *checkbtn_addaddrbyclick; + GtkWidget *checkbtn_add_address_only; + GtkWidget *vbox_spc; + GtkWidget *radiobtn_addr_compl; + GtkWidget *radiobtn_compl_tab; + GtkWidget *radiobtn_no_compl; + + GtkWidget *frame_exit; + GtkWidget *vbox_exit; + GtkWidget *checkbtn_confonexit; + GtkWidget *checkbtn_cleanonexit; + GtkWidget *checkbtn_askonclean; + GtkWidget *checkbtn_warnqueued; + + GtkWidget *frame_online; + GtkWidget *vbox_online; + GtkWidget *radiobtn_online_mode; + GtkWidget *radiobtn_start_offline; + GtkWidget *radiobtn_remember_prev_online; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + + PACK_FRAME (vbox1, frame_recv, _("Receive dialog")); + vbox_recv = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox_recv); + gtk_container_add (GTK_CONTAINER (frame_recv), vbox_recv); + gtk_container_set_border_width (GTK_CONTAINER (vbox_recv), 8); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox_recv), hbox1, FALSE, FALSE, 0); + + label = gtk_label_new (_("Show receive dialog")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox1), label, FALSE, FALSE, 0); + + optmenu_recvdialog = gtk_option_menu_new (); + gtk_widget_show (optmenu_recvdialog); + gtk_box_pack_start (GTK_BOX (hbox1), optmenu_recvdialog, + FALSE, FALSE, 0); + + menu = gtk_menu_new (); + MENUITEM_ADD (menu, menuitem, _("Always"), RECV_DIALOG_ALWAYS); + MENUITEM_ADD (menu, menuitem, _("Only on manual receiving"), + RECV_DIALOG_MANUAL); + MENUITEM_ADD (menu, menuitem, _("Never"), RECV_DIALOG_NEVER); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (optmenu_recvdialog), menu); + + PACK_CHECK_BUTTON (vbox_recv, checkbtn_no_recv_err_panel, + _("Don't popup error dialog on receive error")); + + PACK_CHECK_BUTTON (vbox_recv, checkbtn_close_recv_dialog, + _("Close receive dialog when finished")); + + PACK_FRAME (vbox1, frame_addr, _("Address book")); + + vbox_addr = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox_addr); + gtk_container_add (GTK_CONTAINER (frame_addr), vbox_addr); + gtk_container_set_border_width (GTK_CONTAINER (vbox_addr), 8); + + PACK_CHECK_BUTTON + (vbox_addr, checkbtn_addaddrbyclick, + _("Add address to destination when double-clicked")); + + PACK_CHECK_BUTTON + (vbox_addr, checkbtn_add_address_only, + _("Set only mail address when entering recipient from address book")); + + PACK_VSPACER (vbox_addr, vbox_spc, VSPACING_NARROW_2); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox_addr), hbox1, FALSE, FALSE, 0); + + label = gtk_label_new (_("Auto-completion:")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox1), label, FALSE, FALSE, 0); + + radiobtn_addr_compl = gtk_radio_button_new_with_label + (NULL, _("Automatic")); + gtk_widget_show (radiobtn_addr_compl); + gtk_box_pack_start (GTK_BOX (hbox1), radiobtn_addr_compl, + FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (radiobtn_addr_compl), MENU_VAL_ID, + GINT_TO_POINTER (0)); + + radiobtn_compl_tab = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON (radiobtn_addr_compl), _("Start with Tab")); + gtk_widget_show (radiobtn_compl_tab); + gtk_box_pack_start (GTK_BOX (hbox1), radiobtn_compl_tab, + FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (radiobtn_compl_tab), MENU_VAL_ID, + GINT_TO_POINTER (1)); + + radiobtn_no_compl = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON (radiobtn_addr_compl), _("Disable")); + gtk_widget_show (radiobtn_no_compl); + gtk_box_pack_start (GTK_BOX (hbox1), radiobtn_no_compl, + FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (radiobtn_no_compl), MENU_VAL_ID, + GINT_TO_POINTER (2)); + + PACK_FRAME (vbox1, frame_exit, _("On exit")); + + vbox_exit = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox_exit); + gtk_container_add (GTK_CONTAINER (frame_exit), vbox_exit); + gtk_container_set_border_width (GTK_CONTAINER (vbox_exit), 8); + + PACK_CHECK_BUTTON (vbox_exit, checkbtn_confonexit, + _("Confirm on exit")); + + hbox1 = gtk_hbox_new (FALSE, 32); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox_exit), hbox1, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (hbox1, checkbtn_cleanonexit, + _("Empty trash on exit")); + PACK_CHECK_BUTTON (hbox1, checkbtn_askonclean, + _("Ask before emptying")); + SET_TOGGLE_SENSITIVITY (checkbtn_cleanonexit, checkbtn_askonclean); + + PACK_CHECK_BUTTON (vbox_exit, checkbtn_warnqueued, + _("Warn if there are queued messages")); + + PACK_FRAME (vbox1, frame_online, _("Online mode")); + + vbox_online = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox_online); + gtk_container_add (GTK_CONTAINER (frame_online), vbox_online); + gtk_container_set_border_width (GTK_CONTAINER (vbox_online), 8); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox_online), hbox1, FALSE, FALSE, 0); + + radiobtn_online_mode = gtk_radio_button_new_with_label + (NULL, _("Start as online")); + gtk_widget_show (radiobtn_online_mode); + gtk_box_pack_start (GTK_BOX (hbox1), radiobtn_online_mode, + FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (radiobtn_online_mode), MENU_VAL_ID, + GINT_TO_POINTER (1)); + + radiobtn_start_offline = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON (radiobtn_online_mode), _("Start as offline")); + gtk_widget_show (radiobtn_start_offline); + gtk_box_pack_start (GTK_BOX (hbox1), radiobtn_start_offline, + FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (radiobtn_start_offline), MENU_VAL_ID, + GINT_TO_POINTER (0)); + + radiobtn_remember_prev_online = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON (radiobtn_online_mode), _("Remember previous mode")); + gtk_widget_show (radiobtn_remember_prev_online); + gtk_box_pack_start (GTK_BOX (hbox1), radiobtn_remember_prev_online, + FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (radiobtn_remember_prev_online), MENU_VAL_ID, + GINT_TO_POINTER (2)); + + other.optmenu_recvdialog = optmenu_recvdialog; + other.checkbtn_no_recv_err_panel = checkbtn_no_recv_err_panel; + other.checkbtn_close_recv_dialog = checkbtn_close_recv_dialog; + + other.checkbtn_addaddrbyclick = checkbtn_addaddrbyclick; + other.checkbtn_add_address_only = checkbtn_add_address_only; + other.radiobtn_addr_compl = radiobtn_addr_compl; + + other.checkbtn_confonexit = checkbtn_confonexit; + other.checkbtn_cleanonexit = checkbtn_cleanonexit; + other.checkbtn_askonclean = checkbtn_askonclean; + other.checkbtn_warnqueued = checkbtn_warnqueued; + + other.radiobtn_online_mode = radiobtn_online_mode; + + return vbox1; +} + +static GtkWidget *prefs_extcmd_create(void) +{ + GtkWidget *vbox1; + GtkWidget *hbox1; + + GtkWidget *ext_frame; + GtkWidget *ext_table; + + GtkWidget *uri_label; + GtkWidget *uri_combo; + GtkWidget *uri_entry; + + GtkWidget *exteditor_label; + GtkWidget *exteditor_combo; + GtkWidget *exteditor_entry; + + GtkWidget *frame_printcmd; + GtkWidget *vbox_printcmd; + GtkWidget *checkbtn_printcmd; + GtkWidget *printcmd_label; + GtkWidget *printcmd_entry; + +#ifndef G_OS_WIN32 + GtkWidget *vbox2; + GtkWidget *frame_incext; + GtkWidget *checkbtn_incext; + GtkWidget *label_incext; + GtkWidget *entry_incext; +#endif + GtkWidget *frame_extsend; + GtkWidget *vbox_extsend; + GtkWidget *checkbtn_extsend; + GtkWidget *label_extsend; + GtkWidget *entry_extsend; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + + PACK_FRAME(vbox1, ext_frame, + _("External commands (%s will be replaced with file name / URI)")); + + ext_table = gtk_table_new (2, 2, FALSE); + gtk_widget_show (ext_table); + gtk_container_add (GTK_CONTAINER (ext_frame), ext_table); + gtk_container_set_border_width (GTK_CONTAINER (ext_table), 8); + gtk_table_set_row_spacings (GTK_TABLE (ext_table), VSPACING_NARROW); + gtk_table_set_col_spacings (GTK_TABLE (ext_table), 8); + + uri_label = gtk_label_new (_("Web browser")); + gtk_widget_show(uri_label); + gtk_table_attach (GTK_TABLE (ext_table), uri_label, 0, 1, 0, 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_misc_set_alignment (GTK_MISC (uri_label), 1, 0.5); + + uri_combo = gtk_combo_new (); + gtk_widget_show (uri_combo); + gtk_table_attach (GTK_TABLE (ext_table), uri_combo, 1, 2, 0, 1, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + gtkut_combo_set_items (GTK_COMBO (uri_combo), +#if defined(G_OS_WIN32) || defined(__APPLE__) + _("(Default browser)"), +#else + DEFAULT_BROWSER_CMD, + "sensible-browser '%s'", + "firefox -remote 'openURL(%s,new-window)'", + "firefox '%s'", + "mozilla -remote 'openURL(%s,new-window)'", + "mozilla '%s'", + "rxvt -e w3m '%s'", + "rxvt -e lynx '%s'", +#endif + NULL); + uri_entry = GTK_COMBO (uri_combo)->entry; + + exteditor_label = gtk_label_new (_("Editor")); + gtk_widget_show (exteditor_label); + gtk_table_attach (GTK_TABLE (ext_table), exteditor_label, 0, 1, 1, 2, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_misc_set_alignment (GTK_MISC (exteditor_label), 1, 0.5); + + exteditor_combo = gtk_combo_new (); + gtk_widget_show (exteditor_combo); + gtk_table_attach (GTK_TABLE (ext_table), exteditor_combo, 1, 2, 1, 2, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + gtkut_combo_set_items (GTK_COMBO (exteditor_combo), +#ifdef G_OS_WIN32 + "notepad '%s'", +#elif defined(__APPLE__) + "open -t '%s'", +#else + "gedit %s", + "kedit %s", + "emacs %s", + "xemacs %s", + "rxvt -e jed %s", + "rxvt -e vi %s", +#endif + NULL); + exteditor_entry = GTK_COMBO (exteditor_combo)->entry; + + PACK_FRAME_WITH_CHECK_BUTTON(vbox1, frame_printcmd, checkbtn_printcmd, + _("Use external program for printing")); + + vbox_printcmd = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox_printcmd); + gtk_container_add (GTK_CONTAINER (frame_printcmd), vbox_printcmd); + gtk_container_set_border_width (GTK_CONTAINER (vbox_printcmd), 8); + SET_TOGGLE_SENSITIVITY (checkbtn_printcmd, vbox_printcmd); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox_printcmd), hbox1, FALSE, FALSE, 0); + + printcmd_label = gtk_label_new (_("Command")); + gtk_widget_show (printcmd_label); + gtk_box_pack_start (GTK_BOX (hbox1), printcmd_label, FALSE, FALSE, 0); + + printcmd_entry = gtk_entry_new (); + gtk_widget_show (printcmd_entry); + gtk_box_pack_start (GTK_BOX (hbox1), printcmd_entry, TRUE, TRUE, 0); + +#ifndef G_OS_WIN32 + PACK_FRAME_WITH_CHECK_BUTTON(vbox1, frame_incext, checkbtn_incext, + _("Use external program for incorporation")); + + vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (frame_incext), vbox2); + gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); + SET_TOGGLE_SENSITIVITY (checkbtn_incext, vbox2); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0); + + label_incext = gtk_label_new (_("Command")); + gtk_widget_show (label_incext); + gtk_box_pack_start (GTK_BOX (hbox1), label_incext, FALSE, FALSE, 0); + + entry_incext = gtk_entry_new (); + gtk_widget_show (entry_incext); + gtk_box_pack_start (GTK_BOX (hbox1), entry_incext, TRUE, TRUE, 0); +#endif /* !G_OS_WIN32 */ + + PACK_FRAME_WITH_CHECK_BUTTON (vbox1, frame_extsend, checkbtn_extsend, + _("Use external program for sending")); + + vbox_extsend = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox_extsend); + gtk_container_add (GTK_CONTAINER (frame_extsend), vbox_extsend); + gtk_container_set_border_width (GTK_CONTAINER (vbox_extsend), 8); + SET_TOGGLE_SENSITIVITY(checkbtn_extsend, vbox_extsend); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox_extsend), hbox1, FALSE, FALSE, 0); + + label_extsend = gtk_label_new (_("Command")); + gtk_widget_show (label_extsend); + gtk_box_pack_start (GTK_BOX (hbox1), label_extsend, FALSE, FALSE, 0); + + entry_extsend = gtk_entry_new (); + gtk_widget_show (entry_extsend); + gtk_box_pack_start (GTK_BOX (hbox1), entry_extsend, TRUE, TRUE, 0); + + extcmd.uri_combo = uri_combo; + extcmd.uri_entry = uri_entry; + extcmd.checkbtn_printcmd = checkbtn_printcmd; + extcmd.printcmd_entry = printcmd_entry; + + extcmd.exteditor_combo = exteditor_combo; + extcmd.exteditor_entry = exteditor_entry; + +#ifndef G_OS_WIN32 + extcmd.checkbtn_incext = checkbtn_incext; + extcmd.entry_incext = entry_incext; +#endif + extcmd.checkbtn_extsend = checkbtn_extsend; + extcmd.entry_extsend = entry_extsend; + + return vbox1; +} + +#if USE_UPDATE_CHECK +static GtkWidget *prefs_update_create(void) +{ + GtkWidget *vbox1; + GtkWidget *vbox2; + GtkWidget *checkbtn_autoupdate; + GtkWidget *checkbtn_useproxy; + GtkWidget *label; + GtkWidget *entry_proxyhost; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + +#ifndef G_OS_WIN32 + label = gtk_label_new (_("Update check requires 'curl' command.")); + gtk_widget_show (label); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_box_pack_start (GTK_BOX (vbox1), label, FALSE, FALSE, 0); +#endif + + vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (vbox2, checkbtn_autoupdate, + _("Enable auto update check")); + PACK_CHECK_BUTTON (vbox2, checkbtn_useproxy, + _("Use HTTP proxy")); + + label = gtk_label_new (_("HTTP proxy host (hostname:port):")); + gtk_widget_show (label); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0); + + entry_proxyhost = gtk_entry_new (); + gtk_widget_show (entry_proxyhost); + gtk_box_pack_start (GTK_BOX (vbox2), entry_proxyhost, FALSE, FALSE, 0); + + SET_TOGGLE_SENSITIVITY(checkbtn_useproxy, label); + SET_TOGGLE_SENSITIVITY(checkbtn_useproxy, entry_proxyhost); + + update_check.checkbtn_autoupdate = checkbtn_autoupdate; + update_check.checkbtn_useproxy = checkbtn_useproxy; + update_check.entry_proxyhost = entry_proxyhost; + + return vbox1; +} +#endif /* USE_UPDATE_CHECK */ + +static GtkWidget *prefs_advanced_create(void) +{ + GtkWidget *vbox1; + + GtkWidget *vbox2; + GtkWidget *checkbtn_strict_cache_check; + GtkWidget *label; + + GtkWidget *hbox1; + GtkWidget *label_iotimeout; + GtkWidget *spinbtn_iotimeout; + GtkObject *spinbtn_iotimeout_adj; + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + + vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON (vbox2, checkbtn_strict_cache_check, + _("Enable strict checking of the integrity of summary caches")); + PACK_SMALL_LABEL + (vbox2, label, + _("Enable this if the contents of folders have the possibility of modification by other applications.\n" + "This option will degrade the performance of displaying summary.")); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, FALSE, 0); + + label_iotimeout = gtk_label_new (_("Socket I/O timeout:")); + gtk_widget_show (label_iotimeout); + gtk_box_pack_start (GTK_BOX (hbox1), label_iotimeout, FALSE, FALSE, 0); + + spinbtn_iotimeout_adj = gtk_adjustment_new (60, 0, 1000, 1, 10, 0); + spinbtn_iotimeout = gtk_spin_button_new + (GTK_ADJUSTMENT (spinbtn_iotimeout_adj), 1, 0); + gtk_widget_show (spinbtn_iotimeout); + gtk_box_pack_start (GTK_BOX (hbox1), spinbtn_iotimeout, + FALSE, FALSE, 0); + gtk_widget_set_size_request (spinbtn_iotimeout, 64, -1); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_iotimeout), TRUE); + + label_iotimeout = gtk_label_new (_("second(s)")); + gtk_widget_show (label_iotimeout); + gtk_box_pack_start (GTK_BOX (hbox1), label_iotimeout, FALSE, FALSE, 0); + + vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW); + gtk_widget_show (vbox2); + gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, FALSE, 0); + + advanced.checkbtn_strict_cache_check = checkbtn_strict_cache_check; + + advanced.spinbtn_iotimeout = spinbtn_iotimeout; + advanced.spinbtn_iotimeout_adj = spinbtn_iotimeout_adj; + + return vbox1; +} + +static void prefs_common_set_encoding_optmenu(GtkOptionMenu *optmenu, + gboolean outgoing) +{ + GtkWidget *menu; + GtkWidget *menuitem; + + menu = gtk_menu_new(); + +#define SET_MENUITEM(str, data) \ + MENUITEM_ADD(menu, menuitem, str, data) + + if (outgoing) { + SET_MENUITEM(_("Automatic (Recommended)"), NULL); + } else { + SET_MENUITEM(_("Automatic"), NULL); + } + SET_MENUITEM(NULL, NULL); + SET_MENUITEM(_("7bit ascii (US-ASCII)"), CS_US_ASCII); + SET_MENUITEM(NULL, NULL); + SET_MENUITEM(_("Unicode (UTF-8)"), CS_UTF_8); + SET_MENUITEM(NULL, NULL); + SET_MENUITEM(_("Western European (ISO-8859-1)"), CS_ISO_8859_1); + SET_MENUITEM(_("Western European (ISO-8859-15)"), CS_ISO_8859_15); + if (!outgoing) { + SET_MENUITEM(_("Western European (Windows-1252)"), + CS_WINDOWS_1252); + } + SET_MENUITEM(NULL, NULL); + SET_MENUITEM(_("Central European (ISO-8859-2)"), CS_ISO_8859_2); + SET_MENUITEM(NULL, NULL); + SET_MENUITEM(_("Baltic (ISO-8859-13)"), CS_ISO_8859_13); + SET_MENUITEM(_("Baltic (ISO-8859-4)"), CS_ISO_8859_4); + SET_MENUITEM(_("Baltic (Windows-1257)"), CS_WINDOWS_1257); + SET_MENUITEM(NULL, NULL); + SET_MENUITEM(_("Greek (ISO-8859-7)"), CS_ISO_8859_7); + SET_MENUITEM(NULL, NULL); + SET_MENUITEM(_("Arabic (ISO-8859-6)"), CS_ISO_8859_6); + SET_MENUITEM(_("Arabic (Windows-1256)"), CS_WINDOWS_1256); + SET_MENUITEM(NULL, NULL); + SET_MENUITEM(_("Hebrew (ISO-8859-8)"), CS_ISO_8859_8); + SET_MENUITEM(_("Hebrew (Windows-1255)"), CS_WINDOWS_1255); + SET_MENUITEM(NULL, NULL); + SET_MENUITEM(_("Turkish (ISO-8859-9)"), CS_ISO_8859_9); + SET_MENUITEM(NULL, NULL); + SET_MENUITEM(_("Cyrillic (ISO-8859-5)"), CS_ISO_8859_5); + SET_MENUITEM(_("Cyrillic (KOI8-R)"), CS_KOI8_R); + SET_MENUITEM(_("Cyrillic (KOI8-U)"), CS_KOI8_U); + SET_MENUITEM(_("Cyrillic (Windows-1251)"), CS_WINDOWS_1251); + SET_MENUITEM(NULL, NULL); + SET_MENUITEM(_("Japanese (ISO-2022-JP)"), CS_ISO_2022_JP); + if (!outgoing) { + SET_MENUITEM(_("Japanese (EUC-JP)"), CS_EUC_JP); + SET_MENUITEM(_("Japanese (Shift_JIS)"), CS_SHIFT_JIS); + } + SET_MENUITEM(NULL, NULL); + SET_MENUITEM(_("Simplified Chinese (GB2312)"), CS_GB2312); + SET_MENUITEM(_("Simplified Chinese (GBK)"), CS_GBK); + SET_MENUITEM(_("Traditional Chinese (Big5)"), CS_BIG5); + if (!outgoing) { + SET_MENUITEM(_("Traditional Chinese (EUC-TW)"), CS_EUC_TW); + SET_MENUITEM(_("Chinese (ISO-2022-CN)"), CS_ISO_2022_CN); + } + SET_MENUITEM(NULL, NULL); + SET_MENUITEM(_("Korean (EUC-KR)"), CS_EUC_KR); + SET_MENUITEM(NULL, NULL); + SET_MENUITEM(_("Thai (TIS-620)"), CS_TIS_620); + SET_MENUITEM(_("Thai (Windows-874)"), CS_WINDOWS_874); + +#undef SET_MENUITEM + + gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu); +} + +static void date_format_ok_btn_clicked(GtkButton *button, GtkWidget **widget) +{ + GtkWidget *datefmt_sample = NULL; + gchar *text; + + g_return_if_fail(widget != NULL); + g_return_if_fail(*widget != NULL); + g_return_if_fail(display.entry_datefmt != NULL); + + datefmt_sample = GTK_WIDGET(g_object_get_data + (G_OBJECT(*widget), "datefmt_sample")); + g_return_if_fail(datefmt_sample != NULL); + + text = gtk_editable_get_chars(GTK_EDITABLE(datefmt_sample), 0, -1); + g_free(prefs_common.date_format); + prefs_common.date_format = text; + gtk_entry_set_text(GTK_ENTRY(display.entry_datefmt), text); + + gtk_widget_destroy(*widget); + *widget = NULL; +} + +static void date_format_cancel_btn_clicked(GtkButton *button, + GtkWidget **widget) +{ + g_return_if_fail(widget != NULL); + g_return_if_fail(*widget != NULL); + + gtk_widget_destroy(*widget); + *widget = NULL; +} + +static gboolean date_format_key_pressed(GtkWidget *keywidget, + GdkEventKey *event, + GtkWidget **widget) +{ + if (event && event->keyval == GDK_Escape) + date_format_cancel_btn_clicked(NULL, widget); + return FALSE; +} + +static gboolean date_format_on_delete(GtkWidget *dialogwidget, + GdkEventAny *event, GtkWidget **widget) +{ + g_return_val_if_fail(widget != NULL, FALSE); + g_return_val_if_fail(*widget != NULL, FALSE); + + *widget = NULL; + return FALSE; +} + +static void date_format_entry_on_change(GtkEditable *editable, + GtkLabel *example) +{ + time_t cur_time; + struct tm *cal_time; + gchar buffer[100]; + gchar *text; + + cur_time = time(NULL); + cal_time = localtime(&cur_time); + buffer[0] = 0; + text = gtk_editable_get_chars(editable, 0, -1); + if (text) + strftime(buffer, sizeof buffer, text, cal_time); + g_free(text); + + text = conv_codeset_strdup(buffer, conv_get_locale_charset_str(), + CS_UTF_8); + if (!text) + text = g_strdup(buffer); + gtk_label_set_text(example, text); + g_free(text); +} + +static void date_format_select_row(GtkWidget *date_format_list, gint row, + gint column, GdkEventButton *event, + GtkWidget *date_format) +{ + gint cur_pos; + gchar *format; + const gchar *old_format; + gchar *new_format; + GtkWidget *datefmt_sample; + + /* only on double click */ + if (!event || event->type != GDK_2BUTTON_PRESS) return; + + datefmt_sample = GTK_WIDGET(g_object_get_data + (G_OBJECT(date_format), "datefmt_sample")); + + g_return_if_fail(date_format_list != NULL); + g_return_if_fail(date_format != NULL); + g_return_if_fail(datefmt_sample != NULL); + + /* get format from clist */ + gtk_clist_get_text(GTK_CLIST(date_format_list), row, 0, &format); + + cur_pos = gtk_editable_get_position(GTK_EDITABLE(datefmt_sample)); + old_format = gtk_entry_get_text(GTK_ENTRY(datefmt_sample)); + + /* insert the format into the text entry */ + new_format = g_malloc(strlen(old_format) + 3); + + strncpy(new_format, old_format, cur_pos); + new_format[cur_pos] = '\0'; + strcat(new_format, format); + strcat(new_format, &old_format[cur_pos]); + + gtk_entry_set_text(GTK_ENTRY(datefmt_sample), new_format); + gtk_editable_set_position(GTK_EDITABLE(datefmt_sample), cur_pos + 2); + + g_free(new_format); +} + +static GtkWidget *date_format_create(GtkButton *button, void *data) +{ + static GtkWidget *datefmt_win = NULL; + GtkWidget *vbox1; + GtkWidget *scrolledwindow1; + GtkWidget *datefmt_clist; + GtkWidget *table; + GtkWidget *label1; + GtkWidget *label2; + GtkWidget *label3; + GtkWidget *confirm_area; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *datefmt_entry; + + struct { + gchar *fmt; + gchar *txt; + } time_format[] = { + { "%a", NULL }, + { "%A", NULL }, + { "%b", NULL }, + { "%B", NULL }, + { "%c", NULL }, + { "%C", NULL }, + { "%d", NULL }, + { "%H", NULL }, + { "%I", NULL }, + { "%j", NULL }, + { "%m", NULL }, + { "%M", NULL }, + { "%p", NULL }, + { "%S", NULL }, + { "%w", NULL }, + { "%x", NULL }, + { "%y", NULL }, + { "%Y", NULL }, + { "%Z", NULL } + }; + + gchar *titles[2]; + gint i; + const gint TIME_FORMAT_ELEMS = + sizeof time_format / sizeof time_format[0]; + + time_format[0].txt = _("the full abbreviated weekday name"); + time_format[1].txt = _("the full weekday name"); + time_format[2].txt = _("the abbreviated month name"); + time_format[3].txt = _("the full month name"); + time_format[4].txt = _("the preferred date and time for the current locale"); + time_format[5].txt = _("the century number (year/100)"); + time_format[6].txt = _("the day of the month as a decimal number"); + time_format[7].txt = _("the hour as a decimal number using a 24-hour clock"); + time_format[8].txt = _("the hour as a decimal number using a 12-hour clock"); + time_format[9].txt = _("the day of the year as a decimal number"); + time_format[10].txt = _("the month as a decimal number"); + time_format[11].txt = _("the minute as a decimal number"); + time_format[12].txt = _("either AM or PM"); + time_format[13].txt = _("the second as a decimal number"); + time_format[14].txt = _("the day of the week as a decimal number"); + time_format[15].txt = _("the preferred date for the current locale"); + time_format[16].txt = _("the last two digits of a year"); + time_format[17].txt = _("the year as a decimal number"); + time_format[18].txt = _("the time zone or name or abbreviation"); + + if (datefmt_win) return datefmt_win; + + datefmt_win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width(GTK_CONTAINER(datefmt_win), 8); + gtk_window_set_title(GTK_WINDOW(datefmt_win), _("Date format")); + gtk_window_set_position(GTK_WINDOW(datefmt_win), GTK_WIN_POS_CENTER); + gtk_widget_set_size_request(datefmt_win, 440, 280); + + vbox1 = gtk_vbox_new(FALSE, 10); + gtk_widget_show(vbox1); + gtk_container_add(GTK_CONTAINER(datefmt_win), vbox1); + + scrolledwindow1 = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy + (GTK_SCROLLED_WINDOW(scrolledwindow1), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_widget_show(scrolledwindow1); + gtk_box_pack_start(GTK_BOX(vbox1), scrolledwindow1, TRUE, TRUE, 0); + + titles[0] = _("Specifier"); + titles[1] = _("Description"); + datefmt_clist = gtk_clist_new_with_titles(2, titles); + gtk_widget_show(datefmt_clist); + gtk_container_add(GTK_CONTAINER(scrolledwindow1), datefmt_clist); + /* gtk_clist_set_column_width(GTK_CLIST(datefmt_clist), 0, 80); */ + gtk_clist_set_selection_mode(GTK_CLIST(datefmt_clist), + GTK_SELECTION_BROWSE); + + for (i = 0; i < TIME_FORMAT_ELEMS; i++) { + gchar *text[2]; + /* phoney casting necessary because of gtk... */ + text[0] = (gchar *)time_format[i].fmt; + text[1] = (gchar *)time_format[i].txt; + gtk_clist_append(GTK_CLIST(datefmt_clist), text); + } + + table = gtk_table_new(2, 2, FALSE); + gtk_widget_show(table); + gtk_box_pack_start(GTK_BOX(vbox1), table, FALSE, FALSE, 0); + gtk_table_set_row_spacings(GTK_TABLE(table), 4); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + + label1 = gtk_label_new(_("Date format")); + gtk_widget_show(label1); + gtk_table_attach(GTK_TABLE(table), label1, 0, 1, 0, 1, + GTK_FILL, 0, 0, 0); + gtk_label_set_justify(GTK_LABEL(label1), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(label1), 0, 0.5); + + datefmt_entry = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(datefmt_entry), 256); + gtk_widget_show(datefmt_entry); + gtk_table_attach(GTK_TABLE(table), datefmt_entry, 1, 2, 0, 1, + (GTK_EXPAND | GTK_FILL), 0, 0, 0); + + /* we need the "sample" entry box; add it as data so callbacks can + * get the entry box */ + g_object_set_data(G_OBJECT(datefmt_win), "datefmt_sample", + datefmt_entry); + + label2 = gtk_label_new(_("Example")); + gtk_widget_show(label2); + gtk_table_attach(GTK_TABLE(table), label2, 0, 1, 1, 2, + GTK_FILL, 0, 0, 0); + gtk_label_set_justify(GTK_LABEL(label2), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(label2), 0, 0.5); + + label3 = gtk_label_new(""); + gtk_widget_show(label3); + gtk_table_attach(GTK_TABLE(table), label3, 1, 2, 1, 2, + (GTK_EXPAND | GTK_FILL), 0, 0, 0); + gtk_label_set_justify(GTK_LABEL(label3), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(label3), 0, 0.5); + + gtkut_stock_button_set_create(&confirm_area, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, NULL, NULL); + + gtk_box_pack_start(GTK_BOX(vbox1), confirm_area, FALSE, FALSE, 0); + gtk_widget_show(confirm_area); + gtk_widget_grab_default(ok_btn); + + /* set the current format */ + gtk_entry_set_text(GTK_ENTRY(datefmt_entry), prefs_common.date_format); + date_format_entry_on_change(GTK_EDITABLE(datefmt_entry), + GTK_LABEL(label3)); + + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(date_format_ok_btn_clicked), &datefmt_win); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(date_format_cancel_btn_clicked), + &datefmt_win); + g_signal_connect(G_OBJECT(datefmt_win), "key_press_event", + G_CALLBACK(date_format_key_pressed), &datefmt_win); + g_signal_connect(G_OBJECT(datefmt_win), "delete_event", + G_CALLBACK(date_format_on_delete), &datefmt_win); + g_signal_connect(G_OBJECT(datefmt_entry), "changed", + G_CALLBACK(date_format_entry_on_change), label3); + g_signal_connect(G_OBJECT(datefmt_clist), "select_row", + G_CALLBACK(date_format_select_row), datefmt_win); + + gtk_window_set_position(GTK_WINDOW(datefmt_win), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(datefmt_win), TRUE); + + gtk_widget_show(datefmt_win); + manage_window_set_transient(GTK_WINDOW(datefmt_win)); + + gtk_widget_grab_focus(ok_btn); + + return datefmt_win; +} + +void prefs_quote_colors_dialog(void) +{ + if (!quote_color_win) + prefs_quote_colors_dialog_create(); + gtk_widget_show(quote_color_win); + manage_window_set_transient(GTK_WINDOW(quote_color_win)); + + gtk_main(); + gtk_widget_hide(quote_color_win); + gtk_window_present(GTK_WINDOW(dialog.window)); + + main_window_reflect_prefs_all(); +} + +static void prefs_quote_colors_dialog_create(void) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *quotelevel1_label; + GtkWidget *quotelevel2_label; + GtkWidget *quotelevel3_label; + GtkWidget *uri_label; + GtkWidget *hbbox; + GtkWidget *ok_btn; + GtkWidget *recycle_colors_btn; + GtkWidget *frame_colors; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width(GTK_CONTAINER(window), 2); + gtk_window_set_title(GTK_WINDOW(window), _("Set message colors")); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE); + + vbox = gtk_vbox_new (FALSE, VSPACING); + gtk_container_add (GTK_CONTAINER (window), vbox); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 8); + PACK_FRAME(vbox, frame_colors, _("Colors")); + + table = gtk_table_new (4, 2, FALSE); + gtk_container_add (GTK_CONTAINER (frame_colors), table); + gtk_container_set_border_width (GTK_CONTAINER (table), 8); + gtk_table_set_row_spacings (GTK_TABLE (table), 2); + gtk_table_set_col_spacings (GTK_TABLE (table), 4); + + color_buttons.quote_level1_btn = gtk_button_new(); + gtk_table_attach (GTK_TABLE (table), color_buttons.quote_level1_btn, + 0, 1, 0, 1, 0, 0, 0, 0); + gtk_widget_set_size_request (color_buttons.quote_level1_btn, 40, 30); + gtk_container_set_border_width + (GTK_CONTAINER (color_buttons.quote_level1_btn), 5); + + color_buttons.quote_level2_btn = gtk_button_new(); + gtk_table_attach (GTK_TABLE (table), color_buttons.quote_level2_btn, + 0, 1, 1, 2, 0, 0, 0, 0); + gtk_widget_set_size_request (color_buttons.quote_level2_btn, 40, 30); + gtk_container_set_border_width (GTK_CONTAINER (color_buttons.quote_level2_btn), 5); + + color_buttons.quote_level3_btn = gtk_button_new_with_label (""); + gtk_table_attach (GTK_TABLE (table), color_buttons.quote_level3_btn, + 0, 1, 2, 3, 0, 0, 0, 0); + gtk_widget_set_size_request (color_buttons.quote_level3_btn, 40, 30); + gtk_container_set_border_width + (GTK_CONTAINER (color_buttons.quote_level3_btn), 5); + + color_buttons.uri_btn = gtk_button_new_with_label (""); + gtk_table_attach (GTK_TABLE (table), color_buttons.uri_btn, + 0, 1, 3, 4, 0, 0, 0, 0); + gtk_widget_set_size_request (color_buttons.uri_btn, 40, 30); + gtk_container_set_border_width (GTK_CONTAINER (color_buttons.uri_btn), 5); + + quotelevel1_label = gtk_label_new (_("Quoted Text - First Level")); + gtk_table_attach (GTK_TABLE (table), quotelevel1_label, 1, 2, 0, 1, + (GTK_EXPAND | GTK_FILL), 0, 0, 0); + gtk_label_set_justify (GTK_LABEL (quotelevel1_label), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment (GTK_MISC (quotelevel1_label), 0, 0.5); + + quotelevel2_label = gtk_label_new (_("Quoted Text - Second Level")); + gtk_table_attach (GTK_TABLE (table), quotelevel2_label, 1, 2, 1, 2, + (GTK_EXPAND | GTK_FILL), 0, 0, 0); + gtk_label_set_justify (GTK_LABEL (quotelevel2_label), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment (GTK_MISC (quotelevel2_label), 0, 0.5); + + quotelevel3_label = gtk_label_new (_("Quoted Text - Third Level")); + gtk_table_attach (GTK_TABLE (table), quotelevel3_label, 1, 2, 2, 3, + (GTK_EXPAND | GTK_FILL), 0, 0, 0); + gtk_label_set_justify (GTK_LABEL (quotelevel3_label), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment (GTK_MISC (quotelevel3_label), 0, 0.5); + + uri_label = gtk_label_new (_("URI link")); + gtk_table_attach (GTK_TABLE (table), uri_label, 1, 2, 3, 4, + (GTK_EXPAND | GTK_FILL), 0, 0, 0); + gtk_label_set_justify (GTK_LABEL (uri_label), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment (GTK_MISC (uri_label), 0, 0.5); + + PACK_CHECK_BUTTON (vbox, recycle_colors_btn, + _("Recycle quote colors")); + + gtkut_stock_button_set_create(&hbbox, &ok_btn, GTK_STOCK_OK, + NULL, NULL, NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); + + gtk_widget_grab_default(ok_btn); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + g_signal_connect (G_OBJECT (window), "delete_event", + G_CALLBACK (prefs_quote_colors_deleted), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(prefs_quote_colors_key_pressed), NULL); + + g_signal_connect(G_OBJECT(color_buttons.quote_level1_btn), "clicked", + G_CALLBACK(quote_color_set_dialog), "LEVEL1"); + g_signal_connect(G_OBJECT(color_buttons.quote_level2_btn), "clicked", + G_CALLBACK(quote_color_set_dialog), "LEVEL2"); + g_signal_connect(G_OBJECT(color_buttons.quote_level3_btn), "clicked", + G_CALLBACK(quote_color_set_dialog), "LEVEL3"); + g_signal_connect(G_OBJECT(color_buttons.uri_btn), "clicked", + G_CALLBACK(quote_color_set_dialog), "URI"); + g_signal_connect(G_OBJECT(recycle_colors_btn), "toggled", + G_CALLBACK(prefs_recycle_colors_toggled), NULL); + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(gtk_main_quit), NULL); + + /* show message button colors and recycle options */ + set_button_bg_color(color_buttons.quote_level1_btn, + prefs_common.quote_level1_col); + set_button_bg_color(color_buttons.quote_level2_btn, + prefs_common.quote_level2_col); + set_button_bg_color(color_buttons.quote_level3_btn, + prefs_common.quote_level3_col); + set_button_bg_color(color_buttons.uri_btn, + prefs_common.uri_col); + gtk_toggle_button_set_active((GtkToggleButton *)recycle_colors_btn, + prefs_common.recycle_quote_colors); + + gtk_widget_show_all(vbox); + quote_color_win = window; +} + +static gint prefs_quote_colors_deleted(GtkWidget *widet, GdkEventAny *event, + gpointer data) +{ + gtk_main_quit(); + return TRUE; +} + +static gboolean prefs_quote_colors_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + gtk_main_quit(); + return FALSE; +} + +static void quote_color_set_dialog(GtkWidget *widget, gpointer data) +{ + gchar *type = (gchar *)data; + gchar *title = NULL; + gdouble color[4] = {0.0, 0.0, 0.0, 0.0}; + gint rgbvalue = 0; + GtkColorSelectionDialog *dialog; + + if(g_ascii_strcasecmp(type, "LEVEL1") == 0) { + title = _("Pick color for quotation level 1"); + rgbvalue = prefs_common.quote_level1_col; + } else if(g_ascii_strcasecmp(type, "LEVEL2") == 0) { + title = _("Pick color for quotation level 2"); + rgbvalue = prefs_common.quote_level2_col; + } else if(g_ascii_strcasecmp(type, "LEVEL3") == 0) { + title = _("Pick color for quotation level 3"); + rgbvalue = prefs_common.quote_level3_col; + } else if(g_ascii_strcasecmp(type, "URI") == 0) { + title = _("Pick color for URI"); + rgbvalue = prefs_common.uri_col; + } else { /* Should never be called */ + g_warning("Unrecognized datatype '%s' in quote_color_set_dialog\n", type); + return; + } + + color_dialog = gtk_color_selection_dialog_new(title); + gtk_window_set_position(GTK_WINDOW(color_dialog), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(color_dialog), TRUE); + gtk_window_set_policy(GTK_WINDOW(color_dialog), FALSE, FALSE, FALSE); + manage_window_set_transient(GTK_WINDOW(color_dialog)); + + g_signal_connect(G_OBJECT(GTK_COLOR_SELECTION_DIALOG(color_dialog)->ok_button), + "clicked", G_CALLBACK(quote_colors_set_dialog_ok), data); + g_signal_connect(G_OBJECT(GTK_COLOR_SELECTION_DIALOG(color_dialog)->cancel_button), + "clicked", G_CALLBACK(quote_colors_set_dialog_cancel), data); + g_signal_connect(G_OBJECT(color_dialog), "key_press_event", + G_CALLBACK(quote_colors_set_dialog_key_pressed), data); + + /* preselect the previous color in the color selection dialog */ + color[0] = (gdouble) ((rgbvalue & 0xff0000) >> 16) / 255.0; + color[1] = (gdouble) ((rgbvalue & 0x00ff00) >> 8) / 255.0; + color[2] = (gdouble) (rgbvalue & 0x0000ff) / 255.0; + dialog = GTK_COLOR_SELECTION_DIALOG(color_dialog); + gtk_color_selection_set_color + (GTK_COLOR_SELECTION(dialog->colorsel), color); + + gtk_widget_show(color_dialog); +} + +static void quote_colors_set_dialog_ok(GtkWidget *widget, gpointer data) +{ + GtkColorSelection *colorsel = (GtkColorSelection *) + ((GtkColorSelectionDialog *)color_dialog)->colorsel; + gdouble color[4]; + gint red, green, blue, rgbvalue; + gchar *type = (gchar *)data; + + gtk_color_selection_get_color(colorsel, color); + + red = (gint) (color[0] * 255.0); + green = (gint) (color[1] * 255.0); + blue = (gint) (color[2] * 255.0); + rgbvalue = (gint) ((red * 0x10000) | (green * 0x100) | blue); + +#if 0 + fprintf(stderr, "redc = %f, greenc = %f, bluec = %f\n", color[0], color[1], color[2]); + fprintf(stderr, "red = %d, green = %d, blue = %d\n", red, green, blue); + fprintf(stderr, "Color is %x\n", rgbvalue); +#endif + + if (g_ascii_strcasecmp(type, "LEVEL1") == 0) { + prefs_common.quote_level1_col = rgbvalue; + set_button_bg_color(color_buttons.quote_level1_btn, rgbvalue); + } else if (g_ascii_strcasecmp(type, "LEVEL2") == 0) { + prefs_common.quote_level2_col = rgbvalue; + set_button_bg_color(color_buttons.quote_level2_btn, rgbvalue); + } else if (g_ascii_strcasecmp(type, "LEVEL3") == 0) { + prefs_common.quote_level3_col = rgbvalue; + set_button_bg_color(color_buttons.quote_level3_btn, rgbvalue); + } else if (g_ascii_strcasecmp(type, "URI") == 0) { + prefs_common.uri_col = rgbvalue; + set_button_bg_color(color_buttons.uri_btn, rgbvalue); + } else + fprintf( stderr, "Unrecognized datatype '%s' in quote_color_set_dialog_ok\n", type ); + + gtk_widget_destroy(color_dialog); +} + +static void quote_colors_set_dialog_cancel(GtkWidget *widget, gpointer data) +{ + gtk_widget_destroy(color_dialog); +} + +static gboolean quote_colors_set_dialog_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) { + gtk_widget_destroy(color_dialog); + return TRUE; + } + return FALSE; +} + +static void set_button_bg_color(GtkWidget *widget, gint rgbvalue) +{ + GtkStyle *newstyle; + GdkColor color; + + gtkut_convert_int_to_gdk_color(rgbvalue, &color); + newstyle = gtk_style_copy(gtk_widget_get_default_style()); + newstyle->bg[GTK_STATE_NORMAL] = color; + newstyle->bg[GTK_STATE_PRELIGHT] = color; + newstyle->bg[GTK_STATE_ACTIVE] = color; + + gtk_widget_set_style(GTK_WIDGET(widget), newstyle); +} + +static void prefs_enable_message_color_toggled(void) +{ + gboolean is_active; + + is_active = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(message.chkbtn_enablecol)); + gtk_widget_set_sensitive(message.button_edit_col, is_active); + prefs_common.enable_color = is_active; +} + +static void prefs_recycle_colors_toggled(GtkWidget *widget) +{ + gboolean is_active; + + is_active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); + prefs_common.recycle_quote_colors = is_active; +} + +void prefs_quote_description(void) +{ + if (!quote_desc_win) + prefs_quote_description_create(); + + manage_window_set_transient(GTK_WINDOW(quote_desc_win)); + gtk_widget_show(quote_desc_win); + gtk_main(); + gtk_widget_hide(quote_desc_win); +} + +static void prefs_quote_description_create(void) +{ + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *vbox2; + GtkWidget *label; + GtkWidget *hbbox; + GtkWidget *ok_btn; + + quote_desc_win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(quote_desc_win), + _("Description of symbols")); + gtk_container_set_border_width(GTK_CONTAINER(quote_desc_win), 8); + gtk_window_set_position(GTK_WINDOW(quote_desc_win), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(quote_desc_win), TRUE); + gtk_window_set_policy(GTK_WINDOW(quote_desc_win), FALSE, FALSE, FALSE); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add(GTK_CONTAINER(quote_desc_win), vbox); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); + + vbox2 = gtk_vbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0); + +#define PACK_LABEL() \ + gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0); \ + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); \ + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + label = gtk_label_new + ("%d\n" /* date */ + "%f\n" /* from */ + "%N\n" /* full name of sender */ + "%F\n" /* first name of sender */ + "%I\n" /* initial of sender */ + "%s\n" /* subject */ + "%t\n" /* to */ + "%c\n" /* cc */ + "%n\n" /* newsgroups */ + "%i"); /* message id */ + PACK_LABEL(); + + label = gtk_label_new + ("?x{expr}"); /* condition */ + PACK_LABEL(); + + label = gtk_label_new + ("%M\n" /* message body */ + "%Q\n" /* quoted message body */ + "%m\n" /* message body without signature */ + "%q\n" /* quoted message body without signature */ + "%%"); /* literal percent */ + PACK_LABEL(); + + label = gtk_label_new + ("\\\\\n" /* literal backslash */ + "\\?\n" /* literal question mark */ + "\\{\n" /* literal opening curly brace */ + "\\}"); /* literal closing curly brace */ + PACK_LABEL(); + + vbox2 = gtk_vbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0); + + label = gtk_label_new + (_("Date\n" + "From\n" + "Full Name of Sender\n" + "First Name of Sender\n" + "Initial of Sender\n" + "Subject\n" + "To\n" + "Cc\n" + "Newsgroups\n" + "Message-ID")); + PACK_LABEL(); + + label = gtk_label_new + (_("If x is set, displays expr")); + PACK_LABEL(); + + label = gtk_label_new + (_("Message body\n" + "Quoted message body\n" + "Message body without signature\n" + "Quoted message body without signature\n" + "Literal %")); + PACK_LABEL(); + + label = gtk_label_new + (_("Literal backslash\n" + "Literal question mark\n" + "Literal opening curly brace\n" + "Literal closing curly brace")); + PACK_LABEL(); + +#undef PACK_LABEL + + gtkut_stock_button_set_create(&hbbox, &ok_btn, GTK_STOCK_OK, + NULL, NULL, NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); + + gtk_widget_grab_default(ok_btn); + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(gtk_main_quit), NULL); + g_signal_connect + (G_OBJECT(quote_desc_win), "key_press_event", + G_CALLBACK(prefs_quote_description_key_pressed), NULL); + g_signal_connect(G_OBJECT(quote_desc_win), "delete_event", + G_CALLBACK(prefs_quote_description_deleted), NULL); + + gtk_widget_show_all(vbox); +} + +static gboolean prefs_quote_description_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + gtk_main_quit(); + return FALSE; +} + +static gboolean prefs_quote_description_deleted(GtkWidget *widget, + GdkEventAny *event, + gpointer data) +{ + gtk_main_quit(); + return TRUE; +} + +static void prefs_keybind_select(void) +{ + GtkWidget *window; + GtkWidget *vbox1; + GtkWidget *hbox1; + GtkWidget *label; + GtkWidget *combo; + GtkWidget *confirm_area; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (window), 8); + gtk_window_set_title (GTK_WINDOW (window), _("Key bindings")); + gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER); + gtk_window_set_modal (GTK_WINDOW (window), TRUE); + gtk_window_set_policy (GTK_WINDOW (window), FALSE, FALSE, FALSE); + manage_window_set_transient (GTK_WINDOW (window)); + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_container_add (GTK_CONTAINER (window), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, FALSE, 0); + + label = gtk_label_new(_("Select the preset of key bindings.")); + gtk_box_pack_start (GTK_BOX (hbox1), label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, FALSE, 0); + + combo = gtk_combo_new (); + gtk_box_pack_start (GTK_BOX (hbox1), combo, TRUE, TRUE, 0); + gtkut_combo_set_items (GTK_COMBO (combo), + _("Default"), + "Mew / Wanderlust", + "Mutt", + _("Old Sylpheed"), + NULL); + gtk_editable_set_editable + (GTK_EDITABLE (GTK_COMBO (combo)->entry), FALSE); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, FALSE, 0); + + gtkut_stock_button_set_create (&confirm_area, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end (GTK_BOX (hbox1), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default (ok_btn); + + MANAGE_WINDOW_SIGNALS_CONNECT(window); + g_signal_connect (G_OBJECT (window), "delete_event", + G_CALLBACK (prefs_keybind_deleted), NULL); + g_signal_connect (G_OBJECT (window), "key_press_event", + G_CALLBACK (prefs_keybind_key_pressed), NULL); + g_signal_connect (G_OBJECT (ok_btn), "clicked", + G_CALLBACK (prefs_keybind_apply_clicked), NULL); + g_signal_connect (G_OBJECT (cancel_btn), "clicked", + G_CALLBACK (prefs_keybind_cancel), NULL); + + gtk_widget_show_all(window); + + keybind.window = window; + keybind.combo = combo; +} + +static gboolean prefs_keybind_key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + prefs_keybind_cancel(); + return FALSE; +} + +static gint prefs_keybind_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + prefs_keybind_cancel(); + return TRUE; +} + +static void prefs_keybind_cancel(void) +{ + gtk_widget_destroy(keybind.window); + keybind.window = NULL; + keybind.combo = NULL; +} + +struct KeyBind { + const gchar *accel_path; + const gchar *accel_key; +}; + +static void prefs_keybind_apply(struct KeyBind keybind[], gint num) +{ + gint i; + guint key; + GdkModifierType mods; + + for (i = 0; i < num; i++) { + const gchar *accel_key = + keybind[i].accel_key ? keybind[i].accel_key : ""; + gtk_accelerator_parse(accel_key, &key, &mods); + gtk_accel_map_change_entry(keybind[i].accel_path, + key, mods, TRUE); + } +} + +static void prefs_keybind_apply_clicked(GtkWidget *widget) +{ + GtkEntry *entry = GTK_ENTRY(GTK_COMBO(keybind.combo)->entry); + const gchar *text; + struct KeyBind *menurc; + gint n_menurc; + + static struct KeyBind default_menurc[] = { + {"
/File/Empty all trash", ""}, + {"
/File/Save as...", "S"}, + {"
/File/Print...", ""}, + {"
/File/Exit", "Q"}, + + {"
/Edit/Copy", "C"}, + {"
/Edit/Select all", "A"}, + {"
/Edit/Find in current message...", "F"}, + {"
/Edit/Search messages...", "F"}, + + {"
/View/Show or hide/Message view", "V"}, + {"
/View/Thread view", "T"}, + {"
/View/Go to/Prev message", "P"}, + {"
/View/Go to/Next message", "N"}, + {"
/View/Go to/Prev unread message", "P"}, + {"
/View/Go to/Next unread message", "N"}, + {"
/View/Go to/Other folder...", "G"}, + {"
/View/Open in new window", "N"}, + {"
/View/View source", "U"}, + {"
/View/Show all headers", "H"}, + {"
/View/Update", "U"}, + + {"
/Message/Receive/Get new mail", "I"}, + {"
/Message/Receive/Get from all accounts", "I"}, + {"
/Message/Compose new message", "M"}, + {"
/Message/Reply", "R"}, + {"
/Message/Reply to/all", "R"}, + {"
/Message/Reply to/sender", ""}, + {"
/Message/Reply to/mailing list", "L"}, + {"
/Message/Forward", "F"}, + {"
/Message/Forward as attachment", ""}, + {"
/Message/Move...", "O"}, + {"
/Message/Copy...", "O"}, + {"
/Message/Delete", "D"}, + {"
/Message/Mark/Set flag", "asterisk"}, + {"
/Message/Mark/Unset flag", "U"}, + {"
/Message/Mark/Mark as unread", "exclam"}, + {"
/Message/Mark/Mark as read", ""}, + + {"
/Tools/Address book", "A"}, + {"
/Tools/Execute", "X"}, + {"
/Tools/Log window", "L"}, + + {"/File/Close", "W"}, + {"/Edit/Select all", "A"}, + }; + + static struct KeyBind mew_wl_menurc[] = { + {"
/File/Empty all trash", "D"}, + {"
/File/Save as...", "Y"}, + {"
/File/Print...", "numbersign"}, + {"
/File/Exit", "Q"}, + + {"
/Edit/Copy", "C"}, + {"
/Edit/Select all", "A"}, + {"
/Edit/Find in current message...", "F"}, + {"
/Edit/Search messages...", "S"}, + + {"
/View/Show or hide/Message view", "V"}, + {"
/View/Thread view", "T"}, + {"
/View/Go to/Prev message", "P"}, + {"
/View/Go to/Next message", "N"}, + {"
/View/Go to/Prev unread message", "P"}, + {"
/View/Go to/Next unread message", "N"}, + {"
/View/Go to/Other folder...", "G"}, + {"
/View/Open in new window", "N"}, + {"
/View/View source", "U"}, + {"
/View/Show all headers", "H"}, + {"
/View/Update", "S"}, + + {"
/Message/Receive/Get new mail", "I"}, + {"
/Message/Receive/Get from all accounts", "I"}, + {"
/Message/Compose new message", "W"}, + {"
/Message/Reply", "R"}, + {"
/Message/Reply to/all", "A"}, + {"
/Message/Reply to/sender", ""}, + {"
/Message/Reply to/mailing list", "L"}, + {"
/Message/Forward", "F"}, + {"
/Message/Forward as attachment", "F"}, + {"
/Message/Move...", "O"}, + {"
/Message/Copy...", "O"}, + {"
/Message/Delete", "D"}, + {"
/Message/Mark/Set flag", "asterisk"}, + {"
/Message/Mark/Unset flag", "U"}, + {"
/Message/Mark/Mark as unread", "exclam"}, + {"
/Message/Mark/Mark as read", "R"}, + + {"
/Tools/Address book", "A"}, + {"
/Tools/Execute", "X"}, + {"
/Tools/Log window", "L"}, + + {"/File/Close", "W"}, + {"/Edit/Select all", ""}, + }; + + static struct KeyBind mutt_menurc[] = { + {"
/File/Empty all trash", ""}, + {"
/File/Save as...", "S"}, + {"
/File/Print...", "P"}, + {"
/File/Exit", "Q"}, + + {"
/Edit/Copy", "C"}, + {"
/Edit/Select all", "A"}, + {"
/Edit/Find in current message...", "F"}, + {"
/Edit/Search messages...", "slash"}, + + {"
/View/Show or hide/Message view", "V"}, + {"
/View/Thread view", "T"}, + {"
/View/Go to/Prev message", ""}, + {"
/View/Go to/Next message", ""}, + {"
/View/Go to/Prev unread message", ""}, + {"
/View/Go to/Next unread message", ""}, + {"
/View/Go to/Other folder...", "C"}, + {"
/View/Open in new window", "N"}, + {"
/View/View source", "U"}, + {"
/View/Show all headers", "H"}, + {"
/View/Update", "U"}, + + {"
/Message/Receive/Get new mail", "I"}, + {"
/Message/Receive/Get from all accounts", "I"}, + {"
/Message/Compose new message", "M"}, + {"
/Message/Reply", "R"}, + {"
/Message/Reply to/all", "G"}, + {"
/Message/Reply to/sender", ""}, + {"
/Message/Reply to/mailing list", "L"}, + {"
/Message/Forward", "F"}, + {"
/Message/Forward as attachment", ""}, + {"
/Message/Move...", "O"}, + {"
/Message/Copy...", "C"}, + {"
/Message/Delete", "D"}, + {"
/Message/Mark/Set flag", "F"}, + {"
/Message/Mark/Unset flag", "U"}, + {"
/Message/Mark/Mark as unread", "N"}, + {"
/Message/Mark/Mark as read", ""}, + + {"
/Tools/Address book", "A"}, + {"
/Tools/Execute", "X"}, + {"
/Tools/Log window", "L"}, + + {"/File/Close", "W"}, + {"/Edit/Select all", ""}, + }; + + static struct KeyBind old_sylpheed_menurc[] = { + {"
/File/Empty all trash", ""}, + {"
/File/Save as...", ""}, + {"
/File/Print...", "P"}, + {"
/File/Exit", "Q"}, + + {"
/Edit/Copy", "C"}, + {"
/Edit/Select all", "A"}, + {"
/Edit/Find in current message...", "F"}, + {"
/Edit/Search messages...", "S"}, + + {"
/View/Show or hide/Message view", "V"}, + {"
/View/Thread view", "T"}, + {"
/View/Go to/Prev message", "P"}, + {"
/View/Go to/Next message", "N"}, + {"
/View/Go to/Prev unread message", "P"}, + {"
/View/Go to/Next unread message", "N"}, + {"
/View/Go to/Other folder...", "G"}, + {"
/View/Open in new window", "N"}, + {"
/View/View source", "U"}, + {"
/View/Show all headers", "H"}, + {"
/View/Update", "U"}, + + {"
/Message/Receive/Get new mail", "I"}, + {"
/Message/Receive/Get from all accounts", "I"}, + {"
/Message/Compose new message", "N"}, + {"
/Message/Reply", "R"}, + {"
/Message/Reply to/all", "R"}, + {"
/Message/Reply to/sender", "R"}, + {"
/Message/Reply to/mailing list", "L"}, + {"
/Message/Forward", "F"}, + {"
/Message/Forward as attachment", "F"}, + {"
/Message/Move...", "O"}, + {"
/Message/Copy...", ""}, + {"
/Message/Delete", "D"}, + {"
/Message/Mark/Set flag", "asterisk"}, + {"
/Message/Mark/Unset flag", "U"}, + {"
/Message/Mark/Mark as unread", "exclam"}, + {"
/Message/Mark/Mark as read", ""}, + + {"
/Tools/Address book", "A"}, + {"
/Tools/Execute", "X"}, + {"
/Tools/Log window", "L"}, + + {"/File/Close", "W"}, + {"/Edit/Select all", ""}, + }; + + static struct KeyBind empty_menurc[] = { + {"
/File/Empty all trash", ""}, + {"
/File/Save as...", ""}, + {"
/File/Print...", ""}, + {"
/File/Exit", ""}, + + {"
/Edit/Copy", ""}, + {"
/Edit/Select all", ""}, + {"
/Edit/Find in current message...", ""}, + {"
/Edit/Search messages...", ""}, + + {"
/View/Show or hide/Message view", ""}, + {"
/View/Thread view", ""}, + {"
/View/Go to/Prev message", ""}, + {"
/View/Go to/Next message", ""}, + {"
/View/Go to/Prev unread message", ""}, + {"
/View/Go to/Next unread message", ""}, + {"
/View/Go to/Other folder...", ""}, + {"
/View/Open in new window", ""}, + {"
/View/View source", ""}, + {"
/View/Show all headers", ""}, + {"
/View/Update", ""}, + + {"
/Message/Receive/Get new mail", ""}, + {"
/Message/Receive/Get from all accounts", ""}, + {"
/Message/Compose new message", ""}, + {"
/Message/Reply", ""}, + {"
/Message/Reply to/all", ""}, + {"
/Message/Reply to/sender", ""}, + {"
/Message/Reply to/mailing list", ""}, + {"
/Message/Forward", ""}, + {"
/Message/Forward as attachment", ""}, + {"
/Message/Move...", ""}, + {"
/Message/Copy...", ""}, + {"
/Message/Delete", ""}, + {"
/Message/Mark/Set flag", ""}, + {"
/Message/Mark/Unset flag", ""}, + {"
/Message/Mark/Mark as unread", ""}, + {"
/Message/Mark/Mark as read", ""}, + + {"
/Tools/Address book", ""}, + {"
/Tools/Execute", ""}, + {"
/Tools/Log window", ""}, + + {"/File/Close", ""}, + {"/Edit/Select all", ""}, + }; + + text = gtk_entry_get_text(entry); + + if (!strcmp(text, _("Default"))) { + menurc = default_menurc; + n_menurc = G_N_ELEMENTS(default_menurc); + } else if (!strcmp(text, "Mew / Wanderlust")) { + menurc = mew_wl_menurc; + n_menurc = G_N_ELEMENTS(mew_wl_menurc); + } else if (!strcmp(text, "Mutt")) { + menurc = mutt_menurc; + n_menurc = G_N_ELEMENTS(mutt_menurc); + } else if (!strcmp(text, _("Old Sylpheed"))) { + menurc = old_sylpheed_menurc; + n_menurc = G_N_ELEMENTS(old_sylpheed_menurc); + } else + return; + + prefs_keybind_apply(empty_menurc, G_N_ELEMENTS(empty_menurc)); + prefs_keybind_apply(menurc, n_menurc); + + gtk_widget_destroy(keybind.window); + keybind.window = NULL; + keybind.combo = NULL; +} + +static void prefs_common_charset_set_data_from_optmenu(PrefParam *pparam) +{ + PrefsUIData *ui_data; + GtkWidget *menu; + GtkWidget *menuitem; + gchar *charset; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(*ui_data->widget)); + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + charset = g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID); + g_free(*((gchar **)pparam->data)); + *((gchar **)pparam->data) = g_strdup(charset); +} + +static void prefs_common_charset_set_optmenu(PrefParam *pparam) +{ + PrefsUIData *ui_data; + GtkOptionMenu *optmenu; + gint index; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + optmenu = GTK_OPTION_MENU(*ui_data->widget); + g_return_if_fail(optmenu != NULL); + + index = menu_find_option_menu_index(optmenu, *((gchar **)pparam->data), + (GCompareFunc)strcmp2); + if (index >= 0) + gtk_option_menu_set_history(optmenu, index); + else { + gtk_option_menu_set_history(optmenu, 0); + prefs_common_charset_set_data_from_optmenu(pparam); + } +} + +static void prefs_common_recv_dialog_set_optmenu(PrefParam *pparam) +{ + PrefsUIData *ui_data; + RecvDialogMode mode = *((RecvDialogMode *)pparam->data); + GtkOptionMenu *optmenu; + GtkWidget *menu; + GtkWidget *menuitem; + gint index; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + optmenu = GTK_OPTION_MENU(*ui_data->widget); + g_return_if_fail(optmenu != NULL); + + index = menu_find_option_menu_index(optmenu, GINT_TO_POINTER(mode), + NULL); + if (index >= 0) + gtk_option_menu_set_history(optmenu, index); + else { + gtk_option_menu_set_history(optmenu, 0); + prefs_set_data_from_optmenu(pparam); + } + + menu = gtk_option_menu_get_menu(optmenu); + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + gtk_menu_item_activate(GTK_MENU_ITEM(menuitem)); +} + +static void prefs_common_uri_set_data_from_entry(PrefParam *pparam) +{ + PrefsUIData *ui_data; + gchar **str; + const gchar *entry_str; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + entry_str = gtk_entry_get_text(GTK_ENTRY(*ui_data->widget)); + + if (pparam->type == P_STRING) { + str = (gchar **)pparam->data; + g_free(*str); + + if (entry_str[0] == '\0' || + !strcmp(_("(Default browser)"), entry_str)) + *str = NULL; + else + *str = g_strdup(entry_str); + } else { + g_warning("Invalid type for URI setting\n"); + } +} + +static void prefs_common_uri_set_entry(PrefParam *pparam) +{ + PrefsUIData *ui_data; + gchar **str; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + if (pparam->type == P_STRING) { + str = (gchar **)pparam->data; + gtk_entry_set_text(GTK_ENTRY(*ui_data->widget), + *str ? *str : _("(Default browser)")); + } else { + g_warning("Invalid type for URI setting\n"); + } +} + +static void prefs_common_addr_compl_set_data_from_radiobtn(PrefParam *pparam) +{ + PrefsUIData *ui_data; + GtkRadioButton *radiobtn; + GSList *group; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + radiobtn = GTK_RADIO_BUTTON(*ui_data->widget); + group = gtk_radio_button_get_group(radiobtn); + while (group != NULL) { + GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data); + gint mode; + + if (gtk_toggle_button_get_active(btn)) { + mode = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(btn), + MENU_VAL_ID)); + if (mode == 2) { + prefs_common.enable_address_completion = FALSE; + prefs_common.fullauto_completion_mode = FALSE; + } else { + prefs_common.enable_address_completion = TRUE; + if (mode == 0) + prefs_common.fullauto_completion_mode = TRUE; + else + prefs_common.fullauto_completion_mode = FALSE; + } + break; + } + group = group->next; + } +} + +static void prefs_common_addr_compl_set_radiobtn(PrefParam *pparam) +{ + PrefsUIData *ui_data; + GtkRadioButton *radiobtn; + GSList *group; + gint mode; + + if (prefs_common.enable_address_completion) { + if (prefs_common.fullauto_completion_mode) + mode = 0; + else + mode = 1; + } else + mode = 2; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + radiobtn = GTK_RADIO_BUTTON(*ui_data->widget); + group = gtk_radio_button_get_group(radiobtn); + while (group != NULL) { + GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data); + gint data; + + data = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(btn), + MENU_VAL_ID)); + if (data == mode) { + gtk_toggle_button_set_active(btn, TRUE); + break; + } + group = group->next; + } +} + +static void prefs_common_attach_toolbtn_pos_set_data_from_radiobtn(PrefParam *pparam) +{ + PrefsUIData *ui_data; + GtkRadioButton *radiobtn; + GSList *group; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + radiobtn = GTK_RADIO_BUTTON(*ui_data->widget); + group = gtk_radio_button_get_group(radiobtn); + while (group != NULL) { + GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data); + + if (gtk_toggle_button_get_active(btn)) { + prefs_common.attach_toolbutton_pos = + GPOINTER_TO_INT(g_object_get_data(G_OBJECT(btn), MENU_VAL_ID)); + break; + } + group = group->next; + } +} + +static void prefs_common_attach_toolbtn_pos_set_radiobtn(PrefParam *pparam) +{ + PrefsUIData *ui_data; + GtkRadioButton *radiobtn; + GSList *group; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + radiobtn = GTK_RADIO_BUTTON(*ui_data->widget); + group = gtk_radio_button_get_group(radiobtn); + while (group != NULL) { + GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data); + gint data; + + data = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(btn), + MENU_VAL_ID)); + if (data == prefs_common.attach_toolbutton_pos) { + gtk_toggle_button_set_active(btn, TRUE); + break; + } + group = group->next; + } +} + +static void prefs_common_online_mode_set_data_from_radiobtn(PrefParam *pparam) +{ + PrefsUIData *ui_data; + GtkRadioButton *radiobtn; + GSList *group; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + radiobtn = GTK_RADIO_BUTTON(*ui_data->widget); + group = gtk_radio_button_get_group(radiobtn); + while (group != NULL) { + GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data); + + if (gtk_toggle_button_get_active(btn)) { + prefs_common.startup_online_mode = + GPOINTER_TO_INT(g_object_get_data(G_OBJECT(btn), MENU_VAL_ID)); + break; + } + group = group->next; + } +} + +static void prefs_common_online_mode_set_radiobtn(PrefParam *pparam) +{ + PrefsUIData *ui_data; + GtkRadioButton *radiobtn; + GSList *group; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + radiobtn = GTK_RADIO_BUTTON(*ui_data->widget); + group = gtk_radio_button_get_group(radiobtn); + while (group != NULL) { + GtkToggleButton *btn = GTK_TOGGLE_BUTTON(group->data); + gint data; + + data = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(btn), + MENU_VAL_ID)); + if (data == prefs_common.startup_online_mode) { + gtk_toggle_button_set_active(btn, TRUE); + break; + } + group = group->next; + } +} + +static void prefs_common_dispitem_clicked(void) +{ + prefs_summary_column_open(FOLDER_ITEM_IS_SENT_FOLDER + (main_window_get()->summaryview->folder_item)); +} + +static void prefs_common_select_folder_cb(GtkWidget *widget, gpointer data) +{ + FolderItem *item; + gchar *id; + + item = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL); + if (item && item->path) { + id = folder_item_get_identifier(item); + if (id) { + gtk_entry_set_text(GTK_ENTRY(data), id); + g_free(id); + } + } +} + +#ifdef G_OS_WIN32 +static void prefs_common_file_selection_cb(GtkButton *button, gpointer data) +{ + GtkEntry *entry = GTK_ENTRY(data); + gchar *file; + gchar *ufile; + + file = filesel_select_file(_("Select file"), NULL, + GTK_FILE_CHOOSER_ACTION_OPEN); + if (file) { + ufile = conv_filename_to_utf8(file); + gtk_entry_set_text(entry, ufile); + g_free(ufile); + } + g_free(file); +} +#endif + +static gint prefs_common_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + prefs_common_cancel(); + return TRUE; +} + +static gboolean prefs_common_key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + prefs_common_cancel(); + return FALSE; +} + +static void prefs_common_ok(void) +{ + prefs_common_apply(); + gtk_widget_hide(dialog.window); + if (quote_desc_win && GTK_WIDGET_VISIBLE(quote_desc_win)) + gtk_widget_hide(quote_desc_win); + + main_window_popup(main_window_get()); + inc_unlock(); +} + +static void prefs_common_apply(void) +{ + prefs_set_data_from_dialog(prefs_common_get_params()); + gtkut_stock_button_set_set_reverse(!prefs_common.comply_gnome_hig); + main_window_reflect_prefs_all(); + compose_reflect_prefs_all(); + prefs_common_colorlabel_update(); + colorlabel_write_config(); + sock_set_io_timeout(prefs_common.io_timeout_secs); + prefs_common_write_config(); + + inc_autocheck_timer_remove(); + inc_autocheck_timer_set(); +} + +static void prefs_common_cancel(void) +{ + gtk_widget_hide(dialog.window); + main_window_popup(main_window_get()); + inc_unlock(); +} diff --git a/src/prefs_common_dialog.h b/src/prefs_common_dialog.h new file mode 100644 index 0000000..d8d401a --- /dev/null +++ b/src/prefs_common_dialog.h @@ -0,0 +1,33 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2006 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PREFS_COMMON_DIALOG_H__ +#define __PREFS_COMMON_DIALOG_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +void prefs_common_open (void); + +void prefs_quote_description (void); + +#endif /* __PREFS_COMMON_DIALOG_H__ */ diff --git a/src/prefs_customheader.c b/src/prefs_customheader.c new file mode 100644 index 0000000..b9dfd04 --- /dev/null +++ b/src/prefs_customheader.c @@ -0,0 +1,495 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include + +#include "main.h" +#include "prefs.h" +#include "prefs_ui.h" +#include "prefs_customheader.h" +#include "prefs_common.h" +#include "prefs_account.h" +#include "mainwindow.h" +#include "foldersel.h" +#include "manage_window.h" +#include "customheader.h" +#include "folder.h" +#include "utils.h" +#include "gtkutils.h" +#include "alertpanel.h" + +static struct CustomHdr { + GtkWidget *window; + + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + + GtkWidget *hdr_combo; + GtkWidget *hdr_entry; + GtkWidget *val_entry; + GtkWidget *customhdr_clist; +} customhdr; + +/* widget creating functions */ +static void prefs_custom_header_create (void); + +static void prefs_custom_header_set_dialog (PrefsAccount *ac); +static void prefs_custom_header_set_list (PrefsAccount *ac); +static gint prefs_custom_header_clist_set_row (PrefsAccount *ac, + gint row); + +/* callback functions */ +static void prefs_custom_header_add_cb (void); +static void prefs_custom_header_delete_cb (void); +static void prefs_custom_header_up (void); +static void prefs_custom_header_down (void); +static void prefs_custom_header_select (GtkCList *clist, + gint row, + gint column, + GdkEvent *event); + +static void prefs_custom_header_row_moved (GtkCList *clist, + gint source_row, + gint dest_row, + gpointer data); + +static gboolean prefs_custom_header_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); +static void prefs_custom_header_ok (void); +static void prefs_custom_header_cancel (void); +static gint prefs_custom_header_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); + +static PrefsAccount *cur_ac = NULL; + +void prefs_custom_header_open(PrefsAccount *ac) +{ + if (!customhdr.window) { + prefs_custom_header_create(); + } + + manage_window_set_transient(GTK_WINDOW(customhdr.window)); + gtk_widget_grab_focus(customhdr.ok_btn); + + prefs_custom_header_set_dialog(ac); + + cur_ac = ac; + + gtk_widget_show(customhdr.window); +} + +static void prefs_custom_header_create(void) +{ + GtkWidget *window; + GtkWidget *vbox; + + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + + GtkWidget *confirm_area; + + GtkWidget *vbox1; + + GtkWidget *table1; + GtkWidget *hdr_label; + GtkWidget *hdr_combo; + GtkWidget *val_label; + GtkWidget *val_entry; + + GtkWidget *reg_hbox; + GtkWidget *btn_hbox; + GtkWidget *arrow; + GtkWidget *add_btn; + GtkWidget *del_btn; + + GtkWidget *ch_hbox; + GtkWidget *ch_scrolledwin; + GtkWidget *customhdr_clist; + + GtkWidget *btn_vbox; + GtkWidget *up_btn; + GtkWidget *down_btn; + + gchar *title[1]; + + debug_print("Creating custom header setting window...\n"); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (window), 8); + gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER); + gtk_window_set_modal (GTK_WINDOW (window), TRUE); + gtk_window_set_policy (GTK_WINDOW (window), FALSE, TRUE, FALSE); + + vbox = gtk_vbox_new (FALSE, 6); + gtk_widget_show (vbox); + gtk_container_add (GTK_CONTAINER (window), vbox); + + gtkut_stock_button_set_create(&confirm_area, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_widget_show (confirm_area); + gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default (ok_btn); + + gtk_window_set_title (GTK_WINDOW(window), _("Custom header setting")); + MANAGE_WINDOW_SIGNALS_CONNECT (window); + g_signal_connect (G_OBJECT(window), "delete_event", + G_CALLBACK(prefs_custom_header_deleted), + NULL); + g_signal_connect (G_OBJECT(window), "key_press_event", + G_CALLBACK(prefs_custom_header_key_pressed), + NULL); + g_signal_connect (G_OBJECT(ok_btn), "clicked", + G_CALLBACK(prefs_custom_header_ok), NULL); + g_signal_connect (G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(prefs_custom_header_cancel), NULL); + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_box_pack_start (GTK_BOX (vbox), vbox1, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2); + + table1 = gtk_table_new (2, 2, FALSE); + gtk_widget_show (table1); + gtk_box_pack_start (GTK_BOX (vbox1), table1, + FALSE, FALSE, 0); + gtk_table_set_row_spacings (GTK_TABLE (table1), 8); + gtk_table_set_col_spacings (GTK_TABLE (table1), 8); + + hdr_label = gtk_label_new (_("Header")); + gtk_widget_show (hdr_label); + gtk_table_attach (GTK_TABLE (table1), hdr_label, 0, 1, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (hdr_label), 0, 0.5); + + hdr_combo = gtk_combo_new (); + gtk_widget_show (hdr_combo); + gtk_table_attach (GTK_TABLE (table1), hdr_combo, 0, 1, 1, 2, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + 0, 0, 0); + gtk_widget_set_size_request (hdr_combo, 150, -1); + gtkut_combo_set_items (GTK_COMBO (hdr_combo), + "User-Agent", "X-Face", "X-Operating-System", + NULL); + + val_label = gtk_label_new (_("Value")); + gtk_widget_show (val_label); + gtk_table_attach (GTK_TABLE (table1), val_label, 1, 2, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (val_label), 0, 0.5); + + val_entry = gtk_entry_new (); + gtk_widget_show (val_entry); + gtk_table_attach (GTK_TABLE (table1), val_entry, 1, 2, 1, 2, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + 0, 0, 0); + gtk_widget_set_size_request (val_entry, 200, -1); + + /* add / delete */ + + reg_hbox = gtk_hbox_new (FALSE, 4); + gtk_widget_show (reg_hbox); + gtk_box_pack_start (GTK_BOX (vbox1), reg_hbox, FALSE, FALSE, 0); + + arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT); + gtk_widget_show (arrow); + gtk_box_pack_start (GTK_BOX (reg_hbox), arrow, FALSE, FALSE, 0); + gtk_widget_set_size_request (arrow, -1, 16); + + btn_hbox = gtk_hbox_new (TRUE, 4); + gtk_widget_show (btn_hbox); + gtk_box_pack_start (GTK_BOX (reg_hbox), btn_hbox, FALSE, FALSE, 0); + + add_btn = gtk_button_new_with_label (_("Add")); + gtk_widget_show (add_btn); + gtk_box_pack_start (GTK_BOX (btn_hbox), add_btn, FALSE, TRUE, 0); + g_signal_connect (G_OBJECT (add_btn), "clicked", + G_CALLBACK (prefs_custom_header_add_cb), NULL); + + del_btn = gtk_button_new_with_label (_(" Delete ")); + gtk_widget_show (del_btn); + gtk_box_pack_start (GTK_BOX (btn_hbox), del_btn, FALSE, TRUE, 0); + g_signal_connect (G_OBJECT (del_btn), "clicked", + G_CALLBACK (prefs_custom_header_delete_cb), NULL); + + + ch_hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (ch_hbox); + gtk_box_pack_start (GTK_BOX (vbox1), ch_hbox, TRUE, TRUE, 0); + + ch_scrolledwin = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_set_size_request (ch_scrolledwin, -1, 200); + gtk_widget_show (ch_scrolledwin); + gtk_box_pack_start (GTK_BOX (ch_hbox), ch_scrolledwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (ch_scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + title[0] = _("Custom headers"); + customhdr_clist = gtk_clist_new_with_titles(1, title); + gtk_widget_show (customhdr_clist); + gtk_container_add (GTK_CONTAINER (ch_scrolledwin), customhdr_clist); + gtk_clist_set_column_width (GTK_CLIST (customhdr_clist), 0, 80); + gtk_clist_set_selection_mode (GTK_CLIST (customhdr_clist), + GTK_SELECTION_BROWSE); + gtk_clist_set_reorderable (GTK_CLIST (customhdr_clist), TRUE); + gtk_clist_set_use_drag_icons (GTK_CLIST (customhdr_clist), FALSE); + gtkut_clist_set_redraw (GTK_CLIST (customhdr_clist)); + GTK_WIDGET_UNSET_FLAGS (GTK_CLIST (customhdr_clist)->column[0].button, + GTK_CAN_FOCUS); + g_signal_connect (G_OBJECT (customhdr_clist), "select_row", + G_CALLBACK (prefs_custom_header_select), NULL); + g_signal_connect_after + (G_OBJECT (customhdr_clist), "row_move", + G_CALLBACK (prefs_custom_header_row_moved), NULL); + + btn_vbox = gtk_vbox_new (FALSE, 8); + gtk_widget_show (btn_vbox); + gtk_box_pack_start (GTK_BOX (ch_hbox), btn_vbox, FALSE, FALSE, 0); + + up_btn = gtk_button_new_with_label (_("Up")); + gtk_widget_show (up_btn); + gtk_box_pack_start (GTK_BOX (btn_vbox), up_btn, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (up_btn), "clicked", + G_CALLBACK (prefs_custom_header_up), NULL); + + down_btn = gtk_button_new_with_label (_("Down")); + gtk_widget_show (down_btn); + gtk_box_pack_start (GTK_BOX (btn_vbox), down_btn, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (down_btn), "clicked", + G_CALLBACK (prefs_custom_header_down), NULL); + + gtk_widget_show_all(window); + + customhdr.window = window; + customhdr.ok_btn = ok_btn; + customhdr.cancel_btn = cancel_btn; + + customhdr.hdr_combo = hdr_combo; + customhdr.hdr_entry = GTK_COMBO (hdr_combo)->entry; + customhdr.val_entry = val_entry; + + customhdr.customhdr_clist = customhdr_clist; +} + +static void prefs_custom_header_set_dialog(PrefsAccount *ac) +{ + GtkCList *clist = GTK_CLIST(customhdr.customhdr_clist); + GSList *cur; + gchar *ch_str[1]; + gint row; + + gtk_clist_freeze(clist); + gtk_clist_clear(clist); + + for (cur = ac->customhdr_list; cur != NULL; cur = cur->next) { + CustomHeader *ch = (CustomHeader *)cur->data; + + ch_str[0] = g_strdup_printf("%s: %s", ch->name, + ch->value ? ch->value : ""); + row = gtk_clist_append(clist, ch_str); + gtk_clist_set_row_data(clist, row, ch); + + g_free(ch_str[0]); + } + + gtk_clist_thaw(clist); +} + +static void prefs_custom_header_set_list(PrefsAccount *ac) +{ + gint row = 0; + CustomHeader *ch; + + g_slist_free(ac->customhdr_list); + ac->customhdr_list = NULL; + + while ((ch = gtk_clist_get_row_data + (GTK_CLIST(customhdr.customhdr_clist), row)) != NULL) { + ac->customhdr_list = g_slist_append(ac->customhdr_list, ch); + row++; + } +} + +static gint prefs_custom_header_clist_set_row(PrefsAccount *ac, gint row) +{ + GtkCList *clist = GTK_CLIST(customhdr.customhdr_clist); + CustomHeader *ch; + const gchar *entry_text; + gchar *ch_str[1]; + + entry_text = gtk_entry_get_text(GTK_ENTRY(customhdr.hdr_entry)); + if (entry_text[0] == '\0') { + alertpanel_error(_("Header name is not set.")); + return -1; + } + + ch = g_new0(CustomHeader, 1); + + ch->account_id = ac->account_id; + + ch->name = g_strdup(entry_text); + unfold_line(ch->name); + g_strstrip(ch->name); + gtk_entry_set_text(GTK_ENTRY(customhdr.hdr_entry), ch->name); + + entry_text = gtk_entry_get_text(GTK_ENTRY(customhdr.val_entry)); + if (entry_text[0] != '\0') { + ch->value = g_strdup(entry_text); + unfold_line(ch->value); + g_strstrip(ch->value); + gtk_entry_set_text(GTK_ENTRY(customhdr.val_entry), ch->value); + } + + ch_str[0] = g_strdup_printf("%s: %s", ch->name, + ch->value ? ch->value : ""); + + if (row < 0) + row = gtk_clist_append(clist, ch_str); + else { + CustomHeader *tmp_ch; + + gtk_clist_set_text(clist, row, 0, ch_str[0]); + tmp_ch = gtk_clist_get_row_data(clist, row); + if (tmp_ch) + custom_header_free(tmp_ch); + } + + gtk_clist_set_row_data(clist, row, ch); + + g_free(ch_str[0]); + + prefs_custom_header_set_list(cur_ac); + + return row; +} + +static void prefs_custom_header_add_cb(void) +{ + prefs_custom_header_clist_set_row(cur_ac, -1); +} + +static void prefs_custom_header_delete_cb(void) +{ + GtkCList *clist = GTK_CLIST(customhdr.customhdr_clist); + CustomHeader *ch; + gint row; + + if (!clist->selection) return; + row = GPOINTER_TO_INT(clist->selection->data); + + if (alertpanel(_("Delete header"), + _("Do you really want to delete this header?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL) != G_ALERTDEFAULT) + return; + + ch = gtk_clist_get_row_data(clist, row); + custom_header_free(ch); + gtk_clist_remove(clist, row); + cur_ac->customhdr_list = g_slist_remove(cur_ac->customhdr_list, ch); +} + +static void prefs_custom_header_up(void) +{ + GtkCList *clist = GTK_CLIST(customhdr.customhdr_clist); + gint row; + + if (!clist->selection) return; + + row = GPOINTER_TO_INT(clist->selection->data); + if (row > 0) + gtk_clist_row_move(clist, row, row - 1); +} + +static void prefs_custom_header_down(void) +{ + GtkCList *clist = GTK_CLIST(customhdr.customhdr_clist); + gint row; + + if (!clist->selection) return; + + row = GPOINTER_TO_INT(clist->selection->data); + if (row >= 0 && row < clist->rows - 1) + gtk_clist_row_move(clist, row, row + 1); +} + +#define ENTRY_SET_TEXT(entry, str) \ + gtk_entry_set_text(GTK_ENTRY(entry), str ? str : "") + +static void prefs_custom_header_select(GtkCList *clist, gint row, gint column, + GdkEvent *event) +{ + CustomHeader *ch; + CustomHeader default_ch = { 0, "", NULL }; + + ch = gtk_clist_get_row_data(clist, row); + if (!ch) ch = &default_ch; + + ENTRY_SET_TEXT(customhdr.hdr_entry, ch->name); + ENTRY_SET_TEXT(customhdr.val_entry, ch->value); +} + +#undef ENTRY_SET_TEXT + +static void prefs_custom_header_row_moved(GtkCList *clist, gint source_row, + gint dest_row, gpointer data) +{ + prefs_custom_header_set_list(cur_ac); +} + +static gboolean prefs_custom_header_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + prefs_custom_header_cancel(); + return FALSE; +} + +static void prefs_custom_header_ok(void) +{ + custom_header_write_config(cur_ac); + gtk_widget_hide(customhdr.window); +} + +static void prefs_custom_header_cancel(void) +{ + custom_header_read_config(cur_ac); + gtk_widget_hide(customhdr.window); +} + +static gint prefs_custom_header_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + prefs_custom_header_cancel(); + return TRUE; +} diff --git a/src/prefs_customheader.h b/src/prefs_customheader.h new file mode 100644 index 0000000..18690ee --- /dev/null +++ b/src/prefs_customheader.h @@ -0,0 +1,27 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2001 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PREFS_CUSTOMHEADER_H__ +#define __PREFS_CUSTOMHEADER_H__ + +#include "prefs_account.h" + +void prefs_custom_header_open (PrefsAccount *ac); + +#endif /* __PREFS_CUSTOMHEADER_H__ */ diff --git a/src/prefs_display_header.c b/src/prefs_display_header.c new file mode 100644 index 0000000..dcab6cf --- /dev/null +++ b/src/prefs_display_header.c @@ -0,0 +1,640 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prefs.h" +#include "prefs_ui.h" +#include "prefs_display_header.h" +#include "prefs_common.h" +#include "manage_window.h" +#include "alertpanel.h" +#include "displayheader.h" +#include "utils.h" +#include "gtkutils.h" + +static struct DisplayHeader { + GtkWidget *window; + + GtkWidget *confirm_area; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + + GtkWidget *hdr_combo; + GtkWidget *hdr_entry; + GtkWidget *key_check; + GtkWidget *headers_clist; + GtkWidget *hidden_headers_clist; + + GtkWidget *other_headers; +} dispheader; + +/* widget creating functions */ +static void prefs_display_header_create (void); + +static void prefs_display_header_set_dialog (void); +static void prefs_display_header_set_list (void); +static gint prefs_display_header_clist_set_row (gboolean hidden); + +/* callback functions */ +static void prefs_display_header_register_cb (GtkButton *btn, + gpointer hidden_data); +static void prefs_display_header_delete_cb (GtkButton *btn, + gpointer clist_data); +static void prefs_display_header_up (void); +static void prefs_display_header_down (void); + +static void prefs_display_header_row_moved (GtkCList *clist, + gint source_row, + gint dest_row, + gpointer data); + +static gboolean prefs_display_header_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gpointer data); +static void prefs_display_header_ok (void); +static void prefs_display_header_cancel (void); +static gint prefs_display_header_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); + +static gchar *defaults[] = +{ + "From", + "To", + "Cc", + "Reply-To", + "Newsgroups", + "Followup-To", + "Subject", + "Date", + "Sender", + "Organization", + "X-Mailer", + "X-Newsreader", + "User-Agent", + "-Received", + "-Message-Id", + "-In-Reply-To", + "-References", + "-Mime-Version", + "-Content-Type", + "-Content-Transfer-Encoding", + "-X-UIDL", + "-Precedence", + "-Status", + "-Priority", + "-X-Face" +}; + +static void prefs_display_header_set_default(void) +{ + gint i; + DisplayHeaderProp *dp; + + for(i = 0; i < sizeof(defaults) / sizeof(defaults[0]); i++) { + dp = display_header_prop_read_str(defaults[i]); + prefs_common.disphdr_list = + g_slist_append(prefs_common.disphdr_list, dp); + } +} + +void prefs_display_header_open(void) +{ + if (!dispheader.window) { + prefs_display_header_create(); + } + + gtkut_box_set_reverse_order(GTK_BOX(dispheader.confirm_area), + !prefs_common.comply_gnome_hig); + manage_window_set_transient(GTK_WINDOW(dispheader.window)); + gtk_widget_grab_focus(dispheader.ok_btn); + + prefs_display_header_set_dialog(); + + gtk_widget_show(dispheader.window); +} + +static void prefs_display_header_create(void) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *btn_hbox; + GtkWidget *confirm_area; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + + GtkWidget *vbox1; + + GtkWidget *hbox1; + GtkWidget *hdr_label; + GtkWidget *hdr_combo; + + GtkWidget *btn_vbox; + GtkWidget *reg_btn; + GtkWidget *del_btn; + GtkWidget *up_btn; + GtkWidget *down_btn; + + GtkWidget *clist_hbox; + GtkWidget *clist_hbox1; + GtkWidget *clist_hbox2; + GtkWidget *clist_scrolledwin; + GtkWidget *headers_clist; + GtkWidget *hidden_headers_clist; + + GtkWidget *checkbtn_other_headers; + + gchar *title[1]; + + debug_print(_("Creating display header setting window...\n")); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (window), 8); + gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER); + gtk_window_set_modal (GTK_WINDOW (window), TRUE); + gtk_window_set_policy (GTK_WINDOW (window), FALSE, TRUE, FALSE); + + vbox = gtk_vbox_new (FALSE, 6); + gtk_widget_show (vbox); + gtk_container_add (GTK_CONTAINER (window), vbox); + + btn_hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (btn_hbox); + gtk_box_pack_end (GTK_BOX (vbox), btn_hbox, FALSE, FALSE, 0); + + gtkut_stock_button_set_create(&confirm_area, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_widget_show (confirm_area); + gtk_box_pack_end (GTK_BOX(btn_hbox), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default (ok_btn); + + gtk_window_set_title (GTK_WINDOW(window), + _("Display header setting")); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + g_signal_connect (G_OBJECT(window), "delete_event", + G_CALLBACK(prefs_display_header_deleted), NULL); + g_signal_connect (G_OBJECT(window), "key_press_event", + G_CALLBACK(prefs_display_header_key_pressed), NULL); + g_signal_connect (G_OBJECT(ok_btn), "clicked", + G_CALLBACK(prefs_display_header_ok), NULL); + g_signal_connect (G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(prefs_display_header_cancel), NULL); + + vbox1 = gtk_vbox_new (FALSE, VSPACING); + gtk_widget_show (vbox1); + gtk_box_pack_start (GTK_BOX (vbox), vbox1, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2); + + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, TRUE, 0); + + hdr_label = gtk_label_new (_("Header name")); + gtk_widget_show (hdr_label); + gtk_box_pack_start (GTK_BOX (hbox1), hdr_label, FALSE, FALSE, 0); + + hdr_combo = gtk_combo_new (); + gtk_widget_show (hdr_combo); + gtk_box_pack_start (GTK_BOX (hbox1), hdr_combo, TRUE, TRUE, 0); + gtk_widget_set_size_request (hdr_combo, 150, -1); + gtkut_combo_set_items (GTK_COMBO (hdr_combo), + "From", "To", "Cc", "Subject", "Date", + "Reply-To", "Sender", "User-Agent", "X-Mailer", + NULL); + + clist_hbox = gtk_hbox_new (FALSE, 10); + gtk_widget_show (clist_hbox); + gtk_box_pack_start (GTK_BOX (vbox1), clist_hbox, TRUE, TRUE, 0); + + /* display headers list */ + + clist_hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (clist_hbox1); + gtk_box_pack_start (GTK_BOX (clist_hbox), clist_hbox1, TRUE, TRUE, 0); + + clist_scrolledwin = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_set_size_request (clist_scrolledwin, 200, 210); + gtk_widget_show (clist_scrolledwin); + gtk_box_pack_start (GTK_BOX (clist_hbox1), clist_scrolledwin, + TRUE, TRUE, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (clist_scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + title[0] = _("Displayed Headers"); + headers_clist = gtk_clist_new_with_titles(1, title); + gtk_widget_show (headers_clist); + gtk_container_add (GTK_CONTAINER (clist_scrolledwin), headers_clist); + gtk_clist_set_selection_mode (GTK_CLIST (headers_clist), + GTK_SELECTION_BROWSE); + gtk_clist_set_reorderable (GTK_CLIST (headers_clist), TRUE); + gtk_clist_set_use_drag_icons (GTK_CLIST (headers_clist), FALSE); + gtkut_clist_set_redraw (GTK_CLIST (headers_clist)); + GTK_WIDGET_UNSET_FLAGS (GTK_CLIST (headers_clist)->column[0].button, + GTK_CAN_FOCUS); + g_signal_connect_after + (G_OBJECT (headers_clist), "row_move", + G_CALLBACK (prefs_display_header_row_moved), NULL); + + btn_vbox = gtk_vbox_new (FALSE, 8); + gtk_widget_show (btn_vbox); + gtk_box_pack_start (GTK_BOX (clist_hbox1), btn_vbox, FALSE, FALSE, 0); + + reg_btn = gtk_button_new_with_label (_("Add")); + gtk_widget_show (reg_btn); + gtk_box_pack_start (GTK_BOX (btn_vbox), reg_btn, FALSE, TRUE, 0); + g_signal_connect (G_OBJECT (reg_btn), "clicked", + G_CALLBACK (prefs_display_header_register_cb), + GINT_TO_POINTER(FALSE)); + del_btn = gtk_button_new_with_label (_("Delete")); + gtk_widget_show (del_btn); + gtk_box_pack_start (GTK_BOX (btn_vbox), del_btn, FALSE, TRUE, 0); + g_signal_connect (G_OBJECT (del_btn), "clicked", + G_CALLBACK (prefs_display_header_delete_cb), + headers_clist); + + up_btn = gtk_button_new_with_label (_("Up")); + gtk_widget_show (up_btn); + gtk_box_pack_start (GTK_BOX (btn_vbox), up_btn, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (up_btn), "clicked", + G_CALLBACK (prefs_display_header_up), NULL); + + down_btn = gtk_button_new_with_label (_("Down")); + gtk_widget_show (down_btn); + gtk_box_pack_start (GTK_BOX (btn_vbox), down_btn, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (down_btn), "clicked", + G_CALLBACK (prefs_display_header_down), NULL); + + /* hidden headers list */ + + clist_hbox2 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (clist_hbox2); + gtk_box_pack_start (GTK_BOX (clist_hbox), clist_hbox2, TRUE, TRUE, 0); + + clist_scrolledwin = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_set_size_request (clist_scrolledwin, 200, 210); + gtk_widget_show (clist_scrolledwin); + gtk_box_pack_start (GTK_BOX (clist_hbox2), clist_scrolledwin, + TRUE, TRUE, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (clist_scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + title[0] = _("Hidden headers"); + hidden_headers_clist = gtk_clist_new_with_titles(1, title); + gtk_widget_show (hidden_headers_clist); + gtk_container_add (GTK_CONTAINER (clist_scrolledwin), + hidden_headers_clist); + gtk_clist_set_selection_mode (GTK_CLIST (hidden_headers_clist), + GTK_SELECTION_BROWSE); + gtk_clist_set_auto_sort(GTK_CLIST (hidden_headers_clist), TRUE); + gtkut_clist_set_redraw (GTK_CLIST (hidden_headers_clist)); + GTK_WIDGET_UNSET_FLAGS (GTK_CLIST (hidden_headers_clist)-> + column[0].button, GTK_CAN_FOCUS); + + btn_vbox = gtk_vbox_new (FALSE, 8); + gtk_widget_show (btn_vbox); + gtk_box_pack_start (GTK_BOX (clist_hbox2), btn_vbox, FALSE, FALSE, 0); + + reg_btn = gtk_button_new_with_label (_("Add")); + gtk_widget_show (reg_btn); + gtk_box_pack_start (GTK_BOX (btn_vbox), reg_btn, FALSE, TRUE, 0); + g_signal_connect (G_OBJECT (reg_btn), "clicked", + G_CALLBACK (prefs_display_header_register_cb), + GINT_TO_POINTER (TRUE)); + del_btn = gtk_button_new_with_label (_("Delete")); + gtk_widget_show (del_btn); + gtk_box_pack_start (GTK_BOX (btn_vbox), del_btn, FALSE, TRUE, 0); + g_signal_connect (G_OBJECT (del_btn), "clicked", + G_CALLBACK (prefs_display_header_delete_cb), + hidden_headers_clist); + + PACK_CHECK_BUTTON (btn_hbox, checkbtn_other_headers, + _("Show all unspecified headers")); + SET_TOGGLE_SENSITIVITY (checkbtn_other_headers, clist_hbox2); + + gtk_widget_show_all(window); + + dispheader.window = window; + + dispheader.confirm_area = confirm_area; + dispheader.ok_btn = ok_btn; + dispheader.cancel_btn = cancel_btn; + + dispheader.hdr_combo = hdr_combo; + dispheader.hdr_entry = GTK_COMBO (hdr_combo)->entry; + + dispheader.headers_clist = headers_clist; + dispheader.hidden_headers_clist = hidden_headers_clist; + + dispheader.other_headers = checkbtn_other_headers; +} + +void prefs_display_header_read_config(void) +{ + gchar *rcpath; + FILE *fp; + gchar buf[PREFSBUFSIZE]; + DisplayHeaderProp *dp; + + debug_print(_("Reading configuration for displaying headers...\n")); + + rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, + DISPLAY_HEADER_RC, NULL); + if ((fp = g_fopen(rcpath, "rb")) == NULL) { + if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen"); + g_free(rcpath); + prefs_common.disphdr_list = NULL; + prefs_display_header_set_default(); + return; + } + g_free(rcpath); + + /* remove all previous headers list */ + while (prefs_common.disphdr_list != NULL) { + dp = (DisplayHeaderProp *)prefs_common.disphdr_list->data; + display_header_prop_free(dp); + prefs_common.disphdr_list = + g_slist_remove(prefs_common.disphdr_list, dp); + } + + while (fgets(buf, sizeof(buf), fp) != NULL) { + g_strchomp(buf); + dp = display_header_prop_read_str(buf); + if (dp) + prefs_common.disphdr_list = + g_slist_append(prefs_common.disphdr_list, dp); + } + + fclose(fp); +} + +void prefs_display_header_write_config(void) +{ + gchar *rcpath; + PrefFile *pfile; + GSList *cur; + + debug_print(_("Writing configuration for displaying headers...\n")); + + rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, + DISPLAY_HEADER_RC, NULL); + + if ((pfile = prefs_file_open(rcpath)) == NULL) { + g_warning(_("failed to write configuration to file\n")); + g_free(rcpath); + return; + } + + for (cur = prefs_common.disphdr_list; cur != NULL; + cur = cur->next) { + DisplayHeaderProp *dp = (DisplayHeaderProp *)cur->data; + gchar *dpstr; + + dpstr = display_header_prop_get_str(dp); + if (fputs(dpstr, pfile->fp) == EOF || + fputc('\n', pfile->fp) == EOF) { + FILE_OP_ERROR(rcpath, "fputs || fputc"); + prefs_file_close_revert(pfile); + g_free(rcpath); + g_free(dpstr); + return; + } + g_free(dpstr); + } + + g_free(rcpath); + + if (prefs_file_close(pfile) < 0) { + g_warning(_("failed to write configuration to file\n")); + return; + } +} + +static void prefs_display_header_set_dialog(void) +{ + GtkCList *clist = GTK_CLIST(dispheader.headers_clist); + GtkCList *hidden_clist = GTK_CLIST(dispheader.hidden_headers_clist); + GSList *cur; + gchar *dp_str[1]; + gint row; + + gtk_clist_freeze(clist); + gtk_clist_freeze(hidden_clist); + + gtk_clist_clear(clist); + gtk_clist_clear(hidden_clist); + + for (cur = prefs_common.disphdr_list; cur != NULL; + cur = cur->next) { + DisplayHeaderProp *dp = (DisplayHeaderProp *)cur->data; + + dp_str[0] = dp->name; + + if (dp->hidden) { + row = gtk_clist_append(hidden_clist, dp_str); + gtk_clist_set_row_data(hidden_clist, row, dp); + } else { + row = gtk_clist_append(clist, dp_str); + gtk_clist_set_row_data(clist, row, dp); + } + } + + gtk_clist_thaw(hidden_clist); + gtk_clist_thaw(clist); + + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON(dispheader.other_headers), + prefs_common.show_other_header); +} + +static void prefs_display_header_set_list() +{ + gint row = 0; + DisplayHeaderProp *dp; + + g_slist_free(prefs_common.disphdr_list); + prefs_common.disphdr_list = NULL; + + while ((dp = gtk_clist_get_row_data + (GTK_CLIST(dispheader.headers_clist), row)) != NULL) { + prefs_common.disphdr_list = + g_slist_append(prefs_common.disphdr_list, dp); + row++; + } + + row = 0; + while ((dp = gtk_clist_get_row_data + (GTK_CLIST(dispheader.hidden_headers_clist), row)) != NULL) { + prefs_common.disphdr_list = + g_slist_append(prefs_common.disphdr_list, dp); + row++; + } +} + +static gint prefs_display_header_find_header(GtkCList *clist, + const gchar *header) +{ + gint row = 0; + DisplayHeaderProp *dp; + + while ((dp = gtk_clist_get_row_data(clist, row)) != NULL) { + if (g_ascii_strcasecmp(dp->name, header) == 0) + return row; + row++; + } + + return -1; +} + +static gint prefs_display_header_clist_set_row(gboolean hidden) +{ + GtkCList *clist; + DisplayHeaderProp *dp; + const gchar *entry_text; + gchar *dp_str[1]; + gint row; + + entry_text = gtk_entry_get_text(GTK_ENTRY(dispheader.hdr_entry)); + if (entry_text[0] == '\0') { + alertpanel_error(_("Header name is not set.")); + return -1; + } + + if (hidden) + clist = GTK_CLIST(dispheader.hidden_headers_clist); + else + clist = GTK_CLIST(dispheader.headers_clist); + + if (prefs_display_header_find_header(clist, entry_text) != -1) { + alertpanel_error(_("This header is already in the list.")); + return -1; + } + + dp = g_new0(DisplayHeaderProp, 1); + + dp->name = g_strdup(entry_text); + dp->hidden = hidden; + + dp_str[0] = dp->name; + row = gtk_clist_append(clist, dp_str); + gtk_clist_set_row_data(clist, row, dp); + + prefs_display_header_set_list(); + + return row; +} + +static void prefs_display_header_register_cb(GtkButton *btn, + gpointer hidden_data) +{ + prefs_display_header_clist_set_row(GPOINTER_TO_INT(hidden_data)); +} + +static void prefs_display_header_delete_cb(GtkButton *btn, gpointer clist_data) +{ + GtkCList *clist = GTK_CLIST(clist_data); + DisplayHeaderProp *dp; + gint row; + + if (!clist->selection) return; + row = GPOINTER_TO_INT(clist->selection->data); + + dp = gtk_clist_get_row_data(clist, row); + display_header_prop_free(dp); + gtk_clist_remove(clist, row); + prefs_common.disphdr_list = + g_slist_remove(prefs_common.disphdr_list, dp); +} + +static void prefs_display_header_up(void) +{ + GtkCList *clist = GTK_CLIST(dispheader.headers_clist); + gint row; + + if (!clist->selection) return; + + row = GPOINTER_TO_INT(clist->selection->data); + if (row > 0) + gtk_clist_row_move(clist, row, row - 1); +} + +static void prefs_display_header_down(void) +{ + GtkCList *clist = GTK_CLIST(dispheader.headers_clist); + gint row; + + if (!clist->selection) return; + + row = GPOINTER_TO_INT(clist->selection->data); + if (row >= 0 && row < clist->rows - 1) + gtk_clist_row_move(clist, row, row + 1); +} + +static void prefs_display_header_row_moved(GtkCList *clist, gint source_row, + gint dest_row, gpointer data) +{ + prefs_display_header_set_list(); +} + +static gboolean prefs_display_header_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + prefs_display_header_cancel(); + return FALSE; +} + +static void prefs_display_header_ok(void) +{ + prefs_common.show_other_header = + gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(dispheader.other_headers)); + prefs_display_header_write_config(); + gtk_widget_hide(dispheader.window); +} + +static void prefs_display_header_cancel(void) +{ + prefs_display_header_read_config(); + gtk_widget_hide(dispheader.window); +} + +static gint prefs_display_header_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + prefs_display_header_cancel(); + return TRUE; +} diff --git a/src/prefs_display_header.h b/src/prefs_display_header.h new file mode 100644 index 0000000..bf1f4f2 --- /dev/null +++ b/src/prefs_display_header.h @@ -0,0 +1,27 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2001 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PREFS_DISPLAY_HEADER_H__ +#define __PREFS_DISPLAY_HEADER_H__ + +void prefs_display_header_read_config (void); +void prefs_display_header_write_config (void); +void prefs_display_header_open (void); + +#endif /* __PREFS_DISPLAY_HEADER_H__ */ diff --git a/src/prefs_display_items.c b/src/prefs_display_items.c new file mode 100644 index 0000000..bd554e3 --- /dev/null +++ b/src/prefs_display_items.c @@ -0,0 +1,623 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2015 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include + +#include "prefs.h" +#include "prefs_ui.h" +#include "prefs_common.h" +#include "prefs_display_items.h" +#include "manage_window.h" +#include "mainwindow.h" +#include "gtkutils.h" +#include "utils.h" + +#define SCROLLED_WINDOW_WIDTH 180 +#define SCROLLED_WINDOW_HEIGHT 210 + +static void prefs_display_items_set_sensitive(PrefsDisplayItemsDialog *dialog); + +/* callback functions */ +static void prefs_display_items_add (GtkWidget *widget, + gpointer data); +static void prefs_display_items_remove (GtkWidget *widget, + gpointer data); + +static void prefs_display_items_up (GtkWidget *widget, + gpointer data); +static void prefs_display_items_down (GtkWidget *widget, + gpointer data); + +static void prefs_display_items_default (GtkWidget *widget, + gpointer data); + +static void prefs_display_items_ok (GtkWidget *widget, + gpointer data); +static void prefs_display_items_cancel (GtkWidget *widget, + gpointer data); + +static void prefs_display_items_shown_select_row(GtkWidget *widget, + gint row, + gint column, + GdkEventButton *event, + gpointer data); +static void prefs_display_items_shown_row_move (GtkWidget *widget, + gint row, + gint column, + gpointer data); + +static gint prefs_display_items_delete_event (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean prefs_display_items_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); + +PrefsDisplayItemsDialog *prefs_display_items_dialog_create(void) +{ + PrefsDisplayItemsDialog *dialog; + + GtkWidget *window; + GtkWidget *vbox; + + GtkWidget *label_hbox; + GtkWidget *label; + + GtkWidget *vbox1; + + GtkWidget *hbox1; + GtkWidget *clist_hbox; + GtkWidget *scrolledwin; + GtkWidget *stock_clist; + GtkWidget *shown_clist; + + GtkWidget *btn_vbox; + GtkWidget *btn_vbox1; + GtkWidget *add_btn; + GtkWidget *remove_btn; + GtkWidget *up_btn; + GtkWidget *down_btn; + + GtkWidget *btn_hbox; + GtkWidget *default_btn; + GtkWidget *confirm_area; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + + gchar *title[1]; + + dialog = g_new0(PrefsDisplayItemsDialog, 1); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + gtk_window_set_position(GTK_WINDOW(window), + GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE); + gtk_window_set_title(GTK_WINDOW(window), _("Display items setting")); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(prefs_display_items_delete_event), dialog); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(prefs_display_items_key_pressed), dialog); + + vbox = gtk_vbox_new(FALSE, 6); + gtk_widget_show(vbox); + gtk_container_add(GTK_CONTAINER(window), vbox); + + label_hbox = gtk_hbox_new(FALSE, 0); + gtk_widget_show(label_hbox); + gtk_box_pack_start(GTK_BOX(vbox), label_hbox, FALSE, FALSE, 4); + + label = gtk_label_new(""); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(label_hbox), label, FALSE, FALSE, 4); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); + + vbox1 = gtk_vbox_new(FALSE, VSPACING); + gtk_widget_show(vbox1); + gtk_box_pack_start(GTK_BOX(vbox), vbox1, TRUE, TRUE, 0); + gtk_container_set_border_width(GTK_CONTAINER(vbox1), 2); + + hbox1 = gtk_hbox_new(FALSE, 8); + gtk_widget_show(hbox1); + gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, TRUE, 0); + + clist_hbox = gtk_hbox_new(FALSE, 8); + gtk_widget_show(clist_hbox); + gtk_box_pack_start(GTK_BOX(hbox1), clist_hbox, TRUE, TRUE, 0); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_set_size_request + (scrolledwin, + SCROLLED_WINDOW_WIDTH * gtkut_get_dpi_multiplier(), + SCROLLED_WINDOW_HEIGHT * gtkut_get_dpi_multiplier()); + gtk_widget_show(scrolledwin); + gtk_box_pack_start(GTK_BOX(clist_hbox), scrolledwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + title[0] = _("Available items"); + stock_clist = gtk_clist_new_with_titles(1, title); + gtk_widget_show(stock_clist); + gtk_container_add(GTK_CONTAINER(scrolledwin), stock_clist); + gtk_clist_set_selection_mode(GTK_CLIST(stock_clist), + GTK_SELECTION_BROWSE); + GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(stock_clist)->column[0].button, + GTK_CAN_FOCUS); + gtkut_clist_set_redraw(GTK_CLIST(stock_clist)); + + /* add/remove button */ + btn_vbox = gtk_vbox_new(FALSE, 0); + gtk_widget_show(btn_vbox); + gtk_box_pack_start(GTK_BOX(hbox1), btn_vbox, FALSE, FALSE, 0); + + btn_vbox1 = gtk_vbox_new(FALSE, 8); + gtk_widget_show(btn_vbox1); + gtk_box_pack_start(GTK_BOX(btn_vbox), btn_vbox1, TRUE, FALSE, 0); + + add_btn = gtk_button_new_with_label(_(" -> ")); + gtk_widget_show(add_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox1), add_btn, FALSE, FALSE, 0); + + remove_btn = gtk_button_new_with_label(_(" <- ")); + gtk_widget_show(remove_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox1), remove_btn, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(add_btn), "clicked", + G_CALLBACK(prefs_display_items_add), dialog); + g_signal_connect(G_OBJECT(remove_btn), "clicked", + G_CALLBACK(prefs_display_items_remove), dialog); + + clist_hbox = gtk_hbox_new(FALSE, 8); + gtk_widget_show(clist_hbox); + gtk_box_pack_start(GTK_BOX(hbox1), clist_hbox, TRUE, TRUE, 0); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_set_size_request + (scrolledwin, + SCROLLED_WINDOW_WIDTH * gtkut_get_dpi_multiplier(), + SCROLLED_WINDOW_HEIGHT * gtkut_get_dpi_multiplier()); + gtk_widget_show(scrolledwin); + gtk_box_pack_start(GTK_BOX(clist_hbox), scrolledwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + title[0] = _("Displayed items"); + shown_clist = gtk_clist_new_with_titles(1, title); + gtk_widget_show(shown_clist); + gtk_container_add(GTK_CONTAINER(scrolledwin), shown_clist); + gtk_clist_set_selection_mode(GTK_CLIST(shown_clist), + GTK_SELECTION_BROWSE); +#if 0 + gtk_clist_set_reorderable(GTK_CLIST(shown_clist), TRUE); + gtk_clist_set_use_drag_icons(GTK_CLIST(shown_clist), FALSE); +#endif + GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(shown_clist)->column[0].button, + GTK_CAN_FOCUS); + gtkut_clist_set_redraw(GTK_CLIST(shown_clist)); + + g_signal_connect(G_OBJECT(shown_clist), "select-row", + G_CALLBACK(prefs_display_items_shown_select_row), + dialog); + g_signal_connect_after(G_OBJECT(shown_clist), "row-move", + G_CALLBACK(prefs_display_items_shown_row_move), + dialog); + + /* up/down button */ + btn_vbox = gtk_vbox_new(FALSE, 0); + gtk_widget_show(btn_vbox); + gtk_box_pack_start(GTK_BOX(hbox1), btn_vbox, FALSE, FALSE, 0); + + btn_vbox1 = gtk_vbox_new(FALSE, 8); + gtk_widget_show(btn_vbox1); + gtk_box_pack_start(GTK_BOX(btn_vbox), btn_vbox1, TRUE, FALSE, 0); + + up_btn = gtk_button_new_with_label(_("Up")); + gtk_widget_show(up_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox1), up_btn, FALSE, FALSE, 0); + + down_btn = gtk_button_new_with_label(_("Down")); + gtk_widget_show(down_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox1), down_btn, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(up_btn), "clicked", + G_CALLBACK(prefs_display_items_up), dialog); + g_signal_connect(G_OBJECT(down_btn), "clicked", + G_CALLBACK(prefs_display_items_down), dialog); + + btn_hbox = gtk_hbox_new(FALSE, 8); + gtk_widget_show(btn_hbox); + gtk_box_pack_end(GTK_BOX(vbox), btn_hbox, FALSE, FALSE, 0); + + btn_vbox = gtk_vbox_new(FALSE, 0); + gtk_widget_show(btn_vbox); + gtk_box_pack_start(GTK_BOX(btn_hbox), btn_vbox, FALSE, FALSE, 0); + + default_btn = gtk_button_new_with_label(_(" Revert to default ")); + gtk_widget_show(default_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox), default_btn, TRUE, FALSE, 0); + + g_signal_connect(G_OBJECT(default_btn), "clicked", + G_CALLBACK(prefs_display_items_default), dialog); + + gtkut_stock_button_set_create(&confirm_area, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_widget_show(confirm_area); + gtk_box_pack_end(GTK_BOX(btn_hbox), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_btn); + + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(prefs_display_items_ok), dialog); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(prefs_display_items_cancel), dialog); + + dialog->window = window; + dialog->label = label; + dialog->stock_clist = stock_clist; + dialog->shown_clist = shown_clist; + dialog->add_btn = add_btn; + dialog->remove_btn = remove_btn; + dialog->up_btn = up_btn; + dialog->down_btn = down_btn; + dialog->confirm_area = confirm_area; + dialog->ok_btn = ok_btn; + dialog->cancel_btn = cancel_btn; + + gtkut_box_set_reverse_order(GTK_BOX(dialog->confirm_area), + !prefs_common.comply_gnome_hig); + manage_window_set_transient(GTK_WINDOW(dialog->window)); + gtk_widget_grab_focus(dialog->ok_btn); + + dialog->finished = FALSE; + dialog->cancelled = FALSE; + + return dialog; +} + +void prefs_display_items_dialog_destroy(PrefsDisplayItemsDialog *dialog) +{ + if (!dialog) + return; + + if (dialog->available_items) + g_list_free(dialog->available_items); + if (dialog->visible_items) + g_list_free(dialog->visible_items); + gtk_widget_destroy(dialog->window); + g_free(dialog); +} + +static void prefs_display_items_update_available + (PrefsDisplayItemsDialog *dialog) +{ + GtkCList *stock_clist = GTK_CLIST(dialog->stock_clist); + GList *cur; + + g_return_if_fail(dialog->available_items != NULL); + + gtk_clist_clear(stock_clist); + + for (cur = dialog->available_items; cur != NULL; cur = cur->next) { + PrefsDisplayItem *item = cur->data; + gint row; + gchar *name; + + if (item->allow_multiple || item->in_use == FALSE) { + name = gettext(item->label); + row = gtk_clist_append(stock_clist, (gchar **)&name); + gtk_clist_set_row_data(stock_clist, row, item); + } + } +} + +static PrefsDisplayItem *prefs_display_items_get_item_from_id + (PrefsDisplayItemsDialog *dialog, gint id) +{ + gint i; + + for (i = 0; dialog->all_items[i].id != -1; i++) { + if (id == dialog->all_items[i].id) + return (PrefsDisplayItem *)&dialog->all_items[i]; + } + + return NULL; +} + +void prefs_display_items_dialog_set_available(PrefsDisplayItemsDialog *dialog, + PrefsDisplayItem *all_items, + const gint *ids) +{ + gint i; + GList *list = NULL; + + dialog->all_items = all_items; + for (i = 0; ids[i] != -1; i++) { + PrefsDisplayItem *item; + + item = prefs_display_items_get_item_from_id(dialog, ids[i]); + if (item) + list = g_list_append(list, item); + } + dialog->available_items = list; + prefs_display_items_update_available(dialog); +} + +void prefs_display_items_dialog_set_default_visible + (PrefsDisplayItemsDialog *dialog, + const gint *ids) +{ + dialog->default_visible_ids = ids; +} + +void prefs_display_items_dialog_set_visible(PrefsDisplayItemsDialog *dialog, + const gint *ids) +{ + GtkCList *shown_clist = GTK_CLIST(dialog->shown_clist); + GList *cur; + PrefsDisplayItem *item; + gint i; + gint row; + gchar *name; + + g_return_if_fail(dialog->available_items != NULL); + + if (!ids) + ids = dialog->default_visible_ids; + g_return_if_fail(ids != NULL); + + gtk_clist_clear(shown_clist); + + if (dialog->visible_items) { + g_list_free(dialog->visible_items); + dialog->visible_items = NULL; + } + + for (cur = dialog->available_items; cur != NULL; cur = cur->next) { + item = cur->data; + item->in_use = FALSE; + } + + for (i = 0; ids[i] != -1; i++) { + gint id = ids[i]; + + item = prefs_display_items_get_item_from_id(dialog, id); + + g_return_if_fail(item != NULL); + g_return_if_fail(item->allow_multiple || item->in_use == FALSE); + + item->in_use = TRUE; + + name = gettext(item->label); + row = gtk_clist_append(shown_clist, (gchar **)&name); + gtk_clist_set_row_data(shown_clist, row, item); + } + + name = "--------"; + row = gtk_clist_append(shown_clist, (gchar **)&name); + gtk_widget_ensure_style(GTK_WIDGET(shown_clist)); + gtk_clist_set_foreground + (shown_clist, row, + >K_WIDGET(shown_clist)->style->text[GTK_STATE_INSENSITIVE]); + + prefs_display_items_update_available(dialog); + prefs_display_items_set_sensitive(dialog); + gtk_clist_moveto(shown_clist, 0, 0, 0, 0); +} + +static void prefs_display_items_set_sensitive(PrefsDisplayItemsDialog *dialog) +{ + GtkCList *clist = GTK_CLIST(dialog->shown_clist); + gint row; + + if (!clist->selection) return; + + row = GPOINTER_TO_INT(clist->selection->data); + + if (gtk_clist_get_row_data(clist, row)) + gtk_widget_set_sensitive(dialog->remove_btn, TRUE); + else + gtk_widget_set_sensitive(dialog->remove_btn, FALSE); + + if (row > 0 && row < clist->rows - 1) + gtk_widget_set_sensitive(dialog->up_btn, TRUE); + else + gtk_widget_set_sensitive(dialog->up_btn, FALSE); + + if (row >= 0 && row < clist->rows - 2) + gtk_widget_set_sensitive(dialog->down_btn, TRUE); + else + gtk_widget_set_sensitive(dialog->down_btn, FALSE); + + if (gtk_clist_row_is_visible(clist, row) != GTK_VISIBILITY_FULL) + gtk_clist_moveto(clist, row, 0, 0.5, 0); +} + +static void prefs_display_items_add(GtkWidget *widget, gpointer data) +{ + PrefsDisplayItemsDialog *dialog = data; + GtkCList *stock_clist = GTK_CLIST(dialog->stock_clist); + GtkCList *shown_clist = GTK_CLIST(dialog->shown_clist); + PrefsDisplayItem *item; + gint row; + gchar *name; + + if (!stock_clist->selection) return; + + row = GPOINTER_TO_INT(stock_clist->selection->data); + item = (PrefsDisplayItem *)gtk_clist_get_row_data(stock_clist, row); + if (!item->allow_multiple) { + gtk_clist_remove(stock_clist, row); + if (stock_clist->rows == row) + gtk_clist_select_row(stock_clist, row - 1, -1); + } + + if (!shown_clist->selection) + row = 0; + else + row = GPOINTER_TO_INT(shown_clist->selection->data); + + item->in_use = TRUE; + + name = gettext(item->label); + row = gtk_clist_insert(shown_clist, row, (gchar **)&name); + gtk_clist_set_row_data(shown_clist, row, item); + + prefs_display_items_set_sensitive(dialog); +} + +static void prefs_display_items_remove(GtkWidget *widget, gpointer data) +{ + PrefsDisplayItemsDialog *dialog = data; + GtkCList *shown_clist = GTK_CLIST(dialog->shown_clist); + PrefsDisplayItem *item; + gint row; + + if (!shown_clist->selection) return; + + row = GPOINTER_TO_INT(shown_clist->selection->data); + item = (PrefsDisplayItem *)gtk_clist_get_row_data(shown_clist, row); + if (!item) + return; + gtk_clist_remove(shown_clist, row); + if (shown_clist->rows == row) + gtk_clist_select_row(shown_clist, row - 1, -1); + + if (!item->allow_multiple) { + item->in_use = FALSE; + prefs_display_items_update_available(dialog); + } + + prefs_display_items_set_sensitive(dialog); +} + +static void prefs_display_items_up(GtkWidget *widget, gpointer data) +{ + PrefsDisplayItemsDialog *dialog = data; + GtkCList *shown_clist = GTK_CLIST(dialog->shown_clist); + gint row; + + if (!shown_clist->selection) return; + + row = GPOINTER_TO_INT(shown_clist->selection->data); + if (row > 0 && row < shown_clist->rows - 1) + gtk_clist_row_move(shown_clist, row, row - 1); +} + +static void prefs_display_items_down(GtkWidget *widget, gpointer data) +{ + PrefsDisplayItemsDialog *dialog = data; + GtkCList *shown_clist = GTK_CLIST(dialog->shown_clist); + gint row; + + if (!shown_clist->selection) return; + + row = GPOINTER_TO_INT(shown_clist->selection->data); + if (row >= 0 && row < shown_clist->rows - 2) + gtk_clist_row_move(shown_clist, row, row + 1); +} + +static void prefs_display_items_default(GtkWidget *widget, gpointer data) +{ + PrefsDisplayItemsDialog *dialog = data; + + prefs_display_items_dialog_set_visible(dialog, NULL); +} + +static void prefs_display_items_ok(GtkWidget *widget, gpointer data) +{ + PrefsDisplayItemsDialog *dialog = data; + GtkCList *shown_clist = GTK_CLIST(dialog->shown_clist); + GList *list = NULL; + PrefsDisplayItem *item; + gint row; + + for (row = 0; row < shown_clist->rows; row++) { + item = gtk_clist_get_row_data(shown_clist, row); + if (item) + list = g_list_append(list, item); + } + + dialog->visible_items = list; + dialog->finished = TRUE; +} + +static void prefs_display_items_cancel(GtkWidget *widget, gpointer data) +{ + PrefsDisplayItemsDialog *dialog = data; + + dialog->finished = TRUE; + dialog->cancelled = TRUE; +} + +static void prefs_display_items_shown_select_row(GtkWidget *widget, + gint row, + gint column, + GdkEventButton *event, + gpointer data) +{ + PrefsDisplayItemsDialog *dialog = data; + + prefs_display_items_set_sensitive(dialog); +} + +static void prefs_display_items_shown_row_move (GtkWidget *widget, + gint row, + gint column, + gpointer data) +{ + PrefsDisplayItemsDialog *dialog = data; + + prefs_display_items_set_sensitive(dialog); +} + +static gint prefs_display_items_delete_event(GtkWidget *widget, + GdkEventAny *event, + gpointer data) +{ + PrefsDisplayItemsDialog *dialog = data; + + dialog->finished = TRUE; + dialog->cancelled = TRUE; + return TRUE; +} + +static gboolean prefs_display_items_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gpointer data) +{ + PrefsDisplayItemsDialog *dialog = data; + + if (event && event->keyval == GDK_Escape) { + dialog->finished = TRUE; + dialog->cancelled = TRUE; + } + return FALSE; +} diff --git a/src/prefs_display_items.h b/src/prefs_display_items.h new file mode 100644 index 0000000..00c0180 --- /dev/null +++ b/src/prefs_display_items.h @@ -0,0 +1,87 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2007 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PREFS_DISPLAY_ITEMS_H__ +#define __PREFS_DISPLAY_ITEMS_H__ + +#include +#include + +typedef struct _PrefsDisplayItem PrefsDisplayItem; +typedef struct _PrefsDisplayItemsDialog PrefsDisplayItemsDialog; + +#include "stock_pixmap.h" + +struct _PrefsDisplayItem +{ + gint id; + gchar *name; + gchar *label; + gchar *description; + StockPixmap icon; + const gchar *stock_id; + gboolean allow_multiple; + gboolean in_use; +}; + +struct _PrefsDisplayItemsDialog +{ + GtkWidget *window; + + GtkWidget *label; + + GtkWidget *stock_clist; + GtkWidget *shown_clist; + + GtkWidget *add_btn; + GtkWidget *remove_btn; + GtkWidget *up_btn; + GtkWidget *down_btn; + + GtkWidget *default_btn; + + GtkWidget *confirm_area; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + + const PrefsDisplayItem *all_items; + GList *available_items; + const gint *default_visible_ids; + GList *visible_items; + + gboolean finished; + gboolean cancelled; +}; + +PrefsDisplayItemsDialog *prefs_display_items_dialog_create (void); + +void prefs_display_items_dialog_set_available + (PrefsDisplayItemsDialog *dialog, + PrefsDisplayItem *all_items, + const gint *ids); +void prefs_display_items_dialog_set_default_visible + (PrefsDisplayItemsDialog *dialog, + const gint *ids); +void prefs_display_items_dialog_set_visible + (PrefsDisplayItemsDialog *dialog, + const gint *ids); + +void prefs_display_items_dialog_destroy (PrefsDisplayItemsDialog *dialog); + +#endif /* __PREFS_DISPLAY_ITEMS_H__ */ diff --git a/src/prefs_filter.c b/src/prefs_filter.c new file mode 100644 index 0000000..671aab3 --- /dev/null +++ b/src/prefs_filter.c @@ -0,0 +1,875 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "prefs.h" +#include "prefs_ui.h" +#include "prefs_filter.h" +#include "prefs_filter_edit.h" +#include "prefs_common.h" +#include "mainwindow.h" +#include "foldersel.h" +#include "manage_window.h" +#include "stock_pixmap.h" +#include "inc.h" +#include "procheader.h" +#include "menu.h" +#include "filter.h" +#include "utils.h" +#include "gtkutils.h" +#include "alertpanel.h" +#include "xml.h" +#include "plugin.h" + +static struct FilterRuleListWindow { + GtkWidget *window; + + GtkWidget *treeview; + GtkListStore *store; + GtkTreeSelection *selection; + + GtkWidget *add_btn; + GtkWidget *edit_btn; + GtkWidget *copy_btn; + GtkWidget *del_btn; + + GSList *default_hdr_list; + GSList *user_hdr_list; + GSList *msg_hdr_list; + + GHashTable *msg_hdr_table; + + GtkWidget *close_btn; + + gboolean on_init; + gboolean modified; +} rule_list_window; + +enum { + COL_ENABLED, + COL_NAME, + COL_FILTER_RULE, + N_COLS +}; + +static void prefs_filter_create (void); + +static void prefs_filter_set_dialog (void); +static void prefs_filter_set_list_row (GtkTreeIter *iter, + FilterRule *rule, + gboolean move_view); + +static void prefs_filter_set_list (void); + +/* callback functions */ +static void prefs_filter_add_cb (void); +static void prefs_filter_edit_cb (void); +static void prefs_filter_copy_cb (void); +static void prefs_filter_delete_cb (void); +static void prefs_filter_top (void); +static void prefs_filter_up (void); +static void prefs_filter_down (void); +static void prefs_filter_bottom (void); + +static gboolean prefs_filter_select (GtkTreeSelection *selection, + GtkTreeModel *model, + GtkTreePath *path, + gboolean cur_selected, + gpointer data); +static void prefs_filter_enable_toggled (GtkCellRenderer *cell, + gchar *path, + gpointer data); + +static void prefs_filter_row_activated (GtkTreeView *treeview, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data); +static void prefs_filter_row_reordered (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data, + gpointer user_data); +static void prefs_filter_row_inserted (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data); + +static gint prefs_filter_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean prefs_filter_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gpointer data); +static void prefs_filter_close (void); + + +void prefs_filter_open(MsgInfo *msginfo, const gchar *header, const gchar *key) +{ + inc_lock(); + + rule_list_window.on_init = TRUE; + + if (!rule_list_window.window) + prefs_filter_create(); + + prefs_filter_set_header_list(msginfo); + + manage_window_set_transient(GTK_WINDOW(rule_list_window.window)); + gtk_widget_grab_focus(rule_list_window.close_btn); + + prefs_filter_set_dialog(); + + gtk_widget_show(rule_list_window.window); + manage_window_focus_in(rule_list_window.window, NULL, NULL); + + rule_list_window.modified = FALSE; + + syl_plugin_signal_emit("prefs-filter-open", rule_list_window.window); + + if (msginfo) { + FilterRule *rule; + + rule = prefs_filter_edit_open(NULL, header, key); + gtk_window_present(GTK_WINDOW(rule_list_window.window)); + + if (rule) { + prefs_filter_set_list_row(NULL, rule, TRUE); + prefs_filter_set_list(); + } + } + + rule_list_window.on_init = FALSE; +} + +static void prefs_filter_create(void) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *close_btn; + GtkWidget *confirm_area; + + GtkWidget *hbox; + GtkWidget *scrolledwin; + GtkWidget *treeview; + GtkListStore *store; + GtkTreeSelection *selection; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + GtkWidget *btn_vbox; + GtkWidget *spc_vbox; + GtkWidget *top_btn; + GtkWidget *up_btn; + GtkWidget *down_btn; + GtkWidget *bottom_btn; + + GtkWidget *btn_hbox; + GtkWidget *add_btn; + GtkWidget *edit_btn; + GtkWidget *copy_btn; + GtkWidget *del_btn; + GtkWidget *image; + + debug_print("Creating filter setting window...\n"); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + gtk_widget_set_size_request(window, 540 * gtkut_get_dpi_multiplier(), 360 * gtkut_get_dpi_multiplier()); + gtk_window_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, FALSE); + + vbox = gtk_vbox_new(FALSE, 6); + gtk_widget_show(vbox); + gtk_container_add(GTK_CONTAINER(window), vbox); + + gtkut_stock_button_set_create(&confirm_area, + &close_btn, GTK_STOCK_CLOSE, + NULL, NULL, NULL, NULL); + gtk_widget_show(confirm_area); + gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default(close_btn); + + gtk_window_set_title(GTK_WINDOW(window), + _("Filter settings")); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(prefs_filter_deleted), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(prefs_filter_key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT (window); + g_signal_connect(G_OBJECT(close_btn), "clicked", + G_CALLBACK(prefs_filter_close), NULL); + + /* Rule list */ + + hbox = gtk_hbox_new(FALSE, 8); + gtk_widget_show(hbox); + gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(scrolledwin); + gtk_widget_set_size_request(scrolledwin, -1, 150 * gtkut_get_dpi_multiplier()); + gtk_box_pack_start(GTK_BOX(hbox), scrolledwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + + store = gtk_list_store_new + (N_COLS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER); + + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), COL_NAME); + gtk_tree_view_set_reorderable(GTK_TREE_VIEW(treeview), TRUE); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); + gtk_tree_selection_set_select_function(selection, prefs_filter_select, + NULL, NULL); + + renderer = gtk_cell_renderer_toggle_new(); + g_signal_connect(renderer, "toggled", + G_CALLBACK(prefs_filter_enable_toggled), NULL); + column = gtk_tree_view_column_new_with_attributes + (_("Enabled"), renderer, "active", COL_ENABLED, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes + (_("Name"), renderer, "text", COL_NAME, NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + gtk_widget_show(treeview); + gtk_container_add(GTK_CONTAINER(scrolledwin), treeview); + + g_signal_connect(G_OBJECT(treeview), "row-activated", + G_CALLBACK(prefs_filter_row_activated), NULL); + g_signal_connect_after(G_OBJECT(store), "rows-reordered", + G_CALLBACK(prefs_filter_row_reordered), NULL); + g_signal_connect_after(G_OBJECT(store), "row-inserted", + G_CALLBACK(prefs_filter_row_inserted), NULL); + + /* Up / Down */ + + btn_vbox = gtk_vbox_new (FALSE, 8); + gtk_widget_show(btn_vbox); + gtk_box_pack_start(GTK_BOX(hbox), btn_vbox, FALSE, FALSE, 0); + + top_btn = gtk_button_new_from_stock(GTK_STOCK_GOTO_TOP); + gtk_widget_show(top_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox), top_btn, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(top_btn), "clicked", + G_CALLBACK(prefs_filter_top), NULL); + + PACK_VSPACER(btn_vbox, spc_vbox, VSPACING_NARROW_2); + + up_btn = gtk_button_new(); + image = gtk_image_new_from_stock(GTK_STOCK_GO_UP, GTK_ICON_SIZE_BUTTON); + gtk_widget_show(image); + gtk_button_set_image(GTK_BUTTON(up_btn), image); + gtk_widget_show(up_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox), up_btn, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(up_btn), "clicked", + G_CALLBACK(prefs_filter_up), NULL); + + down_btn = gtk_button_new(); + image = gtk_image_new_from_stock(GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_BUTTON); + gtk_widget_show(image); + gtk_button_set_image(GTK_BUTTON(down_btn), image); + gtk_widget_show(down_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox), down_btn, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(down_btn), "clicked", + G_CALLBACK(prefs_filter_down), NULL); + + PACK_VSPACER(btn_vbox, spc_vbox, VSPACING_NARROW_2); + + bottom_btn = gtk_button_new_from_stock(GTK_STOCK_GOTO_BOTTOM); + gtk_widget_show(bottom_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox), bottom_btn, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(bottom_btn), "clicked", + G_CALLBACK(prefs_filter_bottom), NULL); + + /* add / edit / copy / delete */ + + hbox = gtk_hbox_new(FALSE, 4); + gtk_widget_show(hbox); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + btn_hbox = gtk_hbox_new(TRUE, 4); + gtk_widget_show(btn_hbox); + gtk_box_pack_start(GTK_BOX(hbox), btn_hbox, FALSE, FALSE, 0); + + add_btn = gtk_button_new_from_stock(GTK_STOCK_ADD); + gtk_widget_show(add_btn); + gtk_box_pack_start(GTK_BOX(btn_hbox), add_btn, FALSE, TRUE, 0); + g_signal_connect(G_OBJECT(add_btn), "clicked", + G_CALLBACK(prefs_filter_add_cb), NULL); + +#ifdef GTK_STOCK_EDIT + edit_btn = gtk_button_new_from_stock(GTK_STOCK_EDIT); +#else + edit_btn = gtk_button_new_with_label(_("Edit")); +#endif + gtk_widget_show(edit_btn); + gtk_box_pack_start(GTK_BOX(btn_hbox), edit_btn, FALSE, TRUE, 0); + g_signal_connect(G_OBJECT(edit_btn), "clicked", + G_CALLBACK(prefs_filter_edit_cb), NULL); + + copy_btn = gtk_button_new_from_stock(GTK_STOCK_COPY); + gtk_widget_show(copy_btn); + gtk_box_pack_start(GTK_BOX(btn_hbox), copy_btn, FALSE, TRUE, 0); + g_signal_connect(G_OBJECT(copy_btn), "clicked", + G_CALLBACK(prefs_filter_copy_cb), NULL); + + del_btn = gtk_button_new_from_stock(GTK_STOCK_DELETE); + gtk_widget_show(del_btn); + gtk_box_pack_start(GTK_BOX(btn_hbox), del_btn, FALSE, TRUE, 0); + g_signal_connect(G_OBJECT(del_btn), "clicked", + G_CALLBACK(prefs_filter_delete_cb), NULL); + + gtk_widget_show_all(window); + + rule_list_window.window = window; + rule_list_window.close_btn = close_btn; + + rule_list_window.treeview = treeview; + rule_list_window.store = store; + rule_list_window.selection = selection; + + rule_list_window.add_btn = add_btn; + rule_list_window.edit_btn = edit_btn; + rule_list_window.copy_btn = copy_btn; + rule_list_window.del_btn = del_btn; + + rule_list_window.default_hdr_list = NULL; + rule_list_window.user_hdr_list = NULL; + rule_list_window.msg_hdr_list = NULL; + rule_list_window.msg_hdr_table = NULL; +} + +static void prefs_filter_set_dialog(void) +{ + GSList *cur; + + gtk_list_store_clear(rule_list_window.store); + + for (cur = prefs_common.fltlist; cur != NULL; cur = cur->next) { + FilterRule *rule = (FilterRule *)cur->data; + prefs_filter_set_list_row(NULL, rule, FALSE); + } +} + +static void prefs_filter_set_list_row(GtkTreeIter *iter, FilterRule *rule, + gboolean move_view) +{ + GtkListStore *store = rule_list_window.store; + gchar *rule_name; + GtkTreeIter iter_; + + g_return_if_fail(rule != NULL); + + if (rule->name && *rule->name) + rule_name = g_strdup(rule->name); + else + rule_name = filter_get_str(rule); + + if (!iter) { + gtk_list_store_append(store, &iter_); + gtk_list_store_set(store, &iter_, + COL_ENABLED, rule->enabled, + COL_NAME, rule_name, + COL_FILTER_RULE, rule, -1); + } else { + FilterRule *prev_rule = NULL; + + iter_ = *iter; + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter_, + COL_FILTER_RULE, &prev_rule, -1); + if (!prev_rule) { + g_warning("rule at the row not found\n"); + gtk_list_store_append(store, &iter_); + } + + gtk_list_store_set(store, &iter_, + COL_ENABLED, rule->enabled, + COL_NAME, rule_name, + COL_FILTER_RULE, rule, -1); + + if (prev_rule && prev_rule != rule) + filter_rule_free(prev_rule); + } + + g_free(rule_name); + + if (move_view) { + GtkTreePath *path; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter_); + gtk_tree_view_scroll_to_cell + (GTK_TREE_VIEW(rule_list_window.treeview), + path, NULL, TRUE, 0.5, 0.0); + gtk_tree_path_free(path); + } + + rule_list_window.modified = TRUE; +} + +#define APPEND_HDR_LIST(hdr_list) \ + for (cur = hdr_list; cur != NULL; cur = cur->next) { \ + header = (Header *)cur->data; \ + \ + if (!g_hash_table_lookup(table, header->name)) { \ + g_hash_table_insert(table, header->name, header); \ + list = procheader_add_header_list \ + (list, header->name, header->body); \ + } \ + } + +GSList *prefs_filter_get_header_list(void) +{ + GSList *list = NULL; + GSList *cur; + GHashTable *table; + Header *header; + + table = g_hash_table_new(str_case_hash, str_case_equal); + + APPEND_HDR_LIST(rule_list_window.default_hdr_list) + APPEND_HDR_LIST(rule_list_window.user_hdr_list); + APPEND_HDR_LIST(rule_list_window.msg_hdr_list); + + g_hash_table_destroy(table); + + return list; +} + +#undef APPEND_HDR_LIST + +GSList *prefs_filter_get_user_header_list(void) +{ + return rule_list_window.user_hdr_list; +} + +gchar *prefs_filter_get_msg_header_field(const gchar *header_name) +{ + if (!rule_list_window.msg_hdr_table) + return NULL; + + return (gchar *)g_hash_table_lookup + (rule_list_window.msg_hdr_table, header_name); +} + +void prefs_filter_set_user_header_list(GSList *list) +{ + procheader_header_list_destroy(rule_list_window.user_hdr_list); + rule_list_window.user_hdr_list = list; +} + +void prefs_filter_set_msg_header_list(MsgInfo *msginfo) +{ + gchar *file; + GSList *cur; + GSList *next; + Header *header; + + if (rule_list_window.msg_hdr_table) { + g_hash_table_destroy(rule_list_window.msg_hdr_table); + rule_list_window.msg_hdr_table = NULL; + } + if (rule_list_window.msg_hdr_list) { + procheader_header_list_destroy(rule_list_window.msg_hdr_list); + rule_list_window.msg_hdr_list = NULL; + } + + if (!msginfo) + return; + + file = procmsg_get_message_file(msginfo); + g_return_if_fail(file != NULL); + + rule_list_window.msg_hdr_list = + procheader_get_header_list_from_file(file); + + g_free(file); + + rule_list_window.msg_hdr_table = + g_hash_table_new(str_case_hash, str_case_equal); + + for (cur = rule_list_window.msg_hdr_list; cur != NULL; + cur = next) { + next = cur->next; + header = (Header *)cur->data; + if (!g_ascii_strcasecmp(header->name, "Received") || + !g_ascii_strcasecmp(header->name, "Mime-Version") || + !g_ascii_strcasecmp(header->name, "X-UIDL")) { + procheader_header_free(header); + rule_list_window.msg_hdr_list = + g_slist_remove(rule_list_window.msg_hdr_list, + header); + continue; + } + if (!g_hash_table_lookup(rule_list_window.msg_hdr_table, + header->name)) { + g_hash_table_insert(rule_list_window.msg_hdr_table, + header->name, header->body); + } + } +} + +void prefs_filter_set_header_list(MsgInfo *msginfo) +{ + GSList *list = NULL; + gchar *path; + FILE *fp; + + list = procheader_add_header_list(list, "From", NULL); + list = procheader_add_header_list(list, "To", NULL); + list = procheader_add_header_list(list, "Cc", NULL); + list = procheader_add_header_list(list, "Subject", NULL); + list = procheader_add_header_list(list, "Reply-To", NULL); + list = procheader_add_header_list(list, "List-Id", NULL); + list = procheader_add_header_list(list, "X-ML-Name", NULL); + + procheader_header_list_destroy(rule_list_window.default_hdr_list); + rule_list_window.default_hdr_list = list; + + list = NULL; + path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_HEADER_RC, + NULL); + if ((fp = g_fopen(path, "rb")) != NULL) { + gchar buf[PREFSBUFSIZE]; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + g_strstrip(buf); + if (buf[0] == '\0') continue; + list = procheader_add_header_list(list, buf, NULL); + } + + fclose(fp); + } else + if (ENOENT != errno) FILE_OP_ERROR(path, "fopen"); + g_free(path); + + prefs_filter_set_user_header_list(list); + prefs_filter_set_msg_header_list(msginfo); +} + +void prefs_filter_write_user_header_list(void) +{ + gchar *path; + PrefFile *pfile; + GSList *cur; + + path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_HEADER_RC, + NULL); + + if ((pfile = prefs_file_open(path)) == NULL) { + g_warning("failed to write filter user header list\n"); + g_free(path); + return; + } + g_free(path); + + for (cur = rule_list_window.user_hdr_list; cur != NULL; + cur = cur->next) { + Header *header = (Header *)cur->data; + fputs(header->name, pfile->fp); + fputc('\n', pfile->fp); + } + + if (prefs_file_close(pfile) < 0) + g_warning("failed to write filter user header list\n"); +} + +static void prefs_filter_set_list(void) +{ + FilterRule *rule; + GtkTreeIter iter; + GtkTreeModel *model = GTK_TREE_MODEL(rule_list_window.store); + + g_slist_free(prefs_common.fltlist); + prefs_common.fltlist = NULL; + + if (!gtk_tree_model_get_iter_first(model, &iter)) + return; + + do { + gtk_tree_model_get(model, &iter, COL_FILTER_RULE, &rule, -1); + if (rule) + prefs_common.fltlist = + g_slist_append(prefs_common.fltlist, rule); + } while (gtk_tree_model_iter_next(model, &iter)); +} + +static void prefs_filter_add_cb(void) +{ + FilterRule *rule; + + rule = prefs_filter_edit_open(NULL, NULL, NULL); + gtk_window_present(GTK_WINDOW(rule_list_window.window)); + + if (rule) { + prefs_filter_set_list_row(NULL, rule, TRUE); + prefs_filter_set_list(); + } +} + +static void prefs_filter_edit_cb(void) +{ + GtkTreeIter iter; + FilterRule *rule, *new_rule; + + if (!gtk_tree_selection_get_selected(rule_list_window.selection, + NULL, &iter)) + return; + + gtk_tree_model_get(GTK_TREE_MODEL(rule_list_window.store), &iter, + COL_FILTER_RULE, &rule, -1); + g_return_if_fail(rule != NULL); + + new_rule = prefs_filter_edit_open(rule, NULL, NULL); + gtk_window_present(GTK_WINDOW(rule_list_window.window)); + + if (new_rule) { + prefs_filter_set_list_row(&iter, new_rule, TRUE); + prefs_filter_set_list(); + } +} + +static void prefs_filter_copy_cb(void) +{ + GtkTreeIter iter; + FilterRule *rule, *new_rule; + + if (!gtk_tree_selection_get_selected(rule_list_window.selection, + NULL, &iter)) + return; + + gtk_tree_model_get(GTK_TREE_MODEL(rule_list_window.store), &iter, + COL_FILTER_RULE, &rule, -1); + g_return_if_fail(rule != NULL); + + new_rule = prefs_filter_edit_open(rule, NULL, NULL); + gtk_window_present(GTK_WINDOW(rule_list_window.window)); + + if (new_rule) { + prefs_filter_set_list_row(NULL, new_rule, TRUE); + prefs_filter_set_list(); + } +} + +static void prefs_filter_delete_cb(void) +{ + GtkTreeIter iter; + FilterRule *rule; + gchar buf[BUFFSIZE]; + gboolean valid; + + if (!gtk_tree_selection_get_selected(rule_list_window.selection, + NULL, &iter)) + return; + + gtk_tree_model_get(GTK_TREE_MODEL(rule_list_window.store), &iter, + COL_FILTER_RULE, &rule, -1); + g_return_if_fail(rule != NULL); + + g_snprintf(buf, sizeof(buf), + _("Do you really want to delete the rule '%s'?"), + rule->name ? rule->name : _("(Untitled)")); + if (alertpanel(_("Delete rule"), buf, + GTK_STOCK_YES, GTK_STOCK_NO, NULL) != G_ALERTDEFAULT) + return; + + valid = gtk_list_store_remove(rule_list_window.store, &iter); + if (valid) + gtk_tree_selection_select_iter(rule_list_window.selection, + &iter); + + prefs_common.fltlist = g_slist_remove(prefs_common.fltlist, rule); + filter_rule_free(rule); + + rule_list_window.modified = TRUE; +} + +static void prefs_filter_top(void) +{ + GtkTreeIter iter; + + if (!gtk_tree_selection_get_selected(rule_list_window.selection, + NULL, &iter)) + return; + + gtk_list_store_move_after(rule_list_window.store, &iter, NULL); +} + +static void prefs_filter_up(void) +{ + GtkTreeModel *model = GTK_TREE_MODEL(rule_list_window.store); + GtkTreeIter iter, prev; + GtkTreePath *path; + + if (!gtk_tree_selection_get_selected(rule_list_window.selection, + NULL, &iter)) + return; + + path = gtk_tree_model_get_path(model, &iter); + if (gtk_tree_path_prev(path)) { + gtk_tree_model_get_iter(model, &prev, path); + gtk_list_store_swap(rule_list_window.store, &iter, &prev); + } + gtk_tree_path_free(path); +} + +static void prefs_filter_down(void) +{ + GtkTreeIter iter, next; + + if (!gtk_tree_selection_get_selected(rule_list_window.selection, + NULL, &iter)) + return; + + next = iter; + if (gtk_tree_model_iter_next(GTK_TREE_MODEL(rule_list_window.store), + &next)) + gtk_list_store_swap(rule_list_window.store, &iter, &next); +} + +static void prefs_filter_bottom(void) +{ + GtkTreeIter iter; + + if (!gtk_tree_selection_get_selected(rule_list_window.selection, + NULL, &iter)) + return; + + gtk_list_store_move_before(rule_list_window.store, &iter, NULL); +} + +static gboolean prefs_filter_select(GtkTreeSelection *selection, + GtkTreeModel *model, GtkTreePath *path, + gboolean cur_selected, gpointer data) +{ + return TRUE; +} + +static void prefs_filter_enable_toggled(GtkCellRenderer *cell, gchar *path_str, + gpointer data) +{ + FilterRule *rule; + GtkTreeIter iter; + GtkTreePath *path; + + path = gtk_tree_path_new_from_string(path_str); + gtk_tree_model_get_iter(GTK_TREE_MODEL(rule_list_window.store), + &iter, path); + gtk_tree_path_free(path); + gtk_tree_model_get(GTK_TREE_MODEL(rule_list_window.store), &iter, + COL_FILTER_RULE, &rule, -1); + + rule->enabled ^= TRUE; + + gtk_list_store_set(rule_list_window.store, &iter, + COL_ENABLED, rule->enabled, -1); + rule_list_window.modified = TRUE; +} + +static void prefs_filter_row_activated(GtkTreeView *treeview, GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data) +{ + gtk_button_clicked(GTK_BUTTON(rule_list_window.edit_btn)); +} + +static void prefs_filter_row_reordered(GtkTreeModel *model, + GtkTreePath *path, GtkTreeIter *iter, + gpointer data, gpointer user_data) +{ + GtkTreeIter iter_; + GtkTreePath *path_; + + if (!gtk_tree_selection_get_selected(rule_list_window.selection, + NULL, &iter_)) + return; + path_ = gtk_tree_model_get_path + (GTK_TREE_MODEL(rule_list_window.store), &iter_); + gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(rule_list_window.treeview), + path_, NULL, FALSE, 0.0, 0.0); + gtk_tree_path_free(path_); + rule_list_window.modified = TRUE; +} + +static void prefs_filter_row_inserted(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer user_data) +{ + rule_list_window.modified = TRUE; +} + +static gint prefs_filter_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + prefs_filter_close(); + return TRUE; +} + +static gboolean prefs_filter_key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + if (rule_list_window.on_init) + return TRUE; + + if (event && event->keyval == GDK_Escape) + prefs_filter_close(); + return FALSE; +} + +static void prefs_filter_close(void) +{ + if (rule_list_window.on_init) + return; + + prefs_filter_set_msg_header_list(NULL); + if (rule_list_window.modified) { + prefs_filter_set_list(); + filter_write_config(); + } + gtk_widget_hide(rule_list_window.window); + gtk_list_store_clear(rule_list_window.store); + main_window_popup(main_window_get()); + inc_unlock(); +} diff --git a/src/prefs_filter.h b/src/prefs_filter.h new file mode 100644 index 0000000..0c9aded --- /dev/null +++ b/src/prefs_filter.h @@ -0,0 +1,46 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2008 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PREFS_FILTER_H__ +#define __PREFS_FILTER_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "procmsg.h" + +void prefs_filter_open (MsgInfo *msginfo, + const gchar *header, + const gchar *key); + +GSList *prefs_filter_get_header_list (void); +GSList *prefs_filter_get_user_header_list (void); + +gchar *prefs_filter_get_msg_header_field (const gchar *header_name); + +void prefs_filter_set_header_list (MsgInfo *msginfo); +void prefs_filter_set_user_header_list (GSList *list); +void prefs_filter_set_msg_header_list (MsgInfo *msginfo); + +void prefs_filter_write_user_header_list (void); + +#endif /* __PREFS_FILTER_H__ */ diff --git a/src/prefs_filter_edit.c b/src/prefs_filter_edit.c new file mode 100644 index 0000000..423924a --- /dev/null +++ b/src/prefs_filter_edit.c @@ -0,0 +1,2202 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "prefs.h" +#include "prefs_filter.h" +#include "prefs_filter_edit.h" +#include "prefs_common.h" +#include "mainwindow.h" +#include "foldersel.h" +#include "colorlabel.h" +#include "manage_window.h" +#include "procheader.h" +#include "menu.h" +#include "filter.h" +#include "utils.h" +#include "gtkutils.h" +#include "stock_pixmap.h" +#include "alertpanel.h" +#include "folder.h" +#include "plugin.h" + +static struct FilterRuleEditWindow { + GtkWidget *window; + + GtkWidget *name_entry; + GtkWidget *bool_op_optmenu; + + GtkWidget *cond_scrolled_win; + FilterCondEdit cond_edit; + + GtkWidget *action_scrolled_win; + GtkWidget *action_vbox; + GSList *action_hbox_list; + + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + + FilterRule *new_rule; + gboolean edit_finished; +} rule_edit_window; + +static struct FilterEditHeaderListDialog { + GtkWidget *window; + GtkWidget *clist; + GtkWidget *entry; + + gboolean finished; + gboolean ok; +} edit_header_list_dialog; + +static void prefs_filter_edit_create (void); +static void prefs_filter_edit_clear (void); +static void prefs_filter_edit_rule_to_dialog (FilterRule *rule, + const gchar *default_name); +static void prefs_filter_edit_update_header_list(FilterCondEdit *cond_list); + +static void prefs_filter_edit_set_action_hbox_menu_sensitive + (ActionHBox *hbox, + ActionMenuType type, + gboolean sensitive); +static void prefs_filter_edit_set_action_hbox_menus_sensitive + (void); + +static void prefs_filter_edit_get_action_hbox_menus_selection + (gboolean *selection); +static ActionMenuType prefs_filter_edit_get_action_hbox_type + (ActionHBox *hbox); + +static void prefs_filter_edit_insert_action_hbox(ActionHBox *hbox, + gint pos); +static void prefs_filter_edit_remove_cond_hbox (FilterCondEdit *cond_edit, + CondHBox *hbox); +static void prefs_filter_edit_remove_action_hbox(ActionHBox *hbox); + +static void prefs_filter_edit_add_rule_action (FilterRule *rule); + +static void prefs_filter_edit_set_cond_header_menu + (FilterCondEdit *cond_edit, + CondHBox *hbox); + +static void prefs_filter_edit_activate_cond_header + (FilterCondEdit *cond_edit, + const gchar *header); + +static void prefs_filter_edit_edit_header_list (FilterCondEdit *cond_edit); + +static FilterRule *prefs_filter_edit_dialog_to_rule (void); + +/* callback functions */ +static gint prefs_filter_edit_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean prefs_filter_edit_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); +static void prefs_filter_edit_ok (void); +static void prefs_filter_edit_cancel (void); + +static void prefs_filter_cond_activated_cb (GtkWidget *widget, + gpointer data); +static void prefs_filter_match_activated_cb (GtkWidget *widget, + gpointer data); +static void prefs_filter_action_activated_cb (GtkWidget *widget, + gpointer data); + +static void prefs_filter_action_select_dest_cb (GtkWidget *widget, + gpointer data); + +static void prefs_filter_cond_del_cb (GtkWidget *widget, + gpointer data); +static void prefs_filter_cond_add_cb (GtkWidget *widget, + gpointer data); +static void prefs_filter_action_del_cb (GtkWidget *widget, + gpointer data); +static void prefs_filter_action_add_cb (GtkWidget *widget, + gpointer data); + + +FilterRule *prefs_filter_edit_open(FilterRule *rule, const gchar *header, + const gchar *key) +{ + static gboolean lock = FALSE; + FilterRule *new_rule; + + if (lock) + return NULL; + + lock = TRUE; + + if (!rule_edit_window.window) + prefs_filter_edit_create(); + + manage_window_set_transient(GTK_WINDOW(rule_edit_window.window)); + + prefs_filter_edit_set_header_list(&rule_edit_window.cond_edit, rule); + prefs_filter_edit_rule_to_dialog(rule, key); + if (header) + prefs_filter_edit_activate_cond_header + (&rule_edit_window.cond_edit, header); + gtk_widget_show(rule_edit_window.window); + + syl_plugin_signal_emit("prefs-filter-edit-open", rule, header, key, + rule_edit_window.window); + + rule_edit_window.new_rule = NULL; + rule_edit_window.edit_finished = FALSE; + while (rule_edit_window.edit_finished == FALSE) + gtk_main_iteration(); + + gtk_widget_hide(rule_edit_window.window); + prefs_filter_edit_clear(); + prefs_filter_set_msg_header_list(NULL); + + new_rule = rule_edit_window.new_rule; + rule_edit_window.new_rule = NULL; + + if (new_rule) + debug_print("new rule created: %s\n", new_rule->name); + + lock = FALSE; + + return new_rule; +} + +static void prefs_filter_edit_create(void) +{ + GtkWidget *window; + + GtkWidget *vbox; + + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *name_entry; + + GtkWidget *bool_op_optmenu; + GtkWidget *menu; + GtkWidget *menuitem; + + GtkWidget *cond_scrolled_win; + GtkWidget *cond_vbox; + + GtkWidget *action_scrolled_win; + GtkWidget *action_vbox; + + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *confirm_area; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + gtk_widget_set_size_request(window, 632 * gtkut_get_dpi_multiplier(), 405 * gtkut_get_dpi_multiplier()); + gtk_window_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, FALSE); + gtk_widget_realize(window); + + vbox = gtk_vbox_new(FALSE, 6); + gtk_widget_show(vbox); + gtk_container_add(GTK_CONTAINER(window), vbox); + + gtkut_stock_button_set_create(&confirm_area, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_widget_show(confirm_area); + gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_btn); + + gtk_window_set_title(GTK_WINDOW(window), + _("Filter rule")); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(prefs_filter_edit_deleted), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(prefs_filter_edit_key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT (window); + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(prefs_filter_edit_ok), NULL); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(prefs_filter_edit_cancel), NULL); + + hbox = gtk_hbox_new(FALSE, 4); + gtk_widget_show(hbox); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new(_("Name:")); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + name_entry = gtk_entry_new(); + gtk_widget_show(name_entry); + gtk_box_pack_start(GTK_BOX(hbox), name_entry, TRUE, TRUE, 0); + + hbox = gtk_hbox_new(FALSE, 4); + gtk_widget_show(hbox); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + bool_op_optmenu = gtk_option_menu_new(); + gtk_widget_show(bool_op_optmenu); + gtk_box_pack_start(GTK_BOX(hbox), bool_op_optmenu, FALSE, FALSE, 0); + + menu = gtk_menu_new(); + gtk_widget_show(menu); + MENUITEM_ADD(menu, menuitem, + _("If any of the following condition matches"), FLT_OR); + MENUITEM_ADD(menu, menuitem, + _("If all of the following conditions match"), FLT_AND); + gtk_option_menu_set_menu(GTK_OPTION_MENU(bool_op_optmenu), menu); + gtk_option_menu_set_history(GTK_OPTION_MENU(bool_op_optmenu), FLT_AND); + + cond_scrolled_win = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(cond_scrolled_win); + gtk_widget_set_size_request(cond_scrolled_win, -1, 125 * gtkut_get_dpi_multiplier()); + gtk_box_pack_start(GTK_BOX(vbox), cond_scrolled_win, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(cond_scrolled_win), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + cond_vbox = gtk_vbox_new(FALSE, 2); + gtk_widget_show(cond_vbox); + gtk_container_set_border_width(GTK_CONTAINER(cond_vbox), 2); + gtk_scrolled_window_add_with_viewport + (GTK_SCROLLED_WINDOW(cond_scrolled_win), cond_vbox); + + hbox = gtk_hbox_new(FALSE, 4); + gtk_widget_show(hbox); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new(_("Perform the following actions:")); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + action_scrolled_win = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(action_scrolled_win); + gtk_box_pack_start(GTK_BOX(vbox), action_scrolled_win, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(action_scrolled_win), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + action_vbox = gtk_vbox_new(FALSE, 2); + gtk_widget_show(action_vbox); + gtk_container_set_border_width(GTK_CONTAINER(action_vbox), 2); + gtk_scrolled_window_add_with_viewport + (GTK_SCROLLED_WINDOW(action_scrolled_win), action_vbox); + + rule_edit_window.window = window; + rule_edit_window.name_entry = name_entry; + + rule_edit_window.bool_op_optmenu = bool_op_optmenu; + rule_edit_window.cond_scrolled_win = cond_scrolled_win; + rule_edit_window.cond_edit.cond_vbox = cond_vbox; + rule_edit_window.action_scrolled_win = action_scrolled_win; + rule_edit_window.action_vbox = action_vbox; + + rule_edit_window.ok_btn = ok_btn; + rule_edit_window.cancel_btn = cancel_btn; +} + +FilterCondEdit *prefs_filter_edit_cond_edit_create(void) +{ + FilterCondEdit *cond_edit; + GtkWidget *cond_vbox; + + cond_edit = g_new(FilterCondEdit, 1); + + cond_vbox = gtk_vbox_new(FALSE, 2); + gtk_widget_show(cond_vbox); + gtk_container_set_border_width(GTK_CONTAINER(cond_vbox), 2); + + cond_edit->cond_vbox = cond_vbox; + cond_edit->cond_hbox_list = NULL; + cond_edit->hdr_list = NULL; + cond_edit->rule_hdr_list = NULL; + cond_edit->add_hbox = NULL; + + return cond_edit; +} + +void prefs_filter_edit_clear_cond_edit(FilterCondEdit *cond_edit) +{ + while (cond_edit->cond_hbox_list) { + CondHBox *hbox = (CondHBox *)cond_edit->cond_hbox_list->data; + prefs_filter_edit_remove_cond_hbox(cond_edit, hbox); + } + + procheader_header_list_destroy(cond_edit->hdr_list); + cond_edit->hdr_list = NULL; + procheader_header_list_destroy(cond_edit->rule_hdr_list); + cond_edit->rule_hdr_list = NULL; +} + +static void prefs_filter_edit_clear(void) +{ + prefs_filter_edit_clear_cond_edit(&rule_edit_window.cond_edit); + + while (rule_edit_window.action_hbox_list) { + ActionHBox *hbox = + (ActionHBox *)rule_edit_window.action_hbox_list->data; + prefs_filter_edit_remove_action_hbox(hbox); + } +} + +static void prefs_filter_edit_rule_to_dialog(FilterRule *rule, + const gchar *default_name) +{ + gint index = 1; + static gint count = 1; + + if (rule && rule->name) + gtk_entry_set_text(GTK_ENTRY(rule_edit_window.name_entry), + rule->name); + else if (default_name) + gtk_entry_set_text(GTK_ENTRY(rule_edit_window.name_entry), + default_name); + else { + gchar rule_name[32]; + g_snprintf(rule_name, sizeof(rule_name), "Rule %d", count++); + gtk_entry_set_text(GTK_ENTRY(rule_edit_window.name_entry), + rule_name); + } + + if (rule) { + index = menu_find_option_menu_index + (GTK_OPTION_MENU(rule_edit_window.bool_op_optmenu), + GINT_TO_POINTER(rule->bool_op), NULL); + if (index < 0) + index = 1; + } + if (index >= 0) { + gtk_option_menu_set_history + (GTK_OPTION_MENU(rule_edit_window.bool_op_optmenu), + index); + } + + gtkut_scrolled_window_reset_position + (GTK_SCROLLED_WINDOW(rule_edit_window.cond_scrolled_win)); + gtkut_scrolled_window_reset_position + (GTK_SCROLLED_WINDOW(rule_edit_window.action_scrolled_win)); + + prefs_filter_edit_add_rule_cond(&rule_edit_window.cond_edit, rule); + prefs_filter_edit_add_rule_action(rule); +} + +void prefs_filter_edit_set_header_list(FilterCondEdit *cond_edit, + FilterRule *rule) +{ + GSList *list; + GSList *rule_hdr_list = NULL; + GSList *cur; + FilterCond *cond; + + procheader_header_list_destroy(cond_edit->hdr_list); + cond_edit->hdr_list = NULL; + procheader_header_list_destroy(cond_edit->rule_hdr_list); + cond_edit->rule_hdr_list = NULL; + + list = prefs_filter_get_header_list(); + cond_edit->hdr_list = list; + + if (!rule) + return; + + for (cur = rule->cond_list; cur != NULL; cur = cur->next) { + cond = (FilterCond *)cur->data; + + if (cond->type == FLT_COND_HEADER && + procheader_find_header_list + (rule_hdr_list, cond->header_name) < 0) + rule_hdr_list = procheader_add_header_list + (rule_hdr_list, cond->header_name, NULL); + } + + cond_edit->rule_hdr_list = rule_hdr_list; + cond_edit->hdr_list = procheader_merge_header_list_dup(list, + rule_hdr_list); + procheader_header_list_destroy(list); +} + +static void prefs_filter_edit_update_header_list(FilterCondEdit *cond_edit) +{ + GSList *list; + + procheader_header_list_destroy(cond_edit->hdr_list); + cond_edit->hdr_list = NULL; + + list = prefs_filter_get_header_list(); + cond_edit->hdr_list = procheader_merge_header_list_dup + (list, cond_edit->rule_hdr_list); + procheader_header_list_destroy(list); +} + +CondHBox *prefs_filter_edit_cond_hbox_create(FilterCondEdit *cond_edit) +{ + CondHBox *cond_hbox; + GtkWidget *hbox; + GtkWidget *cond_type_optmenu; + GtkWidget *match_type_optmenu; + GtkWidget *size_match_optmenu; + GtkWidget *age_match_optmenu; + GtkWidget *status_match_optmenu; + GtkWidget *menu; + GtkWidget *menuitem; + GtkWidget *key_entry; + GtkObject *spin_btn_adj; + GtkWidget *spin_btn; + GtkWidget *label; + GtkWidget *del_btn; + GtkWidget *add_btn; + GtkWidget *del_img; + GtkWidget *add_img; + GtkWidget *match_menu_in_addr; + GtkWidget *match_menu_not_in_addr; + + cond_hbox = g_new0(CondHBox, 1); + + hbox = gtk_hbox_new(FALSE, 4); + gtk_widget_show(hbox); + + cond_type_optmenu = gtk_option_menu_new(); + gtk_widget_show(cond_type_optmenu); + gtk_box_pack_start(GTK_BOX(hbox), cond_type_optmenu, FALSE, FALSE, 0); + +#define COND_MENUITEM_ADD(str, action) \ +{ \ + MENUITEM_ADD(menu, menuitem, str, action); \ + g_signal_connect(G_OBJECT(menuitem), "activate", \ + G_CALLBACK(prefs_filter_cond_activated_cb), \ + cond_hbox); \ +} + + menu = gtk_menu_new(); + gtk_widget_show(menu); + MENUITEM_ADD(menu, menuitem, NULL, PF_COND_SEPARATOR); + COND_MENUITEM_ADD(_("To or Cc"), PF_COND_TO_OR_CC); + COND_MENUITEM_ADD(_("Any header"), PF_COND_ANY_HEADER); + COND_MENUITEM_ADD(_("Edit header..."), PF_COND_EDIT_HEADER); + + MENUITEM_ADD(menu, menuitem, NULL, PF_COND_SEPARATOR); + COND_MENUITEM_ADD(_("Message body"), PF_COND_BODY); + COND_MENUITEM_ADD(_("Result of command"), PF_COND_CMD_TEST); + COND_MENUITEM_ADD(_("Size"), PF_COND_SIZE); + COND_MENUITEM_ADD(_("Age"), PF_COND_AGE); + + MENUITEM_ADD(menu, menuitem, NULL, PF_COND_SEPARATOR); + COND_MENUITEM_ADD(_("Unread"), PF_COND_UNREAD); + COND_MENUITEM_ADD(_("Marked"), PF_COND_MARK); + COND_MENUITEM_ADD(_("Has color label"), PF_COND_COLOR_LABEL); + COND_MENUITEM_ADD(_("Has attachment"), PF_COND_MIME); + /* COND_MENUITEM_ADD(_("Account"), PF_COND_ACCOUNT); */ + + gtk_option_menu_set_menu(GTK_OPTION_MENU(cond_type_optmenu), menu); + +#undef COND_MENUITEM_ADD + + match_type_optmenu = gtk_option_menu_new(); + gtk_widget_show(match_type_optmenu); + gtk_box_pack_start(GTK_BOX(hbox), match_type_optmenu, FALSE, FALSE, 0); + +#define MATCH_MENUITEM_ADD(str, action) \ +{ \ + MENUITEM_ADD(menu, menuitem, str, action); \ + g_signal_connect(G_OBJECT(menuitem), "activate", \ + G_CALLBACK(prefs_filter_match_activated_cb), \ + cond_hbox); \ +} + menu = gtk_menu_new(); + gtk_widget_show(menu); + MATCH_MENUITEM_ADD(_("contains"), PF_MATCH_CONTAIN); + MATCH_MENUITEM_ADD(_("doesn't contain"), PF_MATCH_NOT_CONTAIN); + MATCH_MENUITEM_ADD(_("is"), PF_MATCH_EQUAL); + MATCH_MENUITEM_ADD(_("is not"), PF_MATCH_NOT_EQUAL); +#if defined(USE_ONIGURUMA) || defined(HAVE_REGCOMP) + MATCH_MENUITEM_ADD(_("match to regex"), PF_MATCH_REGEX); + MATCH_MENUITEM_ADD(_("doesn't match to regex"), PF_MATCH_NOT_REGEX); +#endif + MATCH_MENUITEM_ADD(_("is in addressbook"), PF_MATCH_IN_ADDRESSBOOK); + match_menu_in_addr = menuitem; + MATCH_MENUITEM_ADD(_("is not in addressbook"), PF_MATCH_NOT_IN_ADDRESSBOOK); + match_menu_not_in_addr = menuitem; + gtk_option_menu_set_menu(GTK_OPTION_MENU(match_type_optmenu), menu); + +#undef MATCH_MENUITEM_ADD + + size_match_optmenu = gtk_option_menu_new(); + gtk_widget_show(size_match_optmenu); + gtk_box_pack_start(GTK_BOX(hbox), size_match_optmenu, FALSE, FALSE, 0); + + menu = gtk_menu_new(); + gtk_widget_show(menu); + MENUITEM_ADD(menu, menuitem, _("is larger than"), PF_SIZE_LARGER); + MENUITEM_ADD(menu, menuitem, _("is smaller than"), PF_SIZE_SMALLER); + gtk_option_menu_set_menu(GTK_OPTION_MENU(size_match_optmenu), menu); + + age_match_optmenu = gtk_option_menu_new(); + gtk_widget_show(age_match_optmenu); + gtk_box_pack_start(GTK_BOX(hbox), age_match_optmenu, FALSE, FALSE, 0); + + menu = gtk_menu_new(); + gtk_widget_show(menu); + MENUITEM_ADD(menu, menuitem, _("is shorter than"), PF_AGE_SHORTER); + MENUITEM_ADD(menu, menuitem, _("is longer than"), PF_AGE_LONGER); + gtk_option_menu_set_menu(GTK_OPTION_MENU(age_match_optmenu), menu); + + status_match_optmenu = gtk_option_menu_new(); + gtk_widget_show(status_match_optmenu); + gtk_box_pack_start(GTK_BOX(hbox), status_match_optmenu, + FALSE, FALSE, 0); + + menu = gtk_menu_new(); + gtk_widget_show(menu); + MENUITEM_ADD(menu, menuitem, _("matches to status"), PF_STATUS_MATCH); + MENUITEM_ADD(menu, menuitem, _("doesn't match to status"), + PF_STATUS_NOT_MATCH); + gtk_option_menu_set_menu(GTK_OPTION_MENU(status_match_optmenu), menu); + + key_entry = gtk_entry_new(); + gtk_widget_show(key_entry); + gtk_box_pack_start(GTK_BOX(hbox), key_entry, TRUE, TRUE, 0); + + spin_btn_adj = gtk_adjustment_new(0, 0, 99999, 1, 10, 0); + spin_btn = gtk_spin_button_new(GTK_ADJUSTMENT(spin_btn_adj), 1, 0); + gtk_box_pack_start(GTK_BOX(hbox), spin_btn, FALSE, FALSE, 0); + gtk_widget_set_size_request(spin_btn, 64, -1); + gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(spin_btn), TRUE); + + label = gtk_label_new(_("KB")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + del_img = gtk_image_new_from_stock + (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU); + gtk_widget_show(del_img); + + del_btn = gtk_button_new(); + gtk_container_add(GTK_CONTAINER(del_btn), del_img); + gtk_widget_show(del_btn); + gtk_box_pack_end(GTK_BOX(hbox), del_btn, FALSE, FALSE, 0); + + add_img = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU); + gtk_widget_show(add_img); + + add_btn = gtk_button_new(); + gtk_container_add(GTK_CONTAINER(add_btn), add_img); + gtk_widget_show(add_btn); + gtk_box_pack_end(GTK_BOX(hbox), add_btn, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(del_btn), "clicked", + G_CALLBACK(prefs_filter_cond_del_cb), cond_hbox); + g_signal_connect(G_OBJECT(add_btn), "clicked", + G_CALLBACK(prefs_filter_cond_add_cb), cond_hbox); + + cond_hbox->hbox = hbox; + cond_hbox->cond_type_optmenu = cond_type_optmenu; + cond_hbox->match_type_optmenu = match_type_optmenu; + cond_hbox->size_match_optmenu = size_match_optmenu; + cond_hbox->age_match_optmenu = age_match_optmenu; + cond_hbox->status_match_optmenu = status_match_optmenu; + cond_hbox->key_entry = key_entry; + cond_hbox->spin_btn = spin_btn; + cond_hbox->label = label; + cond_hbox->match_menu_in_addr = match_menu_in_addr; + cond_hbox->match_menu_not_in_addr = match_menu_not_in_addr; + cond_hbox->del_btn = del_btn; + cond_hbox->add_btn = add_btn; + cond_hbox->cur_type = PF_COND_HEADER; + cond_hbox->cur_header_name = NULL; + cond_hbox->cond_edit = cond_edit; + + prefs_filter_edit_set_cond_header_menu(cond_edit, cond_hbox); + gtk_option_menu_set_history(GTK_OPTION_MENU(cond_type_optmenu), 0); + + return cond_hbox; +} + +ActionHBox *prefs_filter_edit_action_hbox_create(void) +{ + ActionHBox *action_hbox; + GtkWidget *hbox; + GtkWidget *action_type_optmenu; + GtkWidget *menu; + GtkWidget *menuitem; + GtkWidget *label; + GtkWidget *folder_entry; + GtkWidget *cmd_entry; + GtkWidget *address_entry; + GtkWidget *folder_img; + GtkWidget *folder_sel_btn; + GtkWidget *clabel_optmenu; + GtkWidget *del_btn; + GtkWidget *add_btn; + GtkWidget *del_img; + GtkWidget *add_img; + + action_hbox = g_new0(ActionHBox, 1); + + hbox = gtk_hbox_new(FALSE, 4); + gtk_widget_show(hbox); + + action_type_optmenu = gtk_option_menu_new(); + gtk_widget_show(action_type_optmenu); + gtk_box_pack_start(GTK_BOX(hbox), action_type_optmenu, FALSE, FALSE, 0); + + memset(action_hbox->action_type_menu_items, 0, + sizeof(action_hbox->action_type_menu_items)); + +#define ACTION_MENUITEM_ADD(str, action) \ +{ \ + MENUITEM_ADD(menu, menuitem, str, action); \ + action_hbox->action_type_menu_items[action] = menuitem; \ + g_signal_connect(G_OBJECT(menuitem), "activate", \ + G_CALLBACK(prefs_filter_action_activated_cb), \ + action_hbox); \ +} + + menu = gtk_menu_new(); + gtk_widget_show(menu); + ACTION_MENUITEM_ADD(_("Move to"), PF_ACTION_MOVE); + ACTION_MENUITEM_ADD(_("Copy to"), PF_ACTION_COPY); + ACTION_MENUITEM_ADD(_("Don't receive"), PF_ACTION_NOT_RECEIVE); + ACTION_MENUITEM_ADD(_("Delete from server"), PF_ACTION_DELETE); + + MENUITEM_ADD(menu, menuitem, NULL, PF_ACTION_SEPARATOR); + ACTION_MENUITEM_ADD(_("Set mark"), PF_ACTION_MARK); + ACTION_MENUITEM_ADD(_("Set color"), PF_ACTION_COLOR_LABEL); + ACTION_MENUITEM_ADD(_("Mark as read"), PF_ACTION_MARK_READ); + +#if 0 + MENUITEM_ADD(menu, menuitem, NULL, PF_ACTION_SEPARATOR); + ACTION_MENUITEM_ADD(_("Forward"), PF_ACTION_FORWARD); + ACTION_MENUITEM_ADD(_("Forward as attachment"), PF_ACTION_FORWARD_AS_ATTACHMENT); + ACTION_MENUITEM_ADD(_("Redirect"), PF_ACTION_REDIRECT); +#endif + + MENUITEM_ADD(menu, menuitem, NULL, PF_ACTION_SEPARATOR); + ACTION_MENUITEM_ADD(_("Execute command"), PF_ACTION_EXEC); + + MENUITEM_ADD(menu, menuitem, NULL, PF_ACTION_SEPARATOR); + ACTION_MENUITEM_ADD(_("Stop rule evaluation"), PF_ACTION_STOP_EVAL); + + gtk_option_menu_set_menu(GTK_OPTION_MENU(action_type_optmenu), menu); + +#undef ACTION_MENUITEM_ADD + + label = gtk_label_new(_("folder:")); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + folder_entry = gtk_entry_new(); + gtk_widget_show(folder_entry); + gtk_box_pack_start(GTK_BOX(hbox), folder_entry, TRUE, TRUE, 0); + + folder_img = stock_pixbuf_widget(rule_edit_window.window, + STOCK_PIXMAP_FOLDER_OPEN); + gtk_widget_show(folder_img); + + folder_sel_btn = gtk_button_new(); + gtk_container_add(GTK_CONTAINER(folder_sel_btn), folder_img); + gtk_widget_show(folder_sel_btn); + gtk_box_pack_start(GTK_BOX(hbox), folder_sel_btn, FALSE, FALSE, 0); + + cmd_entry = gtk_entry_new(); + gtk_widget_show(cmd_entry); + gtk_box_pack_start(GTK_BOX(hbox), cmd_entry, TRUE, TRUE, 0); + + address_entry = gtk_entry_new(); + gtk_widget_show(address_entry); + gtk_box_pack_start(GTK_BOX(hbox), address_entry, TRUE, TRUE, 0); + + clabel_optmenu = gtk_option_menu_new(); + gtk_widget_show(clabel_optmenu); + gtk_box_pack_start(GTK_BOX(hbox), clabel_optmenu, FALSE, FALSE, 0); + + menu = colorlabel_create_color_menu(); + gtk_option_menu_set_menu(GTK_OPTION_MENU(clabel_optmenu), menu); + + del_img = gtk_image_new_from_stock + (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU); + gtk_widget_show(del_img); + + del_btn = gtk_button_new(); + gtk_container_add(GTK_CONTAINER(del_btn), del_img); + gtk_widget_show(del_btn); + gtk_box_pack_end(GTK_BOX(hbox), del_btn, FALSE, FALSE, 0); + + add_img = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU); + gtk_widget_show(add_img); + + add_btn = gtk_button_new(); + gtk_container_add(GTK_CONTAINER(add_btn), add_img); + gtk_widget_show(add_btn); + gtk_box_pack_end(GTK_BOX(hbox), add_btn, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(folder_sel_btn), "clicked", + G_CALLBACK(prefs_filter_action_select_dest_cb), + action_hbox); + g_signal_connect(G_OBJECT(del_btn), "clicked", + G_CALLBACK(prefs_filter_action_del_cb), action_hbox); + g_signal_connect(G_OBJECT(add_btn), "clicked", + G_CALLBACK(prefs_filter_action_add_cb), action_hbox); + + action_hbox->hbox = hbox; + action_hbox->action_type_optmenu = action_type_optmenu; + action_hbox->label = label; + action_hbox->folder_entry = folder_entry; + action_hbox->cmd_entry = cmd_entry; + action_hbox->address_entry = address_entry; + action_hbox->folder_sel_btn = folder_sel_btn; + action_hbox->clabel_optmenu = clabel_optmenu; + action_hbox->del_btn = del_btn; + action_hbox->add_btn = add_btn; + + return action_hbox; +} + +void prefs_filter_edit_cond_hbox_set(CondHBox *hbox, FilterCond *cond) +{ + GtkOptionMenu *cond_type_optmenu = + GTK_OPTION_MENU(hbox->cond_type_optmenu); + GtkOptionMenu *match_type_optmenu = + GTK_OPTION_MENU(hbox->match_type_optmenu); + gint cond_index = -1; + gint match_index = -1; + CondMenuType cond_type = PF_COND_NONE; + MatchMenuType match_type = PF_MATCH_NONE; + SizeMatchType size_type = PF_SIZE_LARGER; + AgeMatchType age_type = PF_AGE_SHORTER; + StatusMatchType status_type = PF_STATUS_MATCH; + + switch (cond->type) { + case FLT_COND_HEADER: + cond_type = PF_COND_HEADER; break; + case FLT_COND_TO_OR_CC: + cond_type = PF_COND_TO_OR_CC; break; + case FLT_COND_ANY_HEADER: + cond_type = PF_COND_ANY_HEADER; break; + case FLT_COND_BODY: + cond_type = PF_COND_BODY; break; + case FLT_COND_CMD_TEST: + cond_type = PF_COND_CMD_TEST; break; + case FLT_COND_SIZE_GREATER: + cond_type = PF_COND_SIZE; + if (FLT_IS_NOT_MATCH(cond->match_flag)) + size_type = PF_SIZE_SMALLER; + else + size_type = PF_SIZE_LARGER; + break; + case FLT_COND_AGE_GREATER: + cond_type = PF_COND_AGE; + if (FLT_IS_NOT_MATCH(cond->match_flag)) + age_type = PF_AGE_SHORTER; + else + age_type = PF_AGE_LONGER; + break; + case FLT_COND_UNREAD: + cond_type = PF_COND_UNREAD; break; + case FLT_COND_MARK: + cond_type = PF_COND_MARK; break; + case FLT_COND_COLOR_LABEL: + cond_type = PF_COND_COLOR_LABEL; break; + case FLT_COND_MIME: + cond_type = PF_COND_MIME; break; + case FLT_COND_ACCOUNT: + cond_type = PF_COND_ACCOUNT; break; + default: + break; + } + + switch (cond->type) { + case FLT_COND_HEADER: + case FLT_COND_TO_OR_CC: + case FLT_COND_ANY_HEADER: + case FLT_COND_BODY: + switch (cond->match_type) { + case FLT_CONTAIN: + if (FLT_IS_NOT_MATCH(cond->match_flag)) + match_type = PF_MATCH_NOT_CONTAIN; + else + match_type = PF_MATCH_CONTAIN; + break; + case FLT_EQUAL: + if (FLT_IS_NOT_MATCH(cond->match_flag)) + match_type = PF_MATCH_NOT_EQUAL; + else + match_type = PF_MATCH_EQUAL; + break; + case FLT_REGEX: + if (FLT_IS_NOT_MATCH(cond->match_flag)) + match_type = PF_MATCH_NOT_REGEX; + else + match_type = PF_MATCH_REGEX; + break; + case FLT_IN_ADDRESSBOOK: + if (FLT_IS_NOT_MATCH(cond->match_flag)) + match_type = PF_MATCH_NOT_IN_ADDRESSBOOK; + else + match_type = PF_MATCH_IN_ADDRESSBOOK; + break; + } + break; + case FLT_COND_UNREAD: + case FLT_COND_MARK: + case FLT_COND_COLOR_LABEL: + case FLT_COND_MIME: + if (FLT_IS_NOT_MATCH(cond->match_flag)) + status_type = PF_STATUS_NOT_MATCH; + else + status_type = PF_STATUS_MATCH; + break; + default: + break; + } + + if (cond_type == PF_COND_HEADER) + cond_index = procheader_find_header_list + (hbox->cond_edit->hdr_list, cond->header_name); + else + cond_index = menu_find_option_menu_index + (cond_type_optmenu, GINT_TO_POINTER(cond_type), NULL); + if (cond_index >= 0) { + GtkWidget *menuitem; + + if (cond_type == PF_COND_SIZE || cond_type == PF_COND_AGE) { + gtk_spin_button_set_value + (GTK_SPIN_BUTTON(hbox->spin_btn), + (gfloat)cond->int_value); + } else { + gtk_entry_set_text(GTK_ENTRY(hbox->key_entry), + cond->str_value ? + cond->str_value : ""); + } + gtk_option_menu_set_history(cond_type_optmenu, cond_index); + menuitem = gtk_menu_get_active + (GTK_MENU(gtk_option_menu_get_menu(cond_type_optmenu))); + gtk_menu_item_activate(GTK_MENU_ITEM(menuitem)); + } + + match_index = menu_find_option_menu_index + (match_type_optmenu, GINT_TO_POINTER(match_type), NULL); + if (match_index >= 0) + gtk_option_menu_set_history(match_type_optmenu, match_index); + if (cond_type == PF_COND_SIZE) + gtk_option_menu_set_history + (GTK_OPTION_MENU(hbox->size_match_optmenu), size_type); + else if (cond_type == PF_COND_AGE) + gtk_option_menu_set_history + (GTK_OPTION_MENU(hbox->age_match_optmenu), age_type); + else if (cond_type == PF_COND_UNREAD || cond_type == PF_COND_MARK || + cond_type == PF_COND_COLOR_LABEL || cond_type == PF_COND_MIME) + gtk_option_menu_set_history + (GTK_OPTION_MENU(hbox->status_match_optmenu), + status_type); + + if (match_type == PF_MATCH_IN_ADDRESSBOOK || + match_type == PF_MATCH_NOT_IN_ADDRESSBOOK) + gtk_widget_hide(hbox->key_entry); +} + +void prefs_filter_edit_action_hbox_set(ActionHBox *hbox, FilterAction *action) +{ + GtkOptionMenu *type_optmenu = GTK_OPTION_MENU(hbox->action_type_optmenu); + GtkWidget *menu; + ActionMenuType type = PF_ACTION_NONE; + + menu = gtk_option_menu_get_menu(type_optmenu); + + switch (action->type) { + case FLT_ACTION_MOVE: + type = PF_ACTION_MOVE; break; + case FLT_ACTION_COPY: + type = PF_ACTION_COPY; break; + case FLT_ACTION_NOT_RECEIVE: + type = PF_ACTION_NOT_RECEIVE; break; + case FLT_ACTION_DELETE: + type = PF_ACTION_DELETE; break; + case FLT_ACTION_EXEC: + type = PF_ACTION_EXEC; break; + case FLT_ACTION_MARK: + type = PF_ACTION_MARK; break; + case FLT_ACTION_COLOR_LABEL: + type = PF_ACTION_COLOR_LABEL; break; + case FLT_ACTION_MARK_READ: + type = PF_ACTION_MARK_READ; break; + case FLT_ACTION_STOP_EVAL: + type = PF_ACTION_STOP_EVAL; break; + default: + break; + } + + switch (type) { + case PF_ACTION_MOVE: + case PF_ACTION_COPY: + gtk_entry_set_text(GTK_ENTRY(hbox->folder_entry), + action->str_value ? action->str_value : ""); + break; + case PF_ACTION_EXEC: + gtk_entry_set_text(GTK_ENTRY(hbox->cmd_entry), + action->str_value ? action->str_value : ""); + break; + case PF_ACTION_COLOR_LABEL: + gtk_option_menu_set_history + (GTK_OPTION_MENU(hbox->clabel_optmenu), + action->int_value - 1); + break; + default: + break; + } + + prefs_filter_edit_set_action_hbox_widgets(hbox, type); +} + +void prefs_filter_edit_cond_hbox_select(CondHBox *hbox, CondMenuType type, + const gchar *header_name) +{ + gint index; + GtkOptionMenu *cond_type_optmenu = + GTK_OPTION_MENU(hbox->cond_type_optmenu); + GtkWidget *menu; + + if (type == PF_COND_HEADER) { + if (header_name) + index = procheader_find_header_list + (hbox->cond_edit->hdr_list, header_name); + else + index = 0; + } else + index = menu_find_option_menu_index + (cond_type_optmenu, GINT_TO_POINTER(type), NULL); + + if (index < 0) + index = 0; + + menu = gtk_option_menu_get_menu(cond_type_optmenu); + gtk_option_menu_set_history(cond_type_optmenu, index); +} + +void prefs_filter_edit_set_cond_hbox_widgets(CondHBox *hbox, CondMenuType type) +{ + MatchMenuType match_type; + + switch (type) { + case PF_COND_HEADER: + case PF_COND_TO_OR_CC: + case PF_COND_ANY_HEADER: + case PF_COND_BODY: + gtk_widget_show(hbox->match_type_optmenu); + gtk_widget_hide(hbox->size_match_optmenu); + gtk_widget_hide(hbox->age_match_optmenu); + gtk_widget_hide(hbox->status_match_optmenu); + match_type = menu_get_option_menu_active_index + (GTK_OPTION_MENU(hbox->match_type_optmenu)); + if (match_type == PF_MATCH_IN_ADDRESSBOOK || + match_type == PF_MATCH_NOT_IN_ADDRESSBOOK) + gtk_widget_hide(hbox->key_entry); + else + gtk_widget_show(hbox->key_entry); + gtk_widget_hide(hbox->spin_btn); + gtk_widget_hide(hbox->label); + if (type == PF_COND_HEADER || type == PF_COND_TO_OR_CC) { + gtk_widget_show(hbox->match_menu_in_addr); + gtk_widget_show(hbox->match_menu_not_in_addr); + } else { + gtk_widget_hide(hbox->match_menu_in_addr); + gtk_widget_hide(hbox->match_menu_not_in_addr); + if (match_type == PF_MATCH_IN_ADDRESSBOOK || + match_type == PF_MATCH_NOT_IN_ADDRESSBOOK) { + gtk_option_menu_set_history(GTK_OPTION_MENU(hbox->match_type_optmenu), 0); + gtk_widget_show(hbox->key_entry); + } + } + break; + case PF_COND_CMD_TEST: + gtk_widget_hide(hbox->match_type_optmenu); + gtk_widget_hide(hbox->size_match_optmenu); + gtk_widget_hide(hbox->age_match_optmenu); + gtk_widget_hide(hbox->status_match_optmenu); + gtk_widget_show(hbox->key_entry); + gtk_widget_hide(hbox->spin_btn); + gtk_widget_hide(hbox->label); + break; + case PF_COND_SIZE: + gtk_widget_hide(hbox->match_type_optmenu); + gtk_widget_show(hbox->size_match_optmenu); + gtk_widget_hide(hbox->age_match_optmenu); + gtk_widget_hide(hbox->status_match_optmenu); + gtk_widget_hide(hbox->key_entry); + gtk_widget_show(hbox->spin_btn); + gtk_widget_show(hbox->label); + gtk_label_set_text(GTK_LABEL(hbox->label), _("KB")); + break; + case PF_COND_AGE: + gtk_widget_hide(hbox->match_type_optmenu); + gtk_widget_hide(hbox->size_match_optmenu); + gtk_widget_show(hbox->age_match_optmenu); + gtk_widget_hide(hbox->status_match_optmenu); + gtk_widget_hide(hbox->key_entry); + gtk_widget_show(hbox->spin_btn); + gtk_widget_show(hbox->label); + gtk_label_set_text(GTK_LABEL(hbox->label), _("day(s)")); + break; + case PF_COND_UNREAD: + case PF_COND_MARK: + case PF_COND_COLOR_LABEL: + case PF_COND_MIME: + gtk_widget_hide(hbox->match_type_optmenu); + gtk_widget_hide(hbox->size_match_optmenu); + gtk_widget_hide(hbox->age_match_optmenu); + gtk_widget_show(hbox->status_match_optmenu); + gtk_widget_hide(hbox->key_entry); + gtk_widget_hide(hbox->spin_btn); + gtk_widget_hide(hbox->label); + break; + case PF_COND_ACCOUNT: + gtk_widget_hide(hbox->match_type_optmenu); + gtk_widget_hide(hbox->size_match_optmenu); + gtk_widget_hide(hbox->age_match_optmenu); + gtk_widget_hide(hbox->status_match_optmenu); + gtk_widget_hide(hbox->key_entry); + /* gtk_widget_show(hbox->account_optmenu); */ + gtk_widget_hide(hbox->spin_btn); + gtk_widget_hide(hbox->label); + break; + default: + break; + } +} + +void prefs_filter_edit_set_action_hbox_widgets(ActionHBox *hbox, + ActionMenuType type) +{ + GtkOptionMenu *type_optmenu = GTK_OPTION_MENU(hbox->action_type_optmenu); + gint index; + + switch (type) { + case PF_ACTION_MOVE: + case PF_ACTION_COPY: + gtk_widget_show(hbox->label); + gtk_label_set_text(GTK_LABEL(hbox->label), _("folder:")); + gtk_widget_show(hbox->folder_entry); + gtk_widget_show(hbox->folder_sel_btn); + gtk_widget_hide(hbox->cmd_entry); + gtk_widget_hide(hbox->address_entry); + gtk_widget_hide(hbox->clabel_optmenu); + break; + case PF_ACTION_NOT_RECEIVE: + case PF_ACTION_DELETE: + case PF_ACTION_MARK: + case PF_ACTION_MARK_READ: + case PF_ACTION_STOP_EVAL: + gtk_widget_hide(hbox->label); + gtk_widget_hide(hbox->folder_entry); + gtk_widget_hide(hbox->folder_sel_btn); + gtk_widget_hide(hbox->cmd_entry); + gtk_widget_hide(hbox->address_entry); + gtk_widget_hide(hbox->clabel_optmenu); + break; + case PF_ACTION_EXEC: + case PF_ACTION_EXEC_ASYNC: + gtk_widget_hide(hbox->label); + gtk_widget_hide(hbox->folder_entry); + gtk_widget_hide(hbox->folder_sel_btn); + gtk_widget_show(hbox->cmd_entry); + gtk_widget_hide(hbox->address_entry); + gtk_widget_hide(hbox->clabel_optmenu); + break; + case PF_ACTION_COLOR_LABEL: + gtk_widget_hide(hbox->label); + gtk_widget_hide(hbox->folder_entry); + gtk_widget_hide(hbox->folder_sel_btn); + gtk_widget_hide(hbox->cmd_entry); + gtk_widget_hide(hbox->address_entry); + gtk_widget_show(hbox->clabel_optmenu); + break; + case PF_ACTION_FORWARD: + case PF_ACTION_FORWARD_AS_ATTACHMENT: + case PF_ACTION_REDIRECT: + gtk_widget_show(hbox->label); + gtk_label_set_text(GTK_LABEL(hbox->label), _("address:")); + gtk_widget_hide(hbox->folder_entry); + gtk_widget_hide(hbox->folder_sel_btn); + gtk_widget_hide(hbox->cmd_entry); + gtk_widget_show(hbox->address_entry); + gtk_widget_hide(hbox->clabel_optmenu); + break; + default: + break; + } + + index = menu_find_option_menu_index(type_optmenu, GINT_TO_POINTER(type), + NULL); + gtk_option_menu_set_history(type_optmenu, index); + prefs_filter_edit_set_action_hbox_menus_sensitive(); +} + +static void prefs_filter_edit_set_action_hbox_menu_sensitive + (ActionHBox *hbox, ActionMenuType type, gboolean sensitive) +{ + GtkWidget *menuitem; + + menuitem = hbox->action_type_menu_items[type]; + if (menuitem) + gtk_widget_set_sensitive(menuitem, sensitive); +} + +static void prefs_filter_edit_set_action_hbox_menus_sensitive(void) +{ + GSList *cur; + ActionHBox *cur_hbox; + ActionMenuType menu_type; + ActionMenuType cur_type; + gboolean action_menu_selection[PF_ACTION_NONE]; + gboolean action_menu_sensitive[PF_ACTION_NONE]; + + prefs_filter_edit_get_action_hbox_menus_selection + (action_menu_selection); + + for (cur = rule_edit_window.action_hbox_list; cur != NULL; + cur = cur->next) { + cur_hbox = (ActionHBox *)cur->data; + menu_type = prefs_filter_edit_get_action_hbox_type(cur_hbox); + for (cur_type = PF_ACTION_MOVE; cur_type < PF_ACTION_NONE; + cur_type++) + action_menu_sensitive[cur_type] = TRUE; + + for (cur_type = PF_ACTION_MOVE; cur_type < PF_ACTION_NONE; + cur_type++) { + switch (cur_type) { + case PF_ACTION_MOVE: + case PF_ACTION_NOT_RECEIVE: + case PF_ACTION_DELETE: + if (action_menu_selection[cur_type] == TRUE && + menu_type != cur_type) { + action_menu_sensitive[PF_ACTION_MOVE] = FALSE; + action_menu_sensitive[PF_ACTION_NOT_RECEIVE] = FALSE; + action_menu_sensitive[PF_ACTION_DELETE] = FALSE; + } + break; + case PF_ACTION_MARK: + case PF_ACTION_COLOR_LABEL: + case PF_ACTION_MARK_READ: + case PF_ACTION_STOP_EVAL: + if (action_menu_selection[cur_type] == TRUE && + menu_type != cur_type) + action_menu_sensitive[cur_type] = FALSE; + break; + default: + break; + } + } + + for (cur_type = PF_ACTION_MOVE; cur_type < PF_ACTION_NONE; + cur_type++) { + prefs_filter_edit_set_action_hbox_menu_sensitive + (cur_hbox, cur_type, + action_menu_sensitive[cur_type]); + } + } +} + +static void prefs_filter_edit_get_action_hbox_menus_selection(gboolean *selection) +{ + GSList *cur; + ActionHBox *cur_hbox; + ActionMenuType menu_type; + ActionMenuType cur_type; + + for (cur_type = PF_ACTION_MOVE; cur_type < PF_ACTION_NONE; cur_type++) + selection[cur_type] = FALSE; + + for (cur = rule_edit_window.action_hbox_list; cur != NULL; + cur = cur->next) { + cur_hbox = (ActionHBox *)cur->data; + menu_type = prefs_filter_edit_get_action_hbox_type(cur_hbox); + if (menu_type >= PF_ACTION_MOVE && menu_type < PF_ACTION_NONE) + selection[menu_type] = TRUE; + } +} + +static ActionMenuType prefs_filter_edit_get_action_hbox_type(ActionHBox *hbox) +{ + GtkWidget *menuitem; + ActionMenuType type; + + g_return_val_if_fail(hbox != NULL, PF_ACTION_NONE); + + menuitem = gtk_menu_get_active + (GTK_MENU(gtk_option_menu_get_menu + (GTK_OPTION_MENU(hbox->action_type_optmenu)))); + type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), + MENU_VAL_ID)); + + return type; +} + +void prefs_filter_edit_insert_cond_hbox(FilterCondEdit *cond_edit, + CondHBox *hbox, gint pos) +{ + g_return_if_fail(cond_edit != NULL); + g_return_if_fail(hbox != NULL); + + if (!cond_edit->cond_hbox_list) { + gtk_widget_set_sensitive(hbox->del_btn, FALSE); + } else if (cond_edit->cond_hbox_list && + !cond_edit->cond_hbox_list->next) { + CondHBox *top_hbox = + (CondHBox *)cond_edit->cond_hbox_list->data; + gtk_widget_set_sensitive(top_hbox->del_btn, TRUE); + } + + gtk_box_pack_start(GTK_BOX(cond_edit->cond_vbox), + hbox->hbox, FALSE, FALSE, 0); + if (pos >= 0) { + gtk_box_reorder_child(GTK_BOX(cond_edit->cond_vbox), + hbox->hbox, pos); + } + + cond_edit->cond_hbox_list = + g_slist_insert(cond_edit->cond_hbox_list, hbox, pos); +} + +static void prefs_filter_edit_insert_action_hbox(ActionHBox *hbox, gint pos) +{ + g_return_if_fail(hbox != NULL); + + if (!rule_edit_window.action_hbox_list) { + gtk_widget_set_sensitive(hbox->del_btn, FALSE); + } else if (rule_edit_window.action_hbox_list && + !rule_edit_window.action_hbox_list->next) { + ActionHBox *top_hbox = + (ActionHBox *)rule_edit_window.action_hbox_list->data; + gtk_widget_set_sensitive(top_hbox->del_btn, TRUE); + } + + gtk_box_pack_start(GTK_BOX(rule_edit_window.action_vbox), + hbox->hbox, FALSE, FALSE, 0); + if (pos >= 0) { + gtk_box_reorder_child(GTK_BOX(rule_edit_window.action_vbox), + hbox->hbox, pos); + } + + rule_edit_window.action_hbox_list = + g_slist_insert(rule_edit_window.action_hbox_list, hbox, pos); +} + +static void prefs_filter_edit_remove_cond_hbox(FilterCondEdit *cond_edit, + CondHBox *hbox) +{ + g_return_if_fail(cond_edit != NULL); + g_return_if_fail(hbox != NULL); + g_return_if_fail(cond_edit->cond_hbox_list != NULL); + + cond_edit->cond_hbox_list = + g_slist_remove(cond_edit->cond_hbox_list, hbox); + gtk_widget_destroy(hbox->hbox); + g_free(hbox); + + if (cond_edit->cond_hbox_list && !cond_edit->cond_hbox_list->next) { + hbox = (CondHBox *)cond_edit->cond_hbox_list->data; + gtk_widget_set_sensitive(hbox->del_btn, FALSE); + } +} + +static void prefs_filter_edit_remove_action_hbox(ActionHBox *hbox) +{ + g_return_if_fail(hbox != NULL); + g_return_if_fail(rule_edit_window.action_hbox_list != NULL); + + rule_edit_window.action_hbox_list = + g_slist_remove(rule_edit_window.action_hbox_list, hbox); + gtk_widget_destroy(hbox->hbox); + g_free(hbox); + + prefs_filter_edit_set_action_hbox_menus_sensitive(); + + if (rule_edit_window.action_hbox_list && + !rule_edit_window.action_hbox_list->next) { + hbox = (ActionHBox *)rule_edit_window.action_hbox_list->data; + gtk_widget_set_sensitive(hbox->del_btn, FALSE); + } +} + +void prefs_filter_edit_add_rule_cond(FilterCondEdit *cond_edit, + FilterRule *rule) +{ + CondHBox *hbox; + GSList *cur; + FilterCond *cond; + + if (!rule || !rule->cond_list) { + hbox = prefs_filter_edit_cond_hbox_create(cond_edit); + prefs_filter_edit_set_cond_hbox_widgets(hbox, PF_COND_HEADER); + prefs_filter_edit_insert_cond_hbox(cond_edit, hbox, -1); + if (cond_edit->add_hbox) + cond_edit->add_hbox(hbox); + return; + } + + for (cur = rule->cond_list; cur != NULL; cur = cur->next) { + cond = (FilterCond *)cur->data; + + hbox = prefs_filter_edit_cond_hbox_create(cond_edit); + prefs_filter_edit_cond_hbox_set(hbox, cond); + prefs_filter_edit_insert_cond_hbox(cond_edit, hbox, -1); + if (cond_edit->add_hbox) + cond_edit->add_hbox(hbox); + } +} + +static void prefs_filter_edit_add_rule_action(FilterRule *rule) +{ + ActionHBox *hbox; + GSList *cur; + + if (!rule || !rule->action_list) { + hbox = prefs_filter_edit_action_hbox_create(); + prefs_filter_edit_insert_action_hbox(hbox, -1); + prefs_filter_edit_set_action_hbox_widgets(hbox, PF_ACTION_MOVE); + return; + } + + for (cur = rule->action_list; cur != NULL; cur = cur->next) { + FilterAction *action = (FilterAction *)cur->data; + + hbox = prefs_filter_edit_action_hbox_create(); + prefs_filter_edit_insert_action_hbox(hbox, -1); + prefs_filter_edit_action_hbox_set(hbox, action); + } +} + +static void prefs_filter_edit_set_cond_header_menu(FilterCondEdit *cond_edit, + CondHBox *hbox) +{ + GSList *cur; + GtkWidget *menu; + GtkWidget *menuitem; + gint pos = 0; + GList *child; + + menu = gtk_option_menu_get_menu + (GTK_OPTION_MENU(hbox->cond_type_optmenu)); + + /* destroy header items */ + child = GTK_MENU_SHELL(menu)->children; + while (child != NULL) { + GList *next = child->next; + menuitem = GTK_WIDGET(child->data); + if (!g_object_get_data(G_OBJECT(menuitem), "header_str")) + break; + gtk_widget_destroy(menuitem); + child = next; + } + + for (cur = cond_edit->hdr_list; cur != NULL; cur = cur->next, pos++) { + Header *header = (Header *)cur->data; + + menuitem = gtk_menu_item_new_with_label(header->name); + gtk_widget_show(menuitem); + gtk_menu_insert(GTK_MENU(menu), menuitem, pos); + g_object_set_data(G_OBJECT(menuitem), MENU_VAL_ID, + GINT_TO_POINTER(PF_COND_HEADER)); + g_object_set_data(G_OBJECT(menuitem), "header_str", + header->name); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(prefs_filter_cond_activated_cb), + hbox); + } + + if (hbox->cur_type == PF_COND_HEADER) + prefs_filter_edit_cond_hbox_select + (hbox, hbox->cur_type, hbox->cur_header_name); +} + +static void prefs_filter_edit_activate_cond_header(FilterCondEdit *cond_edit, + const gchar *header) +{ + gint index; + CondHBox *hbox; + GtkWidget *menu; + GtkWidget *menuitem; + GList *cur; + gchar *menu_header; + + g_return_if_fail(header != NULL); + g_return_if_fail(cond_edit != NULL); + g_return_if_fail(cond_edit->cond_hbox_list != NULL); + + hbox = (CondHBox *)cond_edit->cond_hbox_list->data; + menu = gtk_option_menu_get_menu + (GTK_OPTION_MENU(hbox->cond_type_optmenu)); + + for (cur = GTK_MENU_SHELL(menu)->children, index = 0; + cur != NULL; cur = cur->next, index++) { + menuitem = GTK_WIDGET(cur->data); + menu_header = g_object_get_data(G_OBJECT(menuitem), + "header_str"); + if (!menu_header) + break; + if (!g_ascii_strcasecmp(menu_header, header)) { + gtk_option_menu_set_history + (GTK_OPTION_MENU(hbox->cond_type_optmenu), + index); + gtk_menu_item_activate(GTK_MENU_ITEM(menuitem)); + break; + } + } +} + +static gint edit_header_list_dialog_deleted(GtkWidget *widget, + GdkEventAny *event, gpointer data) +{ + edit_header_list_dialog.finished = TRUE; + return TRUE; +} + +static gboolean edit_header_list_dialog_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + edit_header_list_dialog.finished = TRUE; + return FALSE; +} + +static void edit_header_list_dialog_add(void) +{ + GtkCList *clist = GTK_CLIST(edit_header_list_dialog.clist); + const gchar *text; + gchar *ctext[1]; + gint row; + gchar *row_text; + + text = gtk_entry_get_text(GTK_ENTRY(edit_header_list_dialog.entry)); + if (text[0] == '\0') return; + + for (row = 0; gtk_clist_get_text(clist, row, 0, &row_text) != 0; + row++) { + if (g_ascii_strcasecmp(row_text, text) == 0) return; + } + + ctext[0] = (gchar *)text; + gtk_clist_append(clist, ctext); +} + +static void edit_header_list_dialog_delete(void) +{ + GtkCList *clist = GTK_CLIST(edit_header_list_dialog.clist); + gint row; + + if (!clist->selection) return; + + row = GPOINTER_TO_INT(clist->selection->data); + gtk_clist_remove(clist, row); +} + +static void edit_header_list_dialog_ok(void) +{ + edit_header_list_dialog.finished = TRUE; + edit_header_list_dialog.ok = TRUE; +} + +static void edit_header_list_dialog_cancel(void) +{ + edit_header_list_dialog.finished = TRUE; +} + +static void prefs_filter_edit_edit_header_list_dialog_create(void) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *hbox; + + GtkWidget *vbox2; + GtkWidget *scrwin; + GtkWidget *clist; + + GtkWidget *entry_hbox; + GtkWidget *label; + GtkWidget *entry; + + GtkWidget *btn_vbox; + GtkWidget *add_btn; + GtkWidget *del_btn; + + GtkWidget *confirm_area; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + + gchar *title[1]; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + gtk_window_position(GTK_WINDOW(window),GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, FALSE); + gtk_window_set_title(GTK_WINDOW(window), _("Edit header list")); + + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(edit_header_list_dialog_deleted), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(edit_header_list_dialog_key_pressed), NULL); + + vbox = gtk_vbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(window), vbox); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); + + vbox2 = gtk_vbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0); + + scrwin = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_set_size_request(scrwin, 140 * gtkut_get_dpi_multiplier(), 180 * gtkut_get_dpi_multiplier()); + gtk_box_pack_start(GTK_BOX(vbox2), scrwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + title[0] = _("Headers"); + clist = gtk_clist_new_with_titles(1, title); + gtk_container_add(GTK_CONTAINER(scrwin), clist); + gtk_clist_set_column_width(GTK_CLIST(clist), 0, 80); + gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_BROWSE); + gtkut_clist_set_redraw(GTK_CLIST(clist)); + GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist)->column[0].button, + GTK_CAN_FOCUS); + + entry_hbox = gtk_hbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(vbox), entry_hbox, FALSE, TRUE, 0); + + label = gtk_label_new(_("Header:")); + gtk_box_pack_start(GTK_BOX(entry_hbox), label, FALSE, FALSE, 0); + + entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(entry_hbox), entry, TRUE, TRUE, 0); + + btn_vbox = gtk_vbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(hbox), btn_vbox, FALSE, FALSE, 0); + + add_btn = gtk_button_new_with_label(_("Add")); + gtk_box_pack_start(GTK_BOX(btn_vbox), add_btn, FALSE, FALSE, 0); + + del_btn = gtk_button_new_with_label(_(" Delete ")); + gtk_box_pack_start(GTK_BOX(btn_vbox), del_btn, FALSE, FALSE, 0); + + gtkut_stock_button_set_create(&confirm_area, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_btn); + + g_signal_connect(G_OBJECT(add_btn), "clicked", + G_CALLBACK(edit_header_list_dialog_add), NULL); + g_signal_connect(G_OBJECT(del_btn), "clicked", + G_CALLBACK(edit_header_list_dialog_delete), NULL); + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(edit_header_list_dialog_ok), NULL); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(edit_header_list_dialog_cancel), NULL); + + manage_window_set_transient(GTK_WINDOW(window)); + + gtk_widget_show_all(window); + + edit_header_list_dialog.window = window; + edit_header_list_dialog.clist = clist; + edit_header_list_dialog.entry = entry; + edit_header_list_dialog.finished = FALSE; + edit_header_list_dialog.ok = FALSE; +} + +static void prefs_filter_edit_edit_header_list_dialog_set(void) +{ + GtkCList *clist = GTK_CLIST(edit_header_list_dialog.clist); + GSList *list; + GSList *cur; + gchar *text[1]; + + gtk_clist_freeze(clist); + + list = prefs_filter_get_user_header_list(); + for (cur = list; cur != NULL; cur = cur->next) { + Header *header = (Header *)cur->data; + text[0] = header->name; + gtk_clist_append(clist, text); + } + + gtk_clist_thaw(clist); +} + +static GSList *prefs_filter_edit_edit_header_list_dialog_get(void) +{ + GtkCList *clist = GTK_CLIST(edit_header_list_dialog.clist); + gint row; + gchar *text; + GSList *list = NULL; + + for (row = 0; gtk_clist_get_text(clist, row, 0, &text) != 0; row++) + list = procheader_add_header_list(list, text, NULL); + + return list; +} + +static void prefs_filter_edit_edit_header_list(FilterCondEdit *cond_edit) +{ + GSList *list; + GSList *cur; + + prefs_filter_edit_edit_header_list_dialog_create(); + prefs_filter_edit_edit_header_list_dialog_set(); + + while (edit_header_list_dialog.finished == FALSE) + gtk_main_iteration(); + + if (edit_header_list_dialog.ok == TRUE) { + list = prefs_filter_edit_edit_header_list_dialog_get(); + prefs_filter_set_user_header_list(list); + prefs_filter_edit_update_header_list(cond_edit); + for (cur = cond_edit->cond_hbox_list; cur != NULL; + cur = cur->next) { + CondHBox *hbox = (CondHBox *)cur->data; + prefs_filter_edit_set_cond_header_menu(cond_edit, hbox); + } + prefs_filter_write_user_header_list(); + } + + gtk_widget_destroy(edit_header_list_dialog.window); + edit_header_list_dialog.window = NULL; + edit_header_list_dialog.clist = NULL; + edit_header_list_dialog.entry = NULL; + edit_header_list_dialog.finished = FALSE; + edit_header_list_dialog.ok = FALSE; +} + +FilterCond *prefs_filter_edit_cond_hbox_to_cond(CondHBox *hbox, + gboolean case_sens, + gchar **error_msg) +{ + FilterCond *cond = NULL; + GtkWidget *cond_type_menuitem; + CondMenuType cond_menu_type; + MatchMenuType match_menu_type; + const gchar *header_name; + const gchar *key_str; + gint int_value; + FilterMatchType match_type = FLT_CONTAIN; + FilterMatchFlag match_flag = 0; + SizeMatchType size_type; + AgeMatchType age_type; + StatusMatchType status_type; + gchar *error_msg_ = NULL; + + cond_type_menuitem = gtk_menu_get_active + (GTK_MENU(gtk_option_menu_get_menu + (GTK_OPTION_MENU(hbox->cond_type_optmenu)))); + cond_menu_type = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(cond_type_menuitem), MENU_VAL_ID)); + + match_menu_type = menu_get_option_menu_active_index + (GTK_OPTION_MENU(hbox->match_type_optmenu)); + + key_str = gtk_entry_get_text(GTK_ENTRY(hbox->key_entry)); + + switch (match_menu_type) { + case PF_MATCH_CONTAIN: + match_type = FLT_CONTAIN; + break; + case PF_MATCH_NOT_CONTAIN: + match_type = FLT_CONTAIN; + match_flag |= FLT_NOT_MATCH; + break; + case PF_MATCH_EQUAL: + match_type = FLT_EQUAL; + break; + case PF_MATCH_NOT_EQUAL: + match_type = FLT_EQUAL; + match_flag |= FLT_NOT_MATCH; + break; + case PF_MATCH_REGEX: + match_type = FLT_REGEX; + break; + case PF_MATCH_NOT_REGEX: + match_type = FLT_REGEX; + match_flag |= FLT_NOT_MATCH; + break; + case PF_MATCH_IN_ADDRESSBOOK: + match_type = FLT_IN_ADDRESSBOOK; + break; + case PF_MATCH_NOT_IN_ADDRESSBOOK: + match_type = FLT_IN_ADDRESSBOOK; + match_flag |= FLT_NOT_MATCH; + break; + default: + break; + } + + if (case_sens) + match_flag |= FLT_CASE_SENS; + + switch (cond_menu_type) { + case PF_COND_HEADER: + header_name = g_object_get_data + (G_OBJECT(cond_type_menuitem), "header_str"); + cond = filter_cond_new(FLT_COND_HEADER, + match_type, match_flag, + header_name, key_str); + break; + case PF_COND_TO_OR_CC: + cond = filter_cond_new(FLT_COND_TO_OR_CC, match_type, + match_flag, NULL, key_str); + break; + case PF_COND_ANY_HEADER: + cond = filter_cond_new(FLT_COND_ANY_HEADER, match_type, + match_flag, NULL, key_str); + break; + case PF_COND_BODY: + cond = filter_cond_new(FLT_COND_BODY, match_type, + match_flag, NULL, key_str); + break; + case PF_COND_CMD_TEST: + if (key_str && *key_str) + cond = filter_cond_new(FLT_COND_CMD_TEST, + 0, 0, NULL, key_str); + else + error_msg_ = _("Command is not specified."); + break; + case PF_COND_SIZE: + size_type = menu_get_option_menu_active_index + (GTK_OPTION_MENU(hbox->size_match_optmenu)); + match_flag = size_type == PF_SIZE_LARGER ? 0 : FLT_NOT_MATCH; + int_value = gtk_spin_button_get_value_as_int + (GTK_SPIN_BUTTON(hbox->spin_btn)); + cond = filter_cond_new(FLT_COND_SIZE_GREATER, + 0, match_flag, NULL, itos(int_value)); + break; + case PF_COND_AGE: + age_type = menu_get_option_menu_active_index + (GTK_OPTION_MENU(hbox->age_match_optmenu)); + match_flag = age_type == PF_AGE_LONGER ? 0 : FLT_NOT_MATCH; + int_value = gtk_spin_button_get_value_as_int + (GTK_SPIN_BUTTON(hbox->spin_btn)); + cond = filter_cond_new(FLT_COND_AGE_GREATER, + 0, match_flag, NULL, itos(int_value)); + break; + case PF_COND_UNREAD: + status_type = menu_get_option_menu_active_index + (GTK_OPTION_MENU(hbox->status_match_optmenu)); + match_flag = status_type == PF_STATUS_MATCH ? 0 : FLT_NOT_MATCH; + cond = filter_cond_new(FLT_COND_UNREAD, 0, match_flag, + NULL, NULL); + break; + case PF_COND_MARK: + status_type = menu_get_option_menu_active_index + (GTK_OPTION_MENU(hbox->status_match_optmenu)); + match_flag = status_type == PF_STATUS_MATCH ? 0 : FLT_NOT_MATCH; + cond = filter_cond_new(FLT_COND_MARK, 0, match_flag, + NULL, NULL); + break; + case PF_COND_COLOR_LABEL: + status_type = menu_get_option_menu_active_index + (GTK_OPTION_MENU(hbox->status_match_optmenu)); + match_flag = status_type == PF_STATUS_MATCH ? 0 : FLT_NOT_MATCH; + cond = filter_cond_new(FLT_COND_COLOR_LABEL, 0, match_flag, + NULL, NULL); + break; + case PF_COND_MIME: + status_type = menu_get_option_menu_active_index + (GTK_OPTION_MENU(hbox->status_match_optmenu)); + match_flag = status_type == PF_STATUS_MATCH ? 0 : FLT_NOT_MATCH; + cond = filter_cond_new(FLT_COND_MIME, 0, match_flag, + NULL, NULL); + break; + case PF_COND_ACCOUNT: + case PF_COND_EDIT_HEADER: + default: + break; + } + + if (error_msg) + *error_msg = error_msg_; + + return cond; +} + +static gboolean check_dest_folder(const gchar *dest, gchar **error_msg) +{ + FolderItem *item; + + if (!dest || *dest == '\0') { + *error_msg = _("Destination folder is not specified."); + return FALSE; + } + + item = folder_find_item_from_identifier(dest); + if (!item || !item->path || !item->parent) { + *error_msg = _("The specified destination folder does not exist."); + return FALSE; + } + + return TRUE; +} + +FilterAction *prefs_filter_edit_action_hbox_to_action(ActionHBox *hbox, + gchar **error_msg) +{ + FilterAction *action = NULL; + ActionMenuType action_menu_type; + const gchar *str; + guint color; + gchar *error_msg_ = NULL; + + action_menu_type = prefs_filter_edit_get_action_hbox_type(hbox); + + switch (action_menu_type) { + case PF_ACTION_MOVE: + str = gtk_entry_get_text(GTK_ENTRY(hbox->folder_entry)); + if (check_dest_folder(str, &error_msg_)) + action = filter_action_new(FLT_ACTION_MOVE, str); + break; + case PF_ACTION_COPY: + str = gtk_entry_get_text(GTK_ENTRY(hbox->folder_entry)); + if (check_dest_folder(str, &error_msg_)) + action = filter_action_new(FLT_ACTION_COPY, str); + break; + case PF_ACTION_NOT_RECEIVE: + action = filter_action_new(FLT_ACTION_NOT_RECEIVE, NULL); + break; + case PF_ACTION_DELETE: + action = filter_action_new(FLT_ACTION_DELETE, NULL); + break; + case PF_ACTION_EXEC: + str = gtk_entry_get_text(GTK_ENTRY(hbox->cmd_entry)); + if (str && *str) + action = filter_action_new(FLT_ACTION_EXEC, str); + else + error_msg_ = _("Command is not specified."); + break; + case PF_ACTION_EXEC_ASYNC: + str = gtk_entry_get_text(GTK_ENTRY(hbox->cmd_entry)); + if (str && *str) + action = filter_action_new(FLT_ACTION_EXEC_ASYNC, str); + else + error_msg_ = _("Command is not specified."); + break; + case PF_ACTION_MARK: + action = filter_action_new(FLT_ACTION_MARK, NULL); + break; + case PF_ACTION_COLOR_LABEL: + color = colorlabel_get_color_menu_active_item + (gtk_option_menu_get_menu + (GTK_OPTION_MENU(hbox->clabel_optmenu))); + action = filter_action_new(FLT_ACTION_COLOR_LABEL, + itos(color)); + break; + case PF_ACTION_MARK_READ: + action = filter_action_new(FLT_ACTION_MARK_READ, NULL); + break; + case PF_ACTION_FORWARD: + case PF_ACTION_FORWARD_AS_ATTACHMENT: + case PF_ACTION_REDIRECT: + break; + case PF_ACTION_STOP_EVAL: + action = filter_action_new(FLT_ACTION_STOP_EVAL, NULL); + break; + case PF_ACTION_SEPARATOR: + default: + break; + } + + if (error_msg) + *error_msg = error_msg_; + + return action; +} + +GSList *prefs_filter_edit_cond_edit_to_list(FilterCondEdit *cond_edit, + gboolean case_sens) +{ + GSList *cur; + FilterCond *cond; + GSList *cond_list = NULL; + + for (cur = cond_edit->cond_hbox_list; cur != NULL; cur = cur->next) { + CondHBox *hbox = (CondHBox *)cur->data; + gchar *error_msg; + + cond = prefs_filter_edit_cond_hbox_to_cond(hbox, case_sens, + &error_msg); + if (cond) { + cond_list = g_slist_append(cond_list, cond); + } else { + if (!error_msg) + error_msg = _("Invalid condition exists."); + alertpanel_error("%s", error_msg); + filter_cond_list_free(cond_list); + return NULL; + } + } + + return cond_list; +} + +static FilterRule *prefs_filter_edit_dialog_to_rule(void) +{ + FilterRule *rule = NULL; + GSList *cur; + const gchar *rule_name; + FilterBoolOp bool_op; + GSList *cond_list = NULL; + GSList *action_list = NULL; + GtkWidget *bool_op_menuitem; + gchar *error_msg = NULL; + + rule_name = gtk_entry_get_text(GTK_ENTRY(rule_edit_window.name_entry)); + if (!rule_name || *rule_name == '\0') { + error_msg = _("Rule name is not specified."); + goto error; + } + + bool_op_menuitem = gtk_menu_get_active + (GTK_MENU(gtk_option_menu_get_menu + (GTK_OPTION_MENU(rule_edit_window.bool_op_optmenu)))); + bool_op = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(bool_op_menuitem), MENU_VAL_ID)); + + cond_list = prefs_filter_edit_cond_edit_to_list + (&rule_edit_window.cond_edit, FALSE); + if (!cond_list) + return NULL; + + for (cur = rule_edit_window.action_hbox_list; cur != NULL; + cur = cur->next) { + ActionHBox *hbox = (ActionHBox *)cur->data; + FilterAction *action; + + action = prefs_filter_edit_action_hbox_to_action(hbox, + &error_msg); + if (action) + action_list = g_slist_append(action_list, action); + else { + if (!error_msg) + error_msg = _("Invalid action exists."); + goto error; + } + } + +error: + if (error_msg || !cond_list || !action_list) { + if (!error_msg) { + if (!cond_list) + error_msg = _("Condition not exist."); + else + error_msg = _("Action not exist."); + } + alertpanel_error("%s", error_msg); + if (cond_list) + filter_cond_list_free(cond_list); + if (action_list) + filter_action_list_free(action_list); + return NULL; + } + + rule = filter_rule_new(rule_name, bool_op, cond_list, action_list); + + return rule; +} + +/* callback functions */ + +static gint prefs_filter_edit_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + prefs_filter_edit_cancel(); + return TRUE; +} + +static gboolean prefs_filter_edit_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + prefs_filter_edit_cancel(); + return FALSE; +} + +static void prefs_filter_edit_ok(void) +{ + FilterRule *rule; + + rule = prefs_filter_edit_dialog_to_rule(); + if (rule) { + rule_edit_window.new_rule = rule; + rule_edit_window.edit_finished = TRUE; + } +} + +static void prefs_filter_edit_cancel(void) +{ + rule_edit_window.new_rule = NULL; + rule_edit_window.edit_finished = TRUE; +} + +static void prefs_filter_cond_activated_cb(GtkWidget *widget, gpointer data) +{ + CondHBox *hbox = (CondHBox *)data; + FilterCondEdit *cond_edit = hbox->cond_edit; + CondMenuType type; + + type = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(widget), MENU_VAL_ID)); + + if (type == PF_COND_EDIT_HEADER) { + prefs_filter_edit_edit_header_list(cond_edit); + prefs_filter_edit_cond_hbox_select + (hbox, hbox->cur_type, hbox->cur_header_name); + } else { + hbox->cur_type = type; + g_free(hbox->cur_header_name); + hbox->cur_header_name = NULL; + + prefs_filter_edit_set_cond_hbox_widgets(hbox, type); + if (type == PF_COND_HEADER) { + gchar *header_name; + gchar *header_field; + + header_name = (gchar *)g_object_get_data + (G_OBJECT(widget), "header_str"); + header_field = prefs_filter_get_msg_header_field + (header_name); + if (header_field) + gtk_entry_set_text(GTK_ENTRY(hbox->key_entry), + header_field); + hbox->cur_header_name = g_strdup(header_name); + } + } +} + +static void prefs_filter_match_activated_cb(GtkWidget *widget, gpointer data) +{ + CondHBox *hbox = (CondHBox *)data; + GtkWidget *cond_type_menuitem; + CondMenuType cond_menu_type; + + cond_type_menuitem = gtk_menu_get_active + (GTK_MENU(gtk_option_menu_get_menu + (GTK_OPTION_MENU(hbox->cond_type_optmenu)))); + cond_menu_type = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(cond_type_menuitem), MENU_VAL_ID)); + + prefs_filter_edit_set_cond_hbox_widgets(hbox, cond_menu_type); +} + +static void prefs_filter_action_activated_cb(GtkWidget *widget, gpointer data) +{ + ActionHBox *hbox = (ActionHBox *)data; + ActionMenuType type; + + type = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(widget), MENU_VAL_ID)); + prefs_filter_edit_set_action_hbox_widgets(hbox, type); +} + +static void prefs_filter_action_select_dest_cb(GtkWidget *widget, gpointer data) +{ + ActionHBox *hbox = (ActionHBox *)data; + + FolderItem *dest; + gchar *id; + + dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL); + if (!dest || !dest->path) return; + + id = folder_item_get_identifier(dest); + if (id) { + gtk_entry_set_text(GTK_ENTRY(hbox->folder_entry), id); + g_free(id); + } +} + +static void prefs_filter_cond_del_cb(GtkWidget *widget, gpointer data) +{ + CondHBox *hbox = (CondHBox *)data; + FilterCondEdit *cond_edit = hbox->cond_edit; + + if (cond_edit->cond_hbox_list && cond_edit->cond_hbox_list->next) + prefs_filter_edit_remove_cond_hbox(cond_edit, hbox); +} + +static void prefs_filter_cond_add_cb(GtkWidget *widget, gpointer data) +{ + CondHBox *hbox = (CondHBox *)data; + CondHBox *new_hbox; + FilterCondEdit *cond_edit = hbox->cond_edit; + gint index; + + index = g_slist_index(cond_edit->cond_hbox_list, hbox); + g_return_if_fail(index >= 0); + new_hbox = prefs_filter_edit_cond_hbox_create(cond_edit); + prefs_filter_edit_set_cond_hbox_widgets(new_hbox, PF_COND_HEADER); + prefs_filter_edit_insert_cond_hbox(cond_edit, new_hbox, index + 1); + if (cond_edit->add_hbox) + cond_edit->add_hbox(new_hbox); +} + +static void prefs_filter_action_del_cb(GtkWidget *widget, gpointer data) +{ + ActionHBox *hbox = (ActionHBox *)data; + + if (rule_edit_window.action_hbox_list && + rule_edit_window.action_hbox_list->next) + prefs_filter_edit_remove_action_hbox(hbox); +} + +static void prefs_filter_action_add_cb(GtkWidget *widget, gpointer data) +{ + ActionHBox *hbox = (ActionHBox *)data; + ActionHBox *new_hbox; + gboolean action_menu_selection[PF_ACTION_NONE]; + gint index; + + prefs_filter_edit_get_action_hbox_menus_selection(action_menu_selection); + + index = g_slist_index(rule_edit_window.action_hbox_list, hbox); + g_return_if_fail(index >= 0); + new_hbox = prefs_filter_edit_action_hbox_create(); + prefs_filter_edit_insert_action_hbox(new_hbox, index + 1); + if (action_menu_selection[PF_ACTION_MOVE] == TRUE || + action_menu_selection[PF_ACTION_NOT_RECEIVE] == TRUE || + action_menu_selection[PF_ACTION_DELETE] == TRUE) + prefs_filter_edit_set_action_hbox_widgets(new_hbox, + PF_ACTION_COPY); + else + prefs_filter_edit_set_action_hbox_widgets(new_hbox, + PF_ACTION_MOVE); +} diff --git a/src/prefs_filter_edit.h b/src/prefs_filter_edit.h new file mode 100644 index 0000000..1b748d8 --- /dev/null +++ b/src/prefs_filter_edit.h @@ -0,0 +1,196 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2012 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PREFS_FILTER_EDIT_H__ +#define __PREFS_FILTER_EDIT_H__ + +#include + +#include "filter.h" + +typedef struct _FilterCondEdit FilterCondEdit; +typedef struct _CondHBox CondHBox; +typedef struct _ActionHBox ActionHBox; + +typedef enum +{ + PF_COND_HEADER, + PF_COND_TO_OR_CC, + PF_COND_ANY_HEADER, + PF_COND_BODY, + PF_COND_CMD_TEST, + PF_COND_SIZE, + PF_COND_AGE, + PF_COND_UNREAD, + PF_COND_MARK, + PF_COND_COLOR_LABEL, + PF_COND_MIME, + PF_COND_ACCOUNT, + PF_COND_EDIT_HEADER, + PF_COND_SEPARATOR, + PF_COND_NONE +} CondMenuType; + +typedef enum +{ + PF_MATCH_CONTAIN, + PF_MATCH_NOT_CONTAIN, + PF_MATCH_EQUAL, + PF_MATCH_NOT_EQUAL, + PF_MATCH_REGEX, + PF_MATCH_NOT_REGEX, + PF_MATCH_IN_ADDRESSBOOK, + PF_MATCH_NOT_IN_ADDRESSBOOK, + PF_MATCH_NONE +} MatchMenuType; + +typedef enum +{ + PF_SIZE_LARGER, + PF_SIZE_SMALLER +} SizeMatchType; + +typedef enum +{ + PF_AGE_SHORTER, + PF_AGE_LONGER +} AgeMatchType; + +typedef enum +{ + PF_STATUS_MATCH, + PF_STATUS_NOT_MATCH +} StatusMatchType; + +typedef enum +{ + PF_ACTION_MOVE, + PF_ACTION_COPY, + PF_ACTION_NOT_RECEIVE, + PF_ACTION_DELETE, + PF_ACTION_EXEC, + PF_ACTION_EXEC_ASYNC, + PF_ACTION_MARK, + PF_ACTION_COLOR_LABEL, + PF_ACTION_MARK_READ, + PF_ACTION_FORWARD, + PF_ACTION_FORWARD_AS_ATTACHMENT, + PF_ACTION_REDIRECT, + PF_ACTION_STOP_EVAL, + PF_ACTION_SEPARATOR, + PF_ACTION_NONE +} ActionMenuType; + +struct _FilterCondEdit { + GtkWidget *cond_vbox; + GSList *cond_hbox_list; + + GSList *hdr_list; + GSList *rule_hdr_list; + + /* callback */ + void (*add_hbox) (CondHBox *hbox); +}; + +struct _CondHBox { + GtkWidget *hbox; + + GtkWidget *cond_type_optmenu; + GtkWidget *match_type_optmenu; + GtkWidget *size_match_optmenu; + GtkWidget *age_match_optmenu; + GtkWidget *status_match_optmenu; + GtkWidget *key_entry; + GtkWidget *spin_btn; + GtkWidget *label; + + GtkWidget *match_menu_in_addr; + GtkWidget *match_menu_not_in_addr; + + GtkWidget *del_btn; + GtkWidget *add_btn; + + CondMenuType cur_type; + gchar *cur_header_name; + + FilterCondEdit *cond_edit; +}; + +struct _ActionHBox { + GtkWidget *hbox; + + GtkWidget *action_type_optmenu; + GtkWidget *label; + GtkWidget *folder_entry; + GtkWidget *cmd_entry; + GtkWidget *address_entry; + GtkWidget *clabel_optmenu; + + GtkWidget *folder_sel_btn; + + GtkWidget *action_type_menu_items[PF_ACTION_NONE]; + + GtkWidget *del_btn; + GtkWidget *add_btn; +}; + + +FilterRule *prefs_filter_edit_open (FilterRule *rule, + const gchar *header, + const gchar *key); + +FilterCondEdit *prefs_filter_edit_cond_edit_create (void); +void prefs_filter_edit_clear_cond_edit (FilterCondEdit *cond_edit); + +void prefs_filter_edit_set_header_list (FilterCondEdit *cond_edit, + FilterRule *rule); + +CondHBox *prefs_filter_edit_cond_hbox_create + (FilterCondEdit *cond_edit); +ActionHBox *prefs_filter_edit_action_hbox_create (void); + +void prefs_filter_edit_cond_hbox_set (CondHBox *hbox, + FilterCond *cond); +void prefs_filter_edit_action_hbox_set (ActionHBox *hbox, + FilterAction *action); + +void prefs_filter_edit_cond_hbox_select (CondHBox *hbox, + CondMenuType type, + const gchar *header_name); + +void prefs_filter_edit_set_cond_hbox_widgets (CondHBox *hbox, + CondMenuType type); +void prefs_filter_edit_set_action_hbox_widgets (ActionHBox *hbox, + ActionMenuType type); + +void prefs_filter_edit_insert_cond_hbox (FilterCondEdit *cond_edit, + CondHBox *hbox, + gint pos); + +void prefs_filter_edit_add_rule_cond (FilterCondEdit *cond_edit, + FilterRule *rule); + +FilterCond *prefs_filter_edit_cond_hbox_to_cond (CondHBox *hbox, + gboolean case_sens, + gchar **error_msg); + +GSList *prefs_filter_edit_cond_edit_to_list (FilterCondEdit *cond_edit, + gboolean case_sens); + +#endif /* __PREFS_FILTER_EDIT_H__ */ diff --git a/src/prefs_folder_item.c b/src/prefs_folder_item.c new file mode 100644 index 0000000..a3e725a --- /dev/null +++ b/src/prefs_folder_item.c @@ -0,0 +1,639 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2006 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include + +#include "folder.h" +#include "prefs.h" +#include "prefs_ui.h" +#include "prefs_folder_item.h" +#include "prefs_account.h" +#include "account.h" +#include "manage_window.h" +#include "folderview.h" +#include "mainwindow.h" +#include "inc.h" +#include "menu.h" + +typedef struct _PrefsFolderItemDialog PrefsFolderItemDialog; + +struct _PrefsFolderItemDialog +{ + PrefsDialog *dialog; + FolderItem *item; + + /* General */ + GtkWidget *name_entry; + GtkWidget *id_label; + GtkWidget *path_label; + GtkWidget *type_optmenu; + + GtkWidget *trim_summary_subj_chkbtn; + GtkWidget *trim_compose_subj_chkbtn; + + /* Compose */ + GtkWidget *account_optmenu; + GtkWidget *ac_apply_sub_chkbtn; + GtkWidget *to_entry; + GtkWidget *on_reply_chkbtn; + GtkWidget *cc_entry; + GtkWidget *bcc_entry; + GtkWidget *replyto_entry; +}; + +static PrefsFolderItemDialog *prefs_folder_item_create + (FolderItem *item); +static void prefs_folder_item_general_create + (PrefsFolderItemDialog *dialog); +static void prefs_folder_item_compose_create + (PrefsFolderItemDialog *dialog); +static void prefs_folder_item_set_dialog(PrefsFolderItemDialog *dialog); + +static void prefs_folder_item_ok_cb (GtkWidget *widget, + PrefsFolderItemDialog *dialog); +static void prefs_folder_item_apply_cb (GtkWidget *widget, + PrefsFolderItemDialog *dialog); +static void prefs_folder_item_cancel_cb (GtkWidget *widget, + PrefsFolderItemDialog *dialog); +static gint prefs_folder_item_delete_cb (GtkWidget *widget, + GdkEventAny *event, + PrefsFolderItemDialog *dialog); +static gboolean prefs_folder_item_key_press_cb + (GtkWidget *widget, + GdkEventKey *event, + PrefsFolderItemDialog *dialog); + +void prefs_folder_item_open(FolderItem *item) +{ + PrefsFolderItemDialog *dialog; + + g_return_if_fail(item != NULL); + + inc_lock(); + + dialog = prefs_folder_item_create(item); + + manage_window_set_transient(GTK_WINDOW(dialog->dialog->window)); + + prefs_folder_item_set_dialog(dialog); + + gtk_widget_show_all(dialog->dialog->window); +} + +PrefsFolderItemDialog *prefs_folder_item_create(FolderItem *item) +{ + PrefsFolderItemDialog *new_dialog; + PrefsDialog *dialog; + + new_dialog = g_new0(PrefsFolderItemDialog, 1); + + dialog = g_new0(PrefsDialog, 1); + prefs_dialog_create(dialog); + + gtk_window_set_title(GTK_WINDOW(dialog->window), _("Folder properties")); + gtk_widget_realize(dialog->window); + g_signal_connect(G_OBJECT(dialog->window), "delete_event", + G_CALLBACK(prefs_folder_item_delete_cb), new_dialog); + g_signal_connect(G_OBJECT(dialog->window), "key_press_event", + G_CALLBACK(prefs_folder_item_key_press_cb), new_dialog); + MANAGE_WINDOW_SIGNALS_CONNECT(dialog->window); + + g_signal_connect(G_OBJECT(dialog->ok_btn), "clicked", + G_CALLBACK(prefs_folder_item_ok_cb), new_dialog); + g_signal_connect(G_OBJECT(dialog->apply_btn), "clicked", + G_CALLBACK(prefs_folder_item_apply_cb), new_dialog); + g_signal_connect(G_OBJECT(dialog->cancel_btn), "clicked", + G_CALLBACK(prefs_folder_item_cancel_cb), new_dialog); + + new_dialog->dialog = dialog; + new_dialog->item = item; + + prefs_folder_item_general_create(new_dialog); + prefs_folder_item_compose_create(new_dialog); + + SET_NOTEBOOK_LABEL(dialog->notebook, _("General"), 0); + SET_NOTEBOOK_LABEL(dialog->notebook, _("Compose"), 1); + + return new_dialog; +} + +static void prefs_folder_item_general_create(PrefsFolderItemDialog *dialog) +{ + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *name_entry; + GtkWidget *id_label; + GtkWidget *path_label; + GtkWidget *optmenu; + GtkWidget *optmenu_menu; + GtkWidget *menuitem; + GtkWidget *vbox2; + GtkWidget *trim_summary_subj_chkbtn; + GtkWidget *trim_compose_subj_chkbtn; + GtkStyle *style; + + vbox = gtk_vbox_new(FALSE, VSPACING); + gtk_container_add(GTK_CONTAINER(dialog->dialog->notebook), vbox); + gtk_container_set_border_width(GTK_CONTAINER (vbox), VBOX_BORDER); + + table = gtk_table_new(4, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + + label = gtk_label_new(_("Name")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + name_entry = gtk_entry_new(); + gtk_editable_set_editable(GTK_EDITABLE(name_entry), FALSE); + gtk_widget_set_size_request(name_entry, 200, -1); + style = gtk_widget_get_style(dialog->dialog->window); + gtk_widget_modify_base(name_entry, GTK_STATE_NORMAL, + &style->bg[GTK_STATE_NORMAL]); + gtk_table_attach(GTK_TABLE(table), name_entry, 1, 2, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + label = gtk_label_new(_("Identifier")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + id_label = gtk_label_new(""); + gtk_label_set_selectable(GTK_LABEL(id_label), TRUE); + gtk_misc_set_alignment(GTK_MISC(id_label), 0, 0.5); + gtk_label_set_justify(GTK_LABEL(id_label), GTK_JUSTIFY_LEFT); +#if GTK_CHECK_VERSION(2, 6, 0) + gtk_label_set_ellipsize(GTK_LABEL(id_label), PANGO_ELLIPSIZE_MIDDLE); +#endif + gtk_table_attach(GTK_TABLE(table), id_label, 1, 2, 1, 2, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + label = gtk_label_new(_("Path")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + path_label = gtk_label_new(""); + gtk_label_set_selectable(GTK_LABEL(path_label), TRUE); + gtk_misc_set_alignment(GTK_MISC(path_label), 0, 0.5); + gtk_label_set_justify(GTK_LABEL(path_label), GTK_JUSTIFY_LEFT); +#if GTK_CHECK_VERSION(2, 6, 0) + gtk_label_set_ellipsize(GTK_LABEL(path_label), PANGO_ELLIPSIZE_MIDDLE); +#endif + gtk_table_attach(GTK_TABLE(table), path_label, 1, 2, 2, 3, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + label = gtk_label_new(_("Type")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_widget_show(hbox); + gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 3, 4, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + optmenu = gtk_option_menu_new(); + gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0); + + optmenu_menu = gtk_menu_new(); + + MENUITEM_ADD(optmenu_menu, menuitem, _("Normal"), F_NORMAL); + MENUITEM_ADD(optmenu_menu, menuitem, _("Inbox") , F_INBOX); + MENUITEM_ADD(optmenu_menu, menuitem, _("Sent") , F_OUTBOX); + MENUITEM_ADD(optmenu_menu, menuitem, _("Drafts"), F_DRAFT); + MENUITEM_ADD(optmenu_menu, menuitem, _("Queue") , F_QUEUE); + MENUITEM_ADD(optmenu_menu, menuitem, _("Trash") , F_TRASH); + MENUITEM_ADD(optmenu_menu, menuitem, _("Junk") , F_JUNK); + + gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), optmenu_menu); + + vbox2 = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 0); + + PACK_CHECK_BUTTON(vbox2, trim_summary_subj_chkbtn, + _("Don't display [...] or (...) at the beginning of subject in summary")); + PACK_CHECK_BUTTON(vbox2, trim_compose_subj_chkbtn, + _("Delete [...] or (...) at the beginning of subject on reply")); + + if (!dialog->item->parent) { + gtk_widget_set_sensitive(optmenu, FALSE); + gtk_widget_set_sensitive(vbox2, FALSE); + } + if (dialog->item->stype == F_VIRTUAL) + gtk_widget_set_sensitive(optmenu, FALSE); + + dialog->name_entry = name_entry; + dialog->id_label = id_label; + dialog->path_label = path_label; + dialog->type_optmenu = optmenu; + dialog->trim_summary_subj_chkbtn = trim_summary_subj_chkbtn; + dialog->trim_compose_subj_chkbtn = trim_compose_subj_chkbtn; +} + +static void prefs_folder_item_compose_create(PrefsFolderItemDialog *dialog) +{ + GtkWidget *vbox; + GtkWidget *frame; + GtkWidget *account_vbox; + GtkWidget *table; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *optmenu; + GtkWidget *optmenu_menu; + GtkWidget *menuitem; + GtkWidget *ac_apply_sub_chkbtn; + GtkWidget *to_entry; + GtkWidget *on_reply_chkbtn; + GtkWidget *cc_entry; + GtkWidget *bcc_entry; + GtkWidget *replyto_entry; + GList *list; + + vbox = gtk_vbox_new(FALSE, VSPACING); + gtk_container_add(GTK_CONTAINER(dialog->dialog->notebook), vbox); + gtk_container_set_border_width(GTK_CONTAINER (vbox), VBOX_BORDER); + + PACK_FRAME(vbox, frame, _("Account")); + + account_vbox = gtk_vbox_new(FALSE, VSPACING_NARROW); + gtk_container_add(GTK_CONTAINER(frame), account_vbox); + gtk_container_set_border_width (GTK_CONTAINER (account_vbox), 8); + + table = gtk_table_new(1, 2, FALSE); + gtk_box_pack_start(GTK_BOX(account_vbox), table, FALSE, FALSE, 0); + gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + + label = gtk_label_new(_("Account")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_widget_show(hbox); + gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + optmenu = gtk_option_menu_new(); + gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0); + + optmenu_menu = gtk_menu_new(); + + MENUITEM_ADD(optmenu_menu, menuitem, _("None"), -1); + + for (list = account_get_list(); list != NULL; list = list->next) { + gchar *text; + PrefsAccount *ac = list->data; + + text = g_strdup_printf("%s: %s", ac->account_name, ac->address); + MENUITEM_ADD(optmenu_menu, menuitem, text, ac->account_id); + } + + gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), optmenu_menu); + + PACK_CHECK_BUTTON(account_vbox, ac_apply_sub_chkbtn, + _("Apply to subfolders")); + + PACK_FRAME(vbox, frame, _("Automatically set the following addresses")); + + table = gtk_table_new(4, 2, FALSE); + gtk_container_add(GTK_CONTAINER(frame), table); + gtk_container_set_border_width (GTK_CONTAINER (table), 8); + gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + + label = gtk_label_new(_("To:")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_widget_show(hbox); + gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + to_entry = gtk_entry_new(); + gtk_widget_set_size_request(to_entry, 200, -1); + gtk_box_pack_start(GTK_BOX(hbox), to_entry, TRUE, TRUE, 0); + + PACK_CHECK_BUTTON(hbox, on_reply_chkbtn, _("use also on reply")); + + label = gtk_label_new(_("Cc:")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + cc_entry = gtk_entry_new(); + gtk_widget_set_size_request(cc_entry, 200, -1); + gtk_table_attach(GTK_TABLE(table), cc_entry, 1, 2, 1, 2, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + label = gtk_label_new(_("Bcc:")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + bcc_entry = gtk_entry_new(); + gtk_widget_set_size_request(bcc_entry, 200, -1); + gtk_table_attach(GTK_TABLE(table), bcc_entry, 1, 2, 2, 3, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + label = gtk_label_new(_("Reply-To:")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + replyto_entry = gtk_entry_new(); + gtk_widget_set_size_request(replyto_entry, 200, -1); + gtk_table_attach(GTK_TABLE(table), replyto_entry, 1, 2, 3, 4, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + if (!dialog->item->parent) { + gtk_widget_set_sensitive(frame, FALSE); + gtk_widget_set_sensitive(ac_apply_sub_chkbtn, FALSE); + } + + dialog->account_optmenu = optmenu; + dialog->ac_apply_sub_chkbtn = ac_apply_sub_chkbtn; + dialog->to_entry = to_entry; + dialog->on_reply_chkbtn = on_reply_chkbtn; + dialog->cc_entry = cc_entry; + dialog->bcc_entry = bcc_entry; + dialog->replyto_entry = replyto_entry; +} + +#define SET_ENTRY(entry, str) \ + gtk_entry_set_text(GTK_ENTRY(dialog->entry), \ + dialog->item->str ? dialog->item->str : "") + +static void prefs_folder_item_set_dialog(PrefsFolderItemDialog *dialog) +{ + GtkWidget *menu; + GtkWidget *menuitem; + GtkOptionMenu *optmenu; + gchar *id; + gchar *path; + gchar *utf8_path; + GList *cur; + SpecialFolderItemType type; + gint n; + guint index = 0; + + /* General */ + + SET_ENTRY(name_entry, name); + + id = folder_item_get_identifier(dialog->item); + gtk_label_set_text(GTK_LABEL(dialog->id_label), id); + g_free(id); + + path = folder_item_get_path(dialog->item); + utf8_path = conv_filename_to_utf8(path); + gtk_label_set_text(GTK_LABEL(dialog->path_label), utf8_path); + g_free(utf8_path); + g_free(path); + + optmenu = GTK_OPTION_MENU(dialog->type_optmenu); + menu = gtk_option_menu_get_menu(optmenu); + for (cur = GTK_MENU_SHELL(menu)->children, n = 0; + cur != NULL; cur = cur->next, n++) { + menuitem = GTK_WIDGET(cur->data); + type = (SpecialFolderItemType) + g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID); + if (type != F_NORMAL && + FOLDER_TYPE(dialog->item->folder) == F_NEWS) + gtk_widget_set_sensitive(menuitem, FALSE); + if (dialog->item->stype == type) + index = n; + } + + gtk_option_menu_set_history(optmenu, index); + + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON(dialog->trim_summary_subj_chkbtn), + dialog->item->trim_summary_subject); + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON(dialog->trim_compose_subj_chkbtn), + dialog->item->trim_compose_subject); + + /* Compose */ + + index = 0; + optmenu = GTK_OPTION_MENU(dialog->account_optmenu); + if (dialog->item->account) { + index = menu_find_option_menu_index + (optmenu, + GINT_TO_POINTER(dialog->item->account->account_id), + NULL); + if (index < 0) + index = 0; + } + + gtk_option_menu_set_history(optmenu, index); + + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON(dialog->ac_apply_sub_chkbtn), + dialog->item->ac_apply_sub); + + SET_ENTRY(to_entry, auto_to); + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON(dialog->on_reply_chkbtn), + dialog->item->use_auto_to_on_reply); + + SET_ENTRY(cc_entry, auto_cc); + SET_ENTRY(bcc_entry, auto_bcc); + SET_ENTRY(replyto_entry, auto_replyto); +} + +#undef SET_ENTRY + +void prefs_folder_item_destroy(PrefsFolderItemDialog *dialog) +{ + prefs_dialog_destroy(dialog->dialog); + g_free(dialog->dialog); + g_free(dialog); + + main_window_popup(main_window_get()); + inc_unlock(); +} + +static void prefs_folder_item_ok_cb(GtkWidget *widget, + PrefsFolderItemDialog *dialog) +{ + prefs_folder_item_apply_cb(widget, dialog); + prefs_folder_item_destroy(dialog); +} + +#define SET_DATA_FROM_ENTRY(entry, str) \ +{ \ + entry_str = gtk_entry_get_text(GTK_ENTRY(dialog->entry)); \ + g_free(item->str); \ + item->str = (entry_str && *entry_str) ? g_strdup(entry_str) : NULL; \ +} + +static void prefs_folder_item_apply_cb(GtkWidget *widget, + PrefsFolderItemDialog *dialog) +{ + GtkWidget *menu; + GtkWidget *menuitem; + GtkOptionMenu *optmenu; + SpecialFolderItemType type; + FolderItem *item = dialog->item; + Folder *folder = item->folder; + FolderItem *prev_item = NULL; + gint account_id; + const gchar *entry_str; + + optmenu = GTK_OPTION_MENU(dialog->type_optmenu); + menu = gtk_option_menu_get_menu(optmenu); + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + type = (SpecialFolderItemType) + g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID); + + if (item->stype != type && item->stype != F_VIRTUAL) { + switch (type) { + case F_NORMAL: + break; + case F_INBOX: + if (folder->inbox) + folder->inbox->stype = F_NORMAL; + prev_item = folder->inbox; + folder->inbox = item; + break; + case F_OUTBOX: + if (folder->outbox) + folder->outbox->stype = F_NORMAL; + prev_item = folder->outbox; + folder->outbox = item; + break; + case F_DRAFT: + if (folder->draft) + folder->draft->stype = F_NORMAL; + prev_item = folder->draft; + folder->draft = item; + break; + case F_QUEUE: + if (folder->queue) + folder->queue->stype = F_NORMAL; + prev_item = folder->queue; + folder->queue = item; + break; + case F_TRASH: + if (folder->trash) + folder->trash->stype = F_NORMAL; + prev_item = folder->trash; + folder->trash = item; + break; + case F_JUNK: + prev_item = folder_get_junk(folder); + if (prev_item) + prev_item->stype = F_NORMAL; + folder_set_junk(folder, item); + break; + default: + type = item->stype; + break; + } + + item->stype = type; + + if (prev_item) + folderview_update_item(prev_item, FALSE); + folderview_update_item(item, FALSE); + } + + item->trim_summary_subject = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(dialog->trim_summary_subj_chkbtn)); + item->trim_compose_subject = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(dialog->trim_compose_subj_chkbtn)); + + /* account menu */ + optmenu = GTK_OPTION_MENU(dialog->account_optmenu); + menu = gtk_option_menu_get_menu(optmenu); + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + account_id = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID)); + if (account_id >= 0) + item->account = account_find_from_id(account_id); + else + item->account = NULL; + + if (!item->parent && item->account) + item->ac_apply_sub = TRUE; + else if (item->account) + item->ac_apply_sub = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(dialog->ac_apply_sub_chkbtn)); + else + item->ac_apply_sub = FALSE; + + SET_DATA_FROM_ENTRY(to_entry, auto_to); + item->use_auto_to_on_reply = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(dialog->on_reply_chkbtn)); + + SET_DATA_FROM_ENTRY(cc_entry, auto_cc); + SET_DATA_FROM_ENTRY(bcc_entry, auto_bcc); + SET_DATA_FROM_ENTRY(replyto_entry, auto_replyto); +} + +#undef SET_DATA_FROM_ENTRY + +static void prefs_folder_item_cancel_cb(GtkWidget *widget, + PrefsFolderItemDialog *dialog) +{ + prefs_folder_item_destroy(dialog); +} + +static gint prefs_folder_item_delete_cb(GtkWidget *widget, GdkEventAny *event, + PrefsFolderItemDialog *dialog) +{ + prefs_folder_item_destroy(dialog); + return TRUE; +} + +static gboolean prefs_folder_item_key_press_cb(GtkWidget *widget, + GdkEventKey *event, + PrefsFolderItemDialog *dialog) +{ + if (event && event->keyval == GDK_Escape) + prefs_folder_item_cancel_cb(widget, dialog); + return FALSE; +} diff --git a/src/prefs_folder_item.h b/src/prefs_folder_item.h new file mode 100644 index 0000000..f68d1c0 --- /dev/null +++ b/src/prefs_folder_item.h @@ -0,0 +1,29 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2006 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PREFS_FOLDER_ITEM_H__ +#define __PREFS_FOLDER_ITEM_H__ + +#include + +#include "folder.h" + +void prefs_folder_item_open (FolderItem *item); + +#endif /* __PREFS_FOLDER_ITEM_H__ */ diff --git a/src/prefs_search_folder.c b/src/prefs_search_folder.c new file mode 100644 index 0000000..a8ad0cd --- /dev/null +++ b/src/prefs_search_folder.c @@ -0,0 +1,453 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2006 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prefs_search_folder.h" +#include "prefs_ui.h" +#include "mainwindow.h" +#include "manage_window.h" +#include "alertpanel.h" +#include "foldersel.h" +#include "folder.h" +#include "filter.h" +#include "prefs_filter.h" +#include "prefs_filter_edit.h" +#include "menu.h" +#include "utils.h" +#include "gtkutils.h" + +typedef struct _PrefsSearchFolderDialog PrefsSearchFolderDialog; + +struct _PrefsSearchFolderDialog +{ + PrefsDialog *dialog; + FolderItem *item; + + GtkWidget *name_entry; + + GtkWidget *bool_optmenu; + + FilterCondEdit *cond_edit; + + GtkWidget *folder_entry; + GtkWidget *folder_btn; + + GtkWidget *subfolder_checkbtn; + GtkWidget *case_checkbtn; + + gboolean finished; + gboolean updated; +}; + +static PrefsSearchFolderDialog *prefs_search_folder_create(FolderItem *item); +static void prefs_search_folder_set_dialog(PrefsSearchFolderDialog *dialog); +static void prefs_search_folder_destroy (PrefsSearchFolderDialog *dialog); + +static gint prefs_search_folder_delete_cb(GtkWidget *widget, + GdkEventAny *event, + PrefsSearchFolderDialog *dialog); +static gint prefs_search_folder_key_press_cb + (GtkWidget *widget, + GdkEventKey *event, + PrefsSearchFolderDialog *dialog); + +static void prefs_search_folder_select_folder + (GtkWidget *widget, + PrefsSearchFolderDialog *dialog); + +static void prefs_search_folder_ok_cb (GtkWidget *widget, + PrefsSearchFolderDialog *dialog); +static void prefs_search_folder_apply_cb (GtkWidget *widget, + PrefsSearchFolderDialog *dialog); +static void prefs_search_folder_cancel_cb(GtkWidget *widget, + PrefsSearchFolderDialog *dialog); + + +gboolean prefs_search_folder_open(FolderItem *item) +{ + PrefsSearchFolderDialog *dialog; + gboolean updated; + + g_return_val_if_fail(item != NULL, FALSE); + + dialog = prefs_search_folder_create(item); + manage_window_set_transient(GTK_WINDOW(dialog->dialog->window)); + prefs_search_folder_set_dialog(dialog); + gtk_widget_show(dialog->dialog->window); + + while (dialog->finished == FALSE) + gtk_main_iteration(); + + updated = dialog->updated; + prefs_search_folder_destroy(dialog); + + return updated; +} + +static PrefsSearchFolderDialog *prefs_search_folder_create(FolderItem *item) +{ + PrefsSearchFolderDialog *new_dialog; + PrefsDialog *dialog; + gchar *title; + GtkWidget *vbox; + + GtkWidget *name_hbox; + GtkWidget *name_label; + GtkWidget *name_entry; + GtkStyle *style; + + GtkWidget *bool_hbox; + GtkWidget *bool_optmenu; + GtkWidget *bool_menu; + GtkWidget *menuitem; + + GtkWidget *scrolledwin; + FilterCondEdit *cond_edit; + + GtkWidget *folder_hbox; + GtkWidget *folder_label; + GtkWidget *folder_entry; + GtkWidget *folder_btn; + + GtkWidget *checkbtn_hbox; + GtkWidget *subfolder_checkbtn; + GtkWidget *case_checkbtn; + + new_dialog = g_new0(PrefsSearchFolderDialog, 1); + + dialog = g_new0(PrefsDialog, 1); + prefs_dialog_create(dialog); + gtk_widget_hide(dialog->apply_btn); + + gtk_widget_set_size_request(dialog->window, 600, -1); + title = g_strdup_printf(_("%s - Edit search condition"), item->name); + gtk_window_set_title(GTK_WINDOW(dialog->window), title); + g_free(title); + gtk_notebook_set_show_border(GTK_NOTEBOOK(dialog->notebook), FALSE); + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(dialog->notebook), FALSE); + gtk_widget_realize(dialog->window); + + g_signal_connect(G_OBJECT(dialog->window), "delete_event", + G_CALLBACK(prefs_search_folder_delete_cb), new_dialog); + g_signal_connect(G_OBJECT(dialog->window), "key_press_event", + G_CALLBACK(prefs_search_folder_key_press_cb), + new_dialog); + + MANAGE_WINDOW_SIGNALS_CONNECT(dialog->window); + + vbox = gtk_vbox_new(FALSE, 6); + gtk_widget_show(vbox); + gtk_container_add(GTK_CONTAINER(dialog->notebook), vbox); + + name_hbox = gtk_hbox_new(FALSE, 8); + gtk_widget_show(name_hbox); + gtk_box_pack_start(GTK_BOX(vbox), name_hbox, FALSE, FALSE, 0); + + name_label = gtk_label_new(_("Name:")); + gtk_widget_show(name_label); + gtk_box_pack_start(GTK_BOX(name_hbox), name_label, FALSE, FALSE, 0); + + name_entry = gtk_entry_new(); + gtk_widget_show(name_entry); + gtk_editable_set_editable(GTK_EDITABLE(name_entry), FALSE); + gtk_box_pack_start(GTK_BOX(name_hbox), name_entry, TRUE, TRUE, 0); + + style = gtk_widget_get_style(dialog->window); + gtk_widget_modify_base(name_entry, GTK_STATE_NORMAL, + &style->bg[GTK_STATE_NORMAL]); + + bool_hbox = gtk_hbox_new(FALSE, 12); + gtk_widget_show(bool_hbox); + gtk_box_pack_start(GTK_BOX(vbox), bool_hbox, FALSE, FALSE, 0); + + bool_optmenu = gtk_option_menu_new(); + gtk_widget_show(bool_optmenu); + gtk_box_pack_start(GTK_BOX(bool_hbox), bool_optmenu, FALSE, FALSE, 0); + + bool_menu = gtk_menu_new(); + MENUITEM_ADD(bool_menu, menuitem, _("Match any of the following"), + FLT_OR); + MENUITEM_ADD(bool_menu, menuitem, _("Match all of the following"), + FLT_AND); + gtk_option_menu_set_menu(GTK_OPTION_MENU(bool_optmenu), bool_menu); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(scrolledwin); + gtk_widget_set_size_request(scrolledwin, -1, 150); + gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + cond_edit = prefs_filter_edit_cond_edit_create(); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolledwin), + cond_edit->cond_vbox); + + folder_hbox = gtk_hbox_new(FALSE, 8); + gtk_widget_show(folder_hbox); + gtk_box_pack_start(GTK_BOX(vbox), folder_hbox, FALSE, FALSE, 0); + + folder_label = gtk_label_new(_("Folder:")); + gtk_widget_show(folder_label); + gtk_box_pack_start(GTK_BOX(folder_hbox), folder_label, FALSE, FALSE, 0); + + folder_entry = gtk_entry_new(); + gtk_widget_show(folder_entry); + gtk_box_pack_start(GTK_BOX(folder_hbox), folder_entry, TRUE, TRUE, 0); + + folder_btn = gtk_button_new_with_label("..."); + gtk_widget_show(folder_btn); + gtk_box_pack_start(GTK_BOX(folder_hbox), folder_btn, FALSE, FALSE, 0); + + checkbtn_hbox = gtk_hbox_new(FALSE, 12); + gtk_widget_show(checkbtn_hbox); + gtk_box_pack_start(GTK_BOX(vbox), checkbtn_hbox, FALSE, FALSE, 0); + + subfolder_checkbtn = + gtk_check_button_new_with_label(_("Search subfolders")); + gtk_widget_show(subfolder_checkbtn); + gtk_box_pack_start(GTK_BOX(checkbtn_hbox), subfolder_checkbtn, + FALSE, FALSE, 0); + + case_checkbtn = gtk_check_button_new_with_label(_("Case sensitive")); + gtk_widget_show(case_checkbtn); + gtk_box_pack_start(GTK_BOX(checkbtn_hbox), case_checkbtn, + FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(folder_btn), "clicked", + G_CALLBACK(prefs_search_folder_select_folder), + new_dialog); + g_signal_connect(G_OBJECT(dialog->ok_btn), "clicked", + G_CALLBACK(prefs_search_folder_ok_cb), new_dialog); + g_signal_connect(G_OBJECT(dialog->apply_btn), "clicked", + G_CALLBACK(prefs_search_folder_apply_cb), new_dialog); + g_signal_connect(G_OBJECT(dialog->cancel_btn), "clicked", + G_CALLBACK(prefs_search_folder_cancel_cb), new_dialog); + + gtk_widget_grab_focus(dialog->ok_btn); + + new_dialog->dialog = dialog; + new_dialog->item = item; + new_dialog->name_entry = name_entry; + new_dialog->bool_optmenu = bool_optmenu; + new_dialog->cond_edit = cond_edit; + + new_dialog->folder_entry = folder_entry; + new_dialog->folder_btn = folder_btn; + new_dialog->subfolder_checkbtn = subfolder_checkbtn; + new_dialog->case_checkbtn = case_checkbtn; + + new_dialog->finished = FALSE; + new_dialog->updated = FALSE; + + return new_dialog; +} + +static void prefs_search_folder_set_dialog(PrefsSearchFolderDialog *dialog) +{ + GSList *flist; + FilterRule *rule; + GSList *cur; + gchar *path; + gchar *rule_file; + gint index; + gboolean case_sens = FALSE; + + path = folder_item_get_path(dialog->item); + rule_file = g_strconcat(path, G_DIR_SEPARATOR_S, "filter.xml", NULL); + flist = filter_read_file(rule_file); + g_free(rule_file); + g_free(path); + + if (!flist) { + g_warning("filter rule not found\n"); + return; + } + + gtk_entry_set_text(GTK_ENTRY(dialog->name_entry), dialog->item->name); + + rule = (FilterRule *)flist->data; + + index = menu_find_option_menu_index + (GTK_OPTION_MENU(dialog->bool_optmenu), + GINT_TO_POINTER(rule->bool_op), NULL); + if (index < 0) + index = 0; + gtk_option_menu_set_history(GTK_OPTION_MENU(dialog->bool_optmenu), + index); + + gtk_entry_set_text(GTK_ENTRY(dialog->folder_entry), + rule->target_folder); + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON(dialog->subfolder_checkbtn), + rule->recursive); + for (cur = rule->cond_list; cur != NULL; cur = cur->next) { + FilterCond *cond = (FilterCond *)cur->data; + if (FLT_IS_CASE_SENS(cond->match_flag)) { + case_sens = TRUE; + break; + } + } + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON(dialog->case_checkbtn), case_sens); + + prefs_filter_set_header_list(NULL); + prefs_filter_edit_set_header_list(dialog->cond_edit, rule); + prefs_filter_edit_add_rule_cond(dialog->cond_edit, rule); + + filter_rule_list_free(flist); +} + +static void prefs_search_folder_destroy(PrefsSearchFolderDialog *dialog) +{ + prefs_dialog_destroy(dialog->dialog); + g_free(dialog->dialog); + g_free(dialog); + + main_window_popup(main_window_get()); +} + +static gint prefs_search_folder_delete_cb(GtkWidget *widget, GdkEventAny *event, + PrefsSearchFolderDialog *dialog) +{ + dialog->finished = TRUE; + return TRUE; +} + +static gint prefs_search_folder_key_press_cb(GtkWidget *widget, + GdkEventKey *event, + PrefsSearchFolderDialog *dialog) +{ + if (event && event->keyval == GDK_Escape) { + prefs_search_folder_cancel_cb(widget, dialog); + return TRUE; + } + return FALSE; +} + +static void prefs_search_folder_select_folder(GtkWidget *widget, + PrefsSearchFolderDialog *dialog) +{ + FolderItem *item; + gchar *id; + + item = foldersel_folder_sel(NULL, FOLDER_SEL_ALL, NULL); + if (!item || item->stype == F_VIRTUAL) + return; + + id = folder_item_get_identifier(item); + if (id) { + gtk_entry_set_text(GTK_ENTRY(dialog->folder_entry), id); + g_free(id); + } +} + +static void prefs_search_folder_ok_cb(GtkWidget *widget, + PrefsSearchFolderDialog *dialog) +{ + prefs_search_folder_apply_cb(widget, dialog); + dialog->finished = TRUE; +} + +static void prefs_search_folder_apply_cb(GtkWidget *widget, + PrefsSearchFolderDialog *dialog) +{ + const gchar *id; + FolderItem *item; + FilterBoolOp bool_op; + gboolean recursive; + gboolean case_sens; + GSList *cond_list; + FilterRule *rule; + GSList list; + gchar *file; + gchar *path; + + id = gtk_entry_get_text(GTK_ENTRY(dialog->folder_entry)); + item = folder_find_item_from_identifier(id); + if (!item) + return; + + bool_op = menu_get_option_menu_active_index + (GTK_OPTION_MENU(dialog->bool_optmenu)); + recursive = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(dialog->subfolder_checkbtn)); + case_sens = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(dialog->case_checkbtn)); + + cond_list = prefs_filter_edit_cond_edit_to_list(dialog->cond_edit, + case_sens); + if (!cond_list) + return; + + rule = filter_rule_new(dialog->item->name, bool_op, cond_list, NULL); + rule->target_folder = g_strdup(id); + rule->recursive = recursive; + list.data = rule; + list.next = NULL; + + path = folder_item_get_path(dialog->item); + file = g_strconcat(path, G_DIR_SEPARATOR_S, FILTER_LIST, NULL); + filter_write_file(&list, file); + g_free(file); + file = g_strconcat(path, G_DIR_SEPARATOR_S, FILTER_LIST, ".bak", NULL); + if (is_file_exist(file)) + g_unlink(file); + g_free(file); + file = g_strconcat(path, G_DIR_SEPARATOR_S, SEARCH_CACHE, NULL); + if (is_file_exist(file)) + g_unlink(file); + g_free(file); + g_free(path); + + filter_rule_free(rule); + + dialog->updated = TRUE; +} + +static void prefs_search_folder_cancel_cb(GtkWidget *widget, + PrefsSearchFolderDialog *dialog) +{ + dialog->finished = TRUE; +} diff --git a/src/prefs_search_folder.h b/src/prefs_search_folder.h new file mode 100644 index 0000000..60f5179 --- /dev/null +++ b/src/prefs_search_folder.h @@ -0,0 +1,29 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2006 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PREFS_SEARCH_FOLDER_H__ +#define __PREFS_SEARCH_FOLDER_H__ + +#include + +#include "folder.h" + +gboolean prefs_search_folder_open (FolderItem *item); + +#endif /* __PREFS_SEARCH_FOLDER_H__ */ diff --git a/src/prefs_summary_column.c b/src/prefs_summary_column.c new file mode 100644 index 0000000..7b223b2 --- /dev/null +++ b/src/prefs_summary_column.c @@ -0,0 +1,592 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2007 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include + +#include "prefs.h" +#include "prefs_ui.h" +#include "prefs_common.h" +#include "prefs_summary_column.h" +#include "manage_window.h" +#include "summaryview.h" +#include "mainwindow.h" +#include "inc.h" +#include "gtkutils.h" +#include "utils.h" + +static struct _SummaryColumnDialog +{ + GtkWidget *window; + + GtkWidget *stock_clist; + GtkWidget *shown_clist; + + GtkWidget *add_btn; + GtkWidget *remove_btn; + GtkWidget *up_btn; + GtkWidget *down_btn; + + GtkWidget *default_btn; + + GtkWidget *confirm_area; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + + gboolean sent_folder; + + gboolean finished; +} summary_col; + +static const gchar *const col_name[N_SUMMARY_VISIBLE_COLS] = { + N_("Mark"), /* S_COL_MARK */ + N_("Unread"), /* S_COL_UNREAD */ + N_("Attachment"), /* S_COL_MIME */ + N_("Subject"), /* S_COL_SUBJECT */ + N_("From"), /* S_COL_FROM */ + N_("Date"), /* S_COL_DATE */ + N_("Size"), /* S_COL_SIZE */ + N_("Number"), /* S_COL_NUMBER */ + N_("To") /* S_COL_TO */ +}; + +static SummaryColumnState default_state[N_SUMMARY_VISIBLE_COLS] = { + { S_COL_MARK , TRUE }, + { S_COL_UNREAD , TRUE }, + { S_COL_MIME , TRUE }, + { S_COL_SUBJECT, TRUE }, + { S_COL_FROM , TRUE }, + { S_COL_DATE , TRUE }, + { S_COL_SIZE , TRUE }, + { S_COL_NUMBER , FALSE }, + { S_COL_TO , FALSE } +}; + +static SummaryColumnState default_sent_state[N_SUMMARY_VISIBLE_COLS] = { + { S_COL_MARK , TRUE }, + { S_COL_UNREAD , TRUE }, + { S_COL_MIME , TRUE }, + { S_COL_SUBJECT, TRUE }, + { S_COL_TO , TRUE }, + { S_COL_DATE , TRUE }, + { S_COL_SIZE , TRUE }, + { S_COL_NUMBER , FALSE }, + { S_COL_FROM , FALSE } +}; + +static void prefs_summary_column_create (void); + +static void prefs_summary_column_set_dialog (SummaryColumnState *state); +static void prefs_summary_column_set_view (void); + +/* callback functions */ +static void prefs_summary_column_add (void); +static void prefs_summary_column_remove (void); + +static void prefs_summary_column_up (void); +static void prefs_summary_column_down (void); + +static void prefs_summary_column_set_to_default (void); + +static void prefs_summary_column_ok (void); +static void prefs_summary_column_cancel (void); + +static gint prefs_summary_column_delete_event (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean prefs_summary_column_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gpointer data); + +void prefs_summary_column_open(gboolean sent_folder) +{ + inc_lock(); + + prefs_summary_column_create(); + summary_col.sent_folder = sent_folder; + + gtkut_box_set_reverse_order(GTK_BOX(summary_col.confirm_area), + !prefs_common.comply_gnome_hig); + manage_window_set_transient(GTK_WINDOW(summary_col.window)); + gtk_widget_grab_focus(summary_col.ok_btn); + + prefs_summary_column_set_dialog(NULL); + + gtk_widget_show(summary_col.window); + + summary_col.finished = FALSE; + while (summary_col.finished == FALSE) + gtk_main_iteration(); + + gtk_widget_destroy(summary_col.window); + summary_col.window = NULL; + main_window_popup(main_window_get()); + + inc_unlock(); +} + +static void prefs_summary_column_create(void) +{ + GtkWidget *window; + GtkWidget *vbox; + + GtkWidget *label_hbox; + GtkWidget *label; + + GtkWidget *vbox1; + + GtkWidget *hbox1; + GtkWidget *clist_hbox; + GtkWidget *scrolledwin; + GtkWidget *stock_clist; + GtkWidget *shown_clist; + + GtkWidget *btn_vbox; + GtkWidget *btn_vbox1; + GtkWidget *add_btn; + GtkWidget *remove_btn; + GtkWidget *up_btn; + GtkWidget *down_btn; + + GtkWidget *btn_hbox; + GtkWidget *default_btn; + GtkWidget *confirm_area; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + + gchar *title[1]; + + debug_print(_("Creating summary column setting window...\n")); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + gtk_window_set_position(GTK_WINDOW(window), + GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE); + gtk_window_set_title(GTK_WINDOW(window), + _("Summary display item setting")); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(prefs_summary_column_delete_event), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(prefs_summary_column_key_pressed), NULL); + + vbox = gtk_vbox_new(FALSE, 6); + gtk_widget_show(vbox); + gtk_container_add(GTK_CONTAINER(window), vbox); + + label_hbox = gtk_hbox_new(FALSE, 0); + gtk_widget_show(label_hbox); + gtk_box_pack_start(GTK_BOX(vbox), label_hbox, FALSE, FALSE, 4); + + label = gtk_label_new + (_("Select items to be displayed on the summary view. You can modify\n" + "the order by using the Up / Down button.")); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(label_hbox), label, FALSE, FALSE, 4); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); + + vbox1 = gtk_vbox_new(FALSE, VSPACING); + gtk_widget_show(vbox1); + gtk_box_pack_start(GTK_BOX(vbox), vbox1, TRUE, TRUE, 0); + gtk_container_set_border_width(GTK_CONTAINER(vbox1), 2); + + hbox1 = gtk_hbox_new(FALSE, 8); + gtk_widget_show(hbox1); + gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, TRUE, 0); + + clist_hbox = gtk_hbox_new(FALSE, 8); + gtk_widget_show(clist_hbox); + gtk_box_pack_start(GTK_BOX(hbox1), clist_hbox, TRUE, TRUE, 0); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_set_size_request(scrolledwin, 180, 210); + gtk_widget_show(scrolledwin); + gtk_box_pack_start(GTK_BOX(clist_hbox), scrolledwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + title[0] = _("Available items"); + stock_clist = gtk_clist_new_with_titles(1, title); + gtk_widget_show(stock_clist); + gtk_container_add(GTK_CONTAINER(scrolledwin), stock_clist); + gtk_clist_set_selection_mode(GTK_CLIST(stock_clist), + GTK_SELECTION_BROWSE); + gtkut_clist_set_redraw(GTK_CLIST(stock_clist)); + GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(stock_clist)->column[0].button, + GTK_CAN_FOCUS); + + /* add/remove button */ + btn_vbox = gtk_vbox_new(FALSE, 0); + gtk_widget_show(btn_vbox); + gtk_box_pack_start(GTK_BOX(hbox1), btn_vbox, FALSE, FALSE, 0); + + btn_vbox1 = gtk_vbox_new(FALSE, 8); + gtk_widget_show(btn_vbox1); + gtk_box_pack_start(GTK_BOX(btn_vbox), btn_vbox1, TRUE, FALSE, 0); + + add_btn = gtk_button_new_with_label(_(" -> ")); + gtk_widget_show(add_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox1), add_btn, FALSE, FALSE, 0); + + remove_btn = gtk_button_new_with_label(_(" <- ")); + gtk_widget_show(remove_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox1), remove_btn, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(add_btn), "clicked", + G_CALLBACK(prefs_summary_column_add), NULL); + g_signal_connect(G_OBJECT(remove_btn), "clicked", + G_CALLBACK(prefs_summary_column_remove), NULL); + + clist_hbox = gtk_hbox_new(FALSE, 8); + gtk_widget_show(clist_hbox); + gtk_box_pack_start(GTK_BOX(hbox1), clist_hbox, TRUE, TRUE, 0); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_set_size_request(scrolledwin, 180, 210); + gtk_widget_show(scrolledwin); + gtk_box_pack_start(GTK_BOX(clist_hbox), scrolledwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + title[0] = _("Displayed items"); + shown_clist = gtk_clist_new_with_titles(1, title); + gtk_widget_show(shown_clist); + gtk_container_add(GTK_CONTAINER(scrolledwin), shown_clist); + gtk_clist_set_selection_mode(GTK_CLIST(shown_clist), + GTK_SELECTION_BROWSE); +#if 0 + gtk_clist_set_reorderable(GTK_CLIST(shown_clist), TRUE); + gtk_clist_set_use_drag_icons(GTK_CLIST(shown_clist), FALSE); +#endif + gtkut_clist_set_redraw(GTK_CLIST(shown_clist)); + GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(shown_clist)->column[0].button, + GTK_CAN_FOCUS); + + /* up/down button */ + btn_vbox = gtk_vbox_new(FALSE, 0); + gtk_widget_show(btn_vbox); + gtk_box_pack_start(GTK_BOX(hbox1), btn_vbox, FALSE, FALSE, 0); + + btn_vbox1 = gtk_vbox_new(FALSE, 8); + gtk_widget_show(btn_vbox1); + gtk_box_pack_start(GTK_BOX(btn_vbox), btn_vbox1, TRUE, FALSE, 0); + + up_btn = gtk_button_new_with_label(_("Up")); + gtk_widget_show(up_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox1), up_btn, FALSE, FALSE, 0); + + down_btn = gtk_button_new_with_label(_("Down")); + gtk_widget_show(down_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox1), down_btn, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(up_btn), "clicked", + G_CALLBACK(prefs_summary_column_up), NULL); + g_signal_connect(G_OBJECT(down_btn), "clicked", + G_CALLBACK(prefs_summary_column_down), NULL); + + btn_hbox = gtk_hbox_new(FALSE, 8); + gtk_widget_show(btn_hbox); + gtk_box_pack_end(GTK_BOX(vbox), btn_hbox, FALSE, FALSE, 0); + + btn_vbox = gtk_vbox_new(FALSE, 0); + gtk_widget_show(btn_vbox); + gtk_box_pack_start(GTK_BOX(btn_hbox), btn_vbox, FALSE, FALSE, 0); + + default_btn = gtk_button_new_with_label(_(" Revert to default ")); + gtk_widget_show(default_btn); + gtk_box_pack_start(GTK_BOX(btn_vbox), default_btn, TRUE, FALSE, 0); + g_signal_connect(G_OBJECT(default_btn), "clicked", + G_CALLBACK(prefs_summary_column_set_to_default), NULL); + + gtkut_stock_button_set_create(&confirm_area, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_widget_show(confirm_area); + gtk_box_pack_end(GTK_BOX(btn_hbox), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_btn); + + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(prefs_summary_column_ok), NULL); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(prefs_summary_column_cancel), NULL); + + summary_col.window = window; + summary_col.stock_clist = stock_clist; + summary_col.shown_clist = shown_clist; + summary_col.add_btn = add_btn; + summary_col.remove_btn = remove_btn; + summary_col.up_btn = up_btn; + summary_col.down_btn = down_btn; + summary_col.confirm_area = confirm_area; + summary_col.ok_btn = ok_btn; + summary_col.cancel_btn = cancel_btn; +} + +SummaryColumnState *prefs_summary_column_get_config(gboolean sent_folder) +{ + static SummaryColumnState state[N_SUMMARY_VISIBLE_COLS]; + SummaryColumnType type; + gboolean *col_visible; + gint *col_pos; + SummaryColumnState *def_state; + gint pos; + + debug_print("prefs_summary_column_get_config(): " + "getting %s folder setting\n", + sent_folder ? "sent" : "normal"); + + if (sent_folder) { + col_visible = prefs_common.summary_sent_col_visible; + col_pos = prefs_common.summary_sent_col_pos; + def_state = default_sent_state; + } else { + col_visible = prefs_common.summary_col_visible; + col_pos = prefs_common.summary_col_pos; + def_state = default_state; + } + + for (pos = 0; pos < N_SUMMARY_VISIBLE_COLS; pos++) + state[pos].type = -1; + + for (type = 0; type < N_SUMMARY_VISIBLE_COLS; type++) { + pos = col_pos[type]; + if (pos < 0 || pos >= N_SUMMARY_VISIBLE_COLS || + state[pos].type != -1) { + g_warning("Wrong column position\n"); + prefs_summary_column_set_config(def_state, sent_folder); + return def_state; + } + + state[pos].type = type; + state[pos].visible = col_visible[type]; + } + + return state; +} + +void prefs_summary_column_set_config(SummaryColumnState *state, + gboolean sent_folder) +{ + SummaryColumnType type; + gboolean *col_visible; + gint *col_pos; + gint pos; + + if (sent_folder) { + col_visible = prefs_common.summary_sent_col_visible; + col_pos = prefs_common.summary_sent_col_pos; + } else { + col_visible = prefs_common.summary_col_visible; + col_pos = prefs_common.summary_col_pos; + } + + for (pos = 0; pos < N_SUMMARY_VISIBLE_COLS; pos++) { + type = state[pos].type; + col_visible[type] = state[pos].visible; + col_pos[type] = pos; + } +} + +static void prefs_summary_column_set_dialog(SummaryColumnState *state) +{ + GtkCList *stock_clist = GTK_CLIST(summary_col.stock_clist); + GtkCList *shown_clist = GTK_CLIST(summary_col.shown_clist); + gint pos; + SummaryColumnType type; + gchar *name; + + gtk_clist_clear(stock_clist); + gtk_clist_clear(shown_clist); + + if (!state) + state = prefs_summary_column_get_config + (summary_col.sent_folder); + + for (pos = 0; pos < N_SUMMARY_VISIBLE_COLS; pos++) { + gint row; + type = state[pos].type; + name = gettext(col_name[type]); + + if (state[pos].visible) { + row = gtk_clist_append(shown_clist, (gchar **)&name); + gtk_clist_set_row_data(shown_clist, row, + GINT_TO_POINTER(type)); + } else { + row = gtk_clist_append(stock_clist, (gchar **)&name); + gtk_clist_set_row_data(stock_clist, row, + GINT_TO_POINTER(type)); + } + } +} + +static void prefs_summary_column_set_view(void) +{ + GtkCList *stock_clist = GTK_CLIST(summary_col.stock_clist); + GtkCList *shown_clist = GTK_CLIST(summary_col.shown_clist); + SummaryColumnState state[N_SUMMARY_VISIBLE_COLS]; + SummaryColumnType type; + gint row, pos = 0; + + g_return_if_fail(stock_clist->rows + shown_clist->rows == + N_SUMMARY_VISIBLE_COLS); + + for (row = 0; row < stock_clist->rows; row++) { + type = GPOINTER_TO_INT + (gtk_clist_get_row_data(stock_clist, row)); + state[row].type = type; + state[row].visible = FALSE; + } + + pos = row; + for (row = 0; row < shown_clist->rows; row++) { + type = GPOINTER_TO_INT + (gtk_clist_get_row_data(shown_clist, row)); + state[pos + row].type = type; + state[pos + row].visible = TRUE; + } + + prefs_summary_column_set_config(state, summary_col.sent_folder); + main_window_set_summary_column(); +} + +static void prefs_summary_column_add(void) +{ + GtkCList *stock_clist = GTK_CLIST(summary_col.stock_clist); + GtkCList *shown_clist = GTK_CLIST(summary_col.shown_clist); + gint row; + SummaryColumnType type; + gchar *name; + + if (!stock_clist->selection) return; + + row = GPOINTER_TO_INT(stock_clist->selection->data); + type = GPOINTER_TO_INT(gtk_clist_get_row_data(stock_clist, row)); + gtk_clist_remove(stock_clist, row); + if (stock_clist->rows == row) + gtk_clist_select_row(stock_clist, row - 1, -1); + + if (!shown_clist->selection) + row = 0; + else + row = GPOINTER_TO_INT(shown_clist->selection->data); + + name = gettext(col_name[type]); + row = gtk_clist_insert(shown_clist, row, (gchar **)&name); + gtk_clist_set_row_data(shown_clist, row, GINT_TO_POINTER(type)); +} + +static void prefs_summary_column_remove(void) +{ + GtkCList *stock_clist = GTK_CLIST(summary_col.stock_clist); + GtkCList *shown_clist = GTK_CLIST(summary_col.shown_clist); + gint row; + SummaryColumnType type; + gchar *name; + + if (!shown_clist->selection) return; + + row = GPOINTER_TO_INT(shown_clist->selection->data); + type = GPOINTER_TO_INT(gtk_clist_get_row_data(shown_clist, row)); + gtk_clist_remove(shown_clist, row); + if (shown_clist->rows == row) + gtk_clist_select_row(shown_clist, row - 1, -1); + + if (!stock_clist->selection) + row = 0; + else + row = GPOINTER_TO_INT(stock_clist->selection->data) + 1; + + name = gettext(col_name[type]); + row = gtk_clist_insert(stock_clist, row, (gchar **)&name); + gtk_clist_set_row_data(stock_clist, row, GINT_TO_POINTER(type)); + gtk_clist_select_row(stock_clist, row, -1); +} + +static void prefs_summary_column_up(void) +{ + GtkCList *clist = GTK_CLIST(summary_col.shown_clist); + gint row; + + if (!clist->selection) return; + + row = GPOINTER_TO_INT(clist->selection->data); + if (row > 0) + gtk_clist_row_move(clist, row, row - 1); +} + +static void prefs_summary_column_down(void) +{ + GtkCList *clist = GTK_CLIST(summary_col.shown_clist); + gint row; + + if (!clist->selection) return; + + row = GPOINTER_TO_INT(clist->selection->data); + if (row >= 0 && row < clist->rows - 1) + gtk_clist_row_move(clist, row, row + 1); +} + +static void prefs_summary_column_set_to_default(void) +{ + if (summary_col.sent_folder) + prefs_summary_column_set_dialog(default_sent_state); + else + prefs_summary_column_set_dialog(default_state); +} + +static void prefs_summary_column_ok(void) +{ + if (!summary_col.finished) { + summary_col.finished = TRUE; + prefs_summary_column_set_view(); + } +} + +static void prefs_summary_column_cancel(void) +{ + summary_col.finished = TRUE; +} + +static gint prefs_summary_column_delete_event(GtkWidget *widget, + GdkEventAny *event, + gpointer data) +{ + summary_col.finished = TRUE; + return TRUE; +} + +static gboolean prefs_summary_column_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + summary_col.finished = TRUE; + return FALSE; +} diff --git a/src/prefs_summary_column.h b/src/prefs_summary_column.h new file mode 100644 index 0000000..2a73a47 --- /dev/null +++ b/src/prefs_summary_column.h @@ -0,0 +1,31 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2001 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PREFS_SUMMARY_COLUMN_H__ +#define __PREFS_SUMMARY_COLUMN_H__ + +#include "summaryview.h" + +void prefs_summary_column_open(gboolean sent_folder); + +SummaryColumnState *prefs_summary_column_get_config(gboolean sent_folder); +void prefs_summary_column_set_config(SummaryColumnState *state, + gboolean sent_folder); + +#endif /* __PREFS_SUMMARY_COLUMN_H__ */ diff --git a/src/prefs_template.c b/src/prefs_template.c new file mode 100644 index 0000000..a6a0b4b --- /dev/null +++ b/src/prefs_template.c @@ -0,0 +1,685 @@ +/* + * Sylpheed templates subsystem + * Copyright (C) 2001 Alexander Barinov + * Copyright (C) 2001-2015 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "template.h" +#include "main.h" +#include "inc.h" +#include "utils.h" +#include "gtkutils.h" +#include "alertpanel.h" +#include "manage_window.h" +#include "prefs_common.h" +#include "prefs_common_dialog.h" +#include "compose.h" +#include "mainwindow.h" +#include "addr_compl.h" +#include "quote_fmt.h" +#include "plugin.h" + +static struct Templates { + GtkWidget *window; + GtkWidget *clist_tmpls; + GtkWidget *entry_name; + GtkWidget *entry_to; + GtkWidget *entry_cc; + GtkWidget *entry_bcc; + GtkWidget *entry_replyto; + GtkWidget *entry_subject; + GtkWidget *text_value; + GtkWidget *confirm_area; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + + gboolean entry_modified; + gboolean list_modified; +} templates; + +/* widget creating functions */ +static void prefs_template_window_create (void); +static void prefs_template_window_setup (void); +static void prefs_template_clear (void); + +static GSList *prefs_template_get_list (void); + +/* callbacks */ +static gint prefs_template_deleted_cb (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean prefs_template_key_pressed_cb (GtkWidget *widget, + GdkEventKey *event, + gpointer data); +static void prefs_template_changed_cb (GtkEditable *editable, + gpointer data); +static void prefs_template_cancel_cb (void); +static void prefs_template_ok_cb (void); +static void prefs_template_select_cb (GtkCList *clist, + gint row, + gint column, + GdkEvent *event); +static void prefs_template_register_cb (void); +static void prefs_template_substitute_cb (void); +static void prefs_template_delete_cb (void); +static void prefs_template_up_cb (void); +static void prefs_template_down_cb (void); + +/* Called from mainwindow.c */ +void prefs_template_open(void) +{ + inc_lock(); + + if (!templates.window) + prefs_template_window_create(); + + prefs_template_window_setup(); + gtk_widget_show(templates.window); + + syl_plugin_signal_emit("prefs-template-open", templates.window); +} + +#define ADD_ENTRY(entry, str, row) \ +{ \ + label1 = gtk_label_new(str); \ + gtk_widget_show(label1); \ + gtk_table_attach(GTK_TABLE(table), label1, 0, 1, row, (row + 1), \ + GTK_FILL, 0, 0, 0); \ + gtk_misc_set_alignment(GTK_MISC(label1), 1, 0.5); \ + \ + entry = gtk_entry_new(); \ + gtk_widget_show(entry); \ + gtk_table_attach(GTK_TABLE(table), entry, 1, 2, row, (row + 1), \ + GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \ + g_signal_connect(G_OBJECT(entry), "changed", \ + G_CALLBACK(prefs_template_changed_cb), NULL); \ +} + +static void prefs_template_window_create(void) +{ + /* window structure ;) */ + GtkWidget *window; + GtkWidget *vpaned; + GtkWidget *vbox1; + GtkWidget *hbox1; + GtkWidget *label1; + GtkWidget *entry_name; + GtkWidget *table; + GtkWidget *entry_to; + GtkWidget *entry_cc; + GtkWidget *entry_bcc; + GtkWidget *entry_replyto; + GtkWidget *entry_subject; + GtkWidget *scroll2; + GtkWidget *text_value; + GtkWidget *vbox2; + GtkWidget *hbox2; + GtkWidget *arrow1; + GtkWidget *hbox3; + GtkWidget *reg_btn; + GtkWidget *subst_btn; + GtkWidget *del_btn; + GtkWidget *desc_btn; + GtkWidget *hbox4; + GtkWidget *scroll1; + GtkWidget *clist_tmpls; + GtkWidget *vbox3; + GtkWidget *vbox4; + GtkWidget *up_btn; + GtkWidget *down_btn; + GtkWidget *confirm_area; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + + GtkTextBuffer *buffer; + + gchar *title[1]; + + /* main window */ + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, FALSE); + + /* vpaned to separate template settings from templates list */ + vpaned = gtk_vpaned_new(); + gtk_widget_show(vpaned); + gtk_container_add(GTK_CONTAINER(window), vpaned); + + /* vbox to handle template name and content */ + vbox1 = gtk_vbox_new(FALSE, 6); + gtk_widget_show(vbox1); + gtk_container_set_border_width(GTK_CONTAINER(vbox1), 8); + gtk_paned_pack1(GTK_PANED(vpaned), vbox1, FALSE, FALSE); + + hbox1 = gtk_hbox_new(FALSE, 8); + gtk_widget_show(hbox1); + gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, FALSE, 0); + + label1 = gtk_label_new(_("Template name")); + gtk_widget_show(label1); + gtk_box_pack_start(GTK_BOX(hbox1), label1, FALSE, FALSE, 0); + + entry_name = gtk_entry_new(); + gtk_widget_show(entry_name); + gtk_box_pack_start(GTK_BOX(hbox1), entry_name, TRUE, TRUE, 0); + g_signal_connect(G_OBJECT(entry_name), "changed", + G_CALLBACK(prefs_template_changed_cb), NULL); + + /* table for headers */ + table = gtk_table_new(5, 2, FALSE); + gtk_widget_show(table); + gtk_box_pack_start(GTK_BOX(vbox1), table, FALSE, FALSE, 0); + gtk_table_set_row_spacings(GTK_TABLE(table), 4); + gtk_table_set_col_spacings(GTK_TABLE(table), 4); + + ADD_ENTRY(entry_to, _("To:"), 0); + address_completion_register_entry(GTK_ENTRY(entry_to)); + ADD_ENTRY(entry_cc, _("Cc:"), 1); + address_completion_register_entry(GTK_ENTRY(entry_cc)); + ADD_ENTRY(entry_bcc, _("Bcc:"), 2); + address_completion_register_entry(GTK_ENTRY(entry_bcc)); + ADD_ENTRY(entry_replyto, _("Reply-To:"), 3); + address_completion_register_entry(GTK_ENTRY(entry_replyto)); + ADD_ENTRY(entry_subject, _("Subject:"), 4); + +#undef ADD_ENTRY + + /* template content */ + scroll2 = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(scroll2); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll2), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll2), + GTK_SHADOW_IN); + gtk_box_pack_start(GTK_BOX(vbox1), scroll2, TRUE, TRUE, 0); + + text_value = gtk_text_view_new(); + gtk_widget_show(text_value); + gtk_widget_set_size_request + (text_value, + 360 * gtkut_get_dpi_multiplier(), + 120 * gtkut_get_dpi_multiplier()); + gtk_container_add(GTK_CONTAINER(scroll2), text_value); + gtk_text_view_set_editable(GTK_TEXT_VIEW(text_value), TRUE); + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_value)); + g_signal_connect(G_OBJECT(buffer), "changed", + G_CALLBACK(prefs_template_changed_cb), NULL); + + /* vbox for buttons and templates list */ + vbox2 = gtk_vbox_new(FALSE, 6); + gtk_widget_show(vbox2); + gtk_container_set_border_width(GTK_CONTAINER(vbox2), 8); + gtk_paned_pack2(GTK_PANED(vpaned), vbox2, TRUE, FALSE); + + /* register | substitute | delete */ + hbox2 = gtk_hbox_new(FALSE, 4); + gtk_widget_show(hbox2); + gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, 0); + + arrow1 = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT); + gtk_widget_show(arrow1); + gtk_box_pack_start(GTK_BOX(hbox2), arrow1, FALSE, FALSE, 0); + gtk_widget_set_size_request(arrow1, -1, + 16 * gtkut_get_dpi_multiplier()); + + hbox3 = gtk_hbox_new(TRUE, 4); + gtk_widget_show(hbox3); + gtk_box_pack_start(GTK_BOX(hbox2), hbox3, FALSE, FALSE, 0); + + reg_btn = gtk_button_new_with_label(_("Register")); + gtk_widget_show(reg_btn); + gtk_box_pack_start(GTK_BOX(hbox3), reg_btn, FALSE, TRUE, 0); + g_signal_connect(G_OBJECT (reg_btn), "clicked", + G_CALLBACK (prefs_template_register_cb), NULL); + + subst_btn = gtk_button_new_with_label(_(" Substitute ")); + gtk_widget_show(subst_btn); + gtk_box_pack_start(GTK_BOX(hbox3), subst_btn, FALSE, TRUE, 0); + g_signal_connect(G_OBJECT(subst_btn), "clicked", + G_CALLBACK(prefs_template_substitute_cb), NULL); + + del_btn = gtk_button_new_with_label(_("Delete")); + gtk_widget_show(del_btn); + gtk_box_pack_start(GTK_BOX(hbox3), del_btn, FALSE, TRUE, 0); + g_signal_connect(G_OBJECT(del_btn), "clicked", + G_CALLBACK(prefs_template_delete_cb), NULL); + + desc_btn = gtk_button_new_with_label(_(" Symbols ")); + gtk_widget_show(desc_btn); + gtk_box_pack_end(GTK_BOX(hbox2), desc_btn, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(desc_btn), "clicked", + G_CALLBACK(prefs_quote_description), NULL); + + /* templates list */ + hbox4 = gtk_hbox_new(FALSE, 8); + gtk_widget_show(hbox4); + gtk_box_pack_start(GTK_BOX(vbox2), hbox4, TRUE, TRUE, 0); + + scroll1 = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(scroll1); + gtk_box_pack_start(GTK_BOX(hbox4), scroll1, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll1), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + title[0] = _("Registered templates"); + clist_tmpls = gtk_clist_new_with_titles(1, title); + gtk_widget_show(clist_tmpls); + gtk_widget_set_size_request(scroll1, -1, + 160 * gtkut_get_dpi_multiplier()); + gtk_container_add(GTK_CONTAINER(scroll1), clist_tmpls); + gtk_clist_set_column_width(GTK_CLIST(clist_tmpls), 0, 80); + gtk_clist_set_selection_mode(GTK_CLIST(clist_tmpls), + GTK_SELECTION_BROWSE); + gtkut_clist_set_redraw(GTK_CLIST(clist_tmpls)); + GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist_tmpls)->column[0].button, + GTK_CAN_FOCUS); + g_signal_connect(G_OBJECT (clist_tmpls), "select_row", + G_CALLBACK (prefs_template_select_cb), NULL); + + vbox3 = gtk_vbox_new(TRUE, 0); + gtk_widget_show(vbox3); + gtk_box_pack_start(GTK_BOX(hbox4), vbox3, FALSE, FALSE, 0); + + vbox4 = gtk_vbox_new(TRUE, 8); + gtk_widget_show(vbox4); + gtk_box_pack_start(GTK_BOX(vbox3), vbox4, TRUE, FALSE, 0); + + up_btn = gtk_button_new_with_label(_("Up")); + gtk_widget_show(up_btn); + gtk_box_pack_start(GTK_BOX(vbox4), up_btn, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT (up_btn), "clicked", + G_CALLBACK (prefs_template_up_cb), NULL); + + down_btn = gtk_button_new_with_label(_("Down")); + gtk_widget_show(down_btn); + gtk_box_pack_start(GTK_BOX(vbox4), down_btn, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT (down_btn), "clicked", + G_CALLBACK (prefs_template_down_cb), NULL); + + /* ok | cancel */ + gtkut_stock_button_set_create(&confirm_area, &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_widget_show(confirm_area); + gtk_box_pack_end(GTK_BOX(vbox2), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_btn); + + gtk_window_set_title(GTK_WINDOW(window), _("Templates")); + + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(prefs_template_deleted_cb), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(prefs_template_key_pressed_cb), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(prefs_template_ok_cb), NULL); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(prefs_template_cancel_cb), NULL); + + address_completion_start(window); + + templates.window = window; + templates.clist_tmpls = clist_tmpls; + templates.entry_name = entry_name; + templates.entry_to = entry_to; + templates.entry_cc = entry_cc; + templates.entry_bcc = entry_bcc; + templates.entry_replyto = entry_replyto; + templates.entry_subject = entry_subject; + templates.text_value = text_value; + templates.confirm_area = confirm_area; + templates.ok_btn = ok_btn; + templates.cancel_btn = cancel_btn; + templates.entry_modified = FALSE; + templates.list_modified = FALSE; +} + +static void prefs_template_window_setup(void) +{ + GtkCList *clist = GTK_CLIST(templates.clist_tmpls); + GSList *tmpl_list; + GSList *cur; + gchar *title[1]; + gint row; + Template *tmpl; + + gtkut_box_set_reverse_order(GTK_BOX(templates.confirm_area), + !prefs_common.comply_gnome_hig); + manage_window_set_transient(GTK_WINDOW(templates.window)); + gtk_widget_grab_focus(templates.ok_btn); + + gtk_clist_freeze(clist); + gtk_clist_clear(clist); + + title[0] = _("(New)"); + row = gtk_clist_append(clist, title); + gtk_clist_set_row_data(clist, row, NULL); + + tmpl_list = template_read_config(); + + for (cur = tmpl_list; cur != NULL; cur = cur->next) { + tmpl = (Template *)cur->data; + title[0] = tmpl->name; + row = gtk_clist_append(clist, title); + gtk_clist_set_row_data(clist, row, tmpl); + } + + g_slist_free(tmpl_list); + + gtk_clist_thaw(clist); + + templates.entry_modified = FALSE; + templates.list_modified = FALSE; +} + +static void prefs_template_clear(void) +{ + Template *tmpl; + gint row = 1; + + while ((tmpl = gtk_clist_get_row_data + (GTK_CLIST(templates.clist_tmpls), row)) != NULL) { + template_free(tmpl); + row++; + } + + gtk_clist_clear(GTK_CLIST(templates.clist_tmpls)); +} + +static gint prefs_template_deleted_cb(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + prefs_template_cancel_cb(); + return TRUE; +} + +static gboolean prefs_template_key_pressed_cb(GtkWidget *widget, + GdkEventKey *event, gpointer data) +{ + if (event && event->keyval == GDK_Escape) + prefs_template_cancel_cb(); + return FALSE; +} + +static void prefs_template_changed_cb(GtkEditable *editable, gpointer data) +{ + templates.entry_modified = TRUE; +} + +static void prefs_template_ok_cb(void) +{ + GSList *tmpl_list; + + if (templates.entry_modified) { + if (alertpanel(_("Template is modified"), + _("Current modification is not applied. Finish without saving it?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL) + != G_ALERTDEFAULT) + return; + } + + tmpl_list = prefs_template_get_list(); + template_set_config(tmpl_list); + compose_reflect_prefs_all(); + gtk_clist_clear(GTK_CLIST(templates.clist_tmpls)); + gtk_widget_hide(templates.window); + main_window_popup(main_window_get()); + inc_unlock(); +} + +static void prefs_template_cancel_cb(void) +{ + if (templates.entry_modified || templates.list_modified) { + if (alertpanel(_("Templates are modified"), + _("Really discard modification to templates?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL) + != G_ALERTDEFAULT) + return; + } + + prefs_template_clear(); + gtk_widget_hide(templates.window); + main_window_popup(main_window_get()); + inc_unlock(); +} + +static void prefs_template_select_cb(GtkCList *clist, gint row, gint column, + GdkEvent *event) +{ + Template *tmpl; + Template tmpl_def; + GtkTextBuffer *buffer; + GtkTextIter iter; + + tmpl_def.name = _("Template"); + tmpl_def.subject = ""; + tmpl_def.to = ""; + tmpl_def.cc = ""; + tmpl_def.bcc = ""; + tmpl_def.replyto = ""; + tmpl_def.value = ""; + + if (!(tmpl = gtk_clist_get_row_data(clist, row))) + tmpl = &tmpl_def; + + gtk_entry_set_text(GTK_ENTRY(templates.entry_name), tmpl->name); + gtk_entry_set_text(GTK_ENTRY(templates.entry_to), + tmpl->to ? tmpl->to : ""); + gtk_entry_set_text(GTK_ENTRY(templates.entry_cc), + tmpl->cc ? tmpl->cc : ""); + gtk_entry_set_text(GTK_ENTRY(templates.entry_bcc), + tmpl->bcc ? tmpl->bcc : ""); + gtk_entry_set_text(GTK_ENTRY(templates.entry_replyto), + tmpl->replyto ? tmpl->replyto : ""); + gtk_entry_set_text(GTK_ENTRY(templates.entry_subject), + tmpl->subject ? tmpl->subject : ""); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(templates.text_value)); + gtk_text_buffer_set_text(buffer, "", 0); + gtk_text_buffer_get_start_iter(buffer, &iter); + gtk_text_buffer_insert(buffer, &iter, tmpl->value, -1); + + templates.entry_modified = FALSE; +} + +static GSList *prefs_template_get_list(void) +{ + gint row = 1; + GSList *tmpl_list = NULL; + Template *tmpl; + + while ((tmpl = gtk_clist_get_row_data + (GTK_CLIST(templates.clist_tmpls), row)) != NULL) { + tmpl->tmplid = row; + tmpl_list = g_slist_append(tmpl_list, tmpl); + row++; + } + + return tmpl_list; +} + +static gint prefs_template_clist_set_row(gint row) +{ + GtkCList *clist = GTK_CLIST(templates.clist_tmpls); + Template *tmpl; + Template *tmp_tmpl; + GtkTextBuffer *buffer; + GtkTextIter start, end; + gchar *name; + gchar *to; + gchar *cc; + gchar *bcc; + gchar *replyto; + gchar *subject; + gchar *value; + gchar *title[1]; + + g_return_val_if_fail(row != 0, -1); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(templates.text_value)); + gtk_text_buffer_get_start_iter(buffer, &start); + gtk_text_buffer_get_end_iter(buffer, &end); + value = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); + + if (value && *value != '\0') { + gchar *parsed_buf; + MsgInfo dummyinfo; + + memset(&dummyinfo, 0, sizeof(MsgInfo)); + quote_fmt_init(&dummyinfo, NULL, NULL); + quote_fmt_scan_string(value); + quote_fmt_parse(); + parsed_buf = quote_fmt_get_buffer(); + if (!parsed_buf) { + alertpanel_error(_("Template format error.")); + g_free(value); + return -1; + } + } + + name = gtk_editable_get_chars(GTK_EDITABLE(templates.entry_name), + 0, -1); + to = gtk_editable_get_chars(GTK_EDITABLE(templates.entry_to), 0, -1); + cc = gtk_editable_get_chars(GTK_EDITABLE(templates.entry_cc), 0, -1); + bcc = gtk_editable_get_chars(GTK_EDITABLE(templates.entry_bcc), 0, -1); + replyto = gtk_editable_get_chars(GTK_EDITABLE(templates.entry_replyto), + 0, -1); + subject = gtk_editable_get_chars(GTK_EDITABLE(templates.entry_subject), + 0, -1); + +#define NULLIFY_IF_EMPTY(val) \ + if (val && *val == '\0') { \ + g_free(val); \ + val = NULL; \ + } + + NULLIFY_IF_EMPTY(to); + NULLIFY_IF_EMPTY(cc); + NULLIFY_IF_EMPTY(bcc); + NULLIFY_IF_EMPTY(replyto); + NULLIFY_IF_EMPTY(subject); + +#undef NULLIFY_IF_EMPTY + + tmpl = g_new(Template, 1); + tmpl->name = name; + tmpl->to = to; + tmpl->cc = cc; + tmpl->bcc = bcc; + tmpl->replyto = replyto; + tmpl->subject = subject; + tmpl->value = value; + + title[0] = name; + + if (row < 0) { + row = gtk_clist_append(clist, title); + } else { + gtk_clist_set_text(clist, row, 0, name); + tmp_tmpl = gtk_clist_get_row_data(clist, row); + if (tmp_tmpl) + template_free(tmp_tmpl); + } + + gtk_clist_set_row_data(clist, row, tmpl); + templates.entry_modified = FALSE; + templates.list_modified = TRUE; + + return row; +} + +static void prefs_template_register_cb(void) +{ + prefs_template_clist_set_row(-1); +} + +static void prefs_template_substitute_cb(void) +{ + GtkCList *clist = GTK_CLIST(templates.clist_tmpls); + Template *tmpl; + gint row; + + if (!clist->selection) return; + + row = GPOINTER_TO_INT(clist->selection->data); + if (row == 0) return; + + tmpl = gtk_clist_get_row_data(clist, row); + if (!tmpl) return; + + prefs_template_clist_set_row(row); +} + +static void prefs_template_delete_cb(void) +{ + GtkCList *clist = GTK_CLIST(templates.clist_tmpls); + Template *tmpl; + gint row; + + if (!clist->selection) return; + row = GPOINTER_TO_INT(clist->selection->data); + if (row == 0) return; + + if (alertpanel(_("Delete template"), + _("Do you really want to delete this template?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL) != G_ALERTDEFAULT) + return; + + tmpl = gtk_clist_get_row_data(clist, row); + template_free(tmpl); + gtk_clist_remove(clist, row); + templates.list_modified = TRUE; +} + +static void prefs_template_up_cb(void) +{ + GtkCList *clist = GTK_CLIST(templates.clist_tmpls); + gint row; + + if (!clist->selection) return; + row = GPOINTER_TO_INT(clist->selection->data); + if (row > 1) { + gtk_clist_row_move(clist, row, row - 1); + templates.list_modified = TRUE; + } +} + +static void prefs_template_down_cb(void) +{ + GtkCList *clist = GTK_CLIST(templates.clist_tmpls); + gint row; + + if (!clist->selection) return; + row = GPOINTER_TO_INT(clist->selection->data); + if (row > 0 && row < clist->rows - 1) { + gtk_clist_row_move(clist, row, row + 1); + templates.list_modified = TRUE; + } +} diff --git a/src/prefs_template.h b/src/prefs_template.h new file mode 100644 index 0000000..c9e9551 --- /dev/null +++ b/src/prefs_template.h @@ -0,0 +1,25 @@ +/* + * Sylpheed templates subsystem + * Copyright (C) 2001 Alexander Barinov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PREFS_TEMPLATES_H__ +#define __PREFS_TEMPLATES_H__ + +void prefs_template_open(void); + +#endif /* __PREFS_TEMPLATES_H__ */ diff --git a/src/prefs_toolbar.c b/src/prefs_toolbar.c new file mode 100644 index 0000000..1aad751 --- /dev/null +++ b/src/prefs_toolbar.c @@ -0,0 +1,409 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2008 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include + +#include + +#include "prefs_toolbar.h" +#include "prefs_display_items.h" + +static PrefsDisplayItem all_items[] = +{ + {T_SEPARATOR, "separator", N_("---- Separator ----"), + NULL, 0, NULL, TRUE, FALSE}, + {T_GET, "get", N_("Get"), + N_("Incorporate new mail"), + STOCK_PIXMAP_MAIL_RECEIVE, NULL, FALSE, FALSE}, + {T_GET_ALL, "get-all", N_("Get all"), + N_("Incorporate new mail of all accounts"), + STOCK_PIXMAP_MAIL_RECEIVE_ALL, NULL, FALSE, FALSE}, + {T_REMOTE_MAILBOX, "remote-mailbox", N_("Remote mailbox"), + N_("POP3 Remote mailbox"), + STOCK_PIXMAP_REMOTE_MAILBOX, GTK_STOCK_NETWORK, FALSE, FALSE}, + {T_SEND_QUEUE, "send-queue", N_("Send"), + N_("Send queued message(s)"), + STOCK_PIXMAP_MAIL_SEND, NULL, FALSE, FALSE}, + {T_COMPOSE, "compose", N_("Compose"), + N_("Compose new message"), + STOCK_PIXMAP_MAIL_COMPOSE, NULL, FALSE, FALSE}, + {T_REPLY, "reply", N_("Reply"), + N_("Reply to the message"), + STOCK_PIXMAP_MAIL_REPLY, NULL, FALSE, FALSE}, + {T_REPLY_ALL, "reply-all", N_("Reply all"), + N_("Reply to all"), + STOCK_PIXMAP_MAIL_REPLY_TO_ALL, NULL, FALSE, FALSE}, + {T_FORWARD, "forward", N_("Forward"), + N_("Forward the message"), + STOCK_PIXMAP_MAIL_FORWARD, NULL, FALSE, FALSE}, + {T_DELETE, "delete", N_("Delete"), + N_("Delete the message"), + STOCK_PIXMAP_DELETE, NULL, FALSE, FALSE}, + {T_JUNK, "junk", N_("Junk"), + N_("Set as junk mail"), + STOCK_PIXMAP_SPAM, NULL, FALSE, FALSE}, + {T_NOTJUNK, "not-junk", N_("Not junk"), + N_("Set as not junk mail"), + STOCK_PIXMAP_NOTSPAM, NULL, FALSE, FALSE}, + {T_NEXT, "next", N_("Next"), + N_("Next unread message"), + STOCK_PIXMAP_NEXT, GTK_STOCK_GO_DOWN, FALSE, FALSE}, + {T_PREV, "prev", N_("Prev"), + N_("Previous unread message"), + STOCK_PIXMAP_PREV, GTK_STOCK_GO_UP, FALSE, FALSE}, + {T_SEARCH, "search", N_("Search"), + N_("Search messages"), + STOCK_PIXMAP_SEARCH, GTK_STOCK_FIND, FALSE, FALSE}, + {T_PRINT, "print", N_("Print"), + N_("Print message"), + STOCK_PIXMAP_PRINT, GTK_STOCK_PRINT, FALSE, FALSE}, + {T_STOP, "stop", N_("Stop"), + N_("Stop receiving"), + STOCK_PIXMAP_STOP, GTK_STOCK_STOP, FALSE, FALSE}, + {T_ADDRESS_BOOK, "address-book", N_("Address"), + N_("Address book"), + STOCK_PIXMAP_ADDRESS_BOOK, NULL, FALSE, FALSE}, + {T_EXECUTE, "execute", N_("Execute"), + N_("Execute marked process"), + STOCK_PIXMAP_EXECUTE, GTK_STOCK_EXECUTE, FALSE, FALSE}, + {T_COMMON_PREFS, "common-prefs", N_("Prefs"), + N_("Common preferences"), + STOCK_PIXMAP_COMMON_PREFS, GTK_STOCK_PREFERENCES, FALSE, FALSE}, + {T_ACCOUNT_PREFS, "account-prefs", N_("Account"), + N_("Account preferences"), + STOCK_PIXMAP_ACCOUNT_PREFS, GTK_STOCK_PREFERENCES, FALSE, FALSE}, + + {T_SEND, "send", N_("Send"), + N_("Send message"), + STOCK_PIXMAP_MAIL_SEND, NULL, FALSE, FALSE}, + {T_SEND_LATER, "send-later", N_("Send later"), + N_("Put into queue folder and send later"), + STOCK_PIXMAP_MAIL_SEND_QUEUE, NULL, FALSE, FALSE}, + {T_DRAFT, "draft", N_("Draft"), + N_("Save to draft folder"), + STOCK_PIXMAP_SAVE, GTK_STOCK_SAVE, FALSE, FALSE}, + {T_INSERT_FILE, "insert-file", N_("Insert"), + N_("Insert file"), + STOCK_PIXMAP_INSERT_FILE, NULL, FALSE, FALSE}, + {T_ATTACH_FILE, "attach-file", N_("Attach"), + N_("Attach file"), + STOCK_PIXMAP_MAIL_ATTACH, NULL, FALSE, FALSE}, + {T_SIGNATURE, "signature", N_("Signature"), + N_("Append signature"), + STOCK_PIXMAP_SIGN, NULL, FALSE, FALSE}, + {T_EDITOR, "editor", N_("Editor"), + N_("Edit with external editor"), + STOCK_PIXMAP_EDIT, GTK_STOCK_EDIT, FALSE, FALSE}, + {T_LINEWRAP, "linewrap", N_("Linewrap"), + N_("Wrap all long lines"), + STOCK_PIXMAP_LINEWRAP, NULL, FALSE, FALSE}, + + {-1, NULL, NULL, FALSE, FALSE} +}; + +static gint main_available_items[] = +{ + T_SEPARATOR, + T_GET, + T_GET_ALL, + T_REMOTE_MAILBOX, + T_SEND_QUEUE, + T_COMPOSE, + T_REPLY, + T_REPLY_ALL, + T_FORWARD, + T_DELETE, + T_JUNK, + T_NOTJUNK, + T_NEXT, + T_PREV, + T_SEARCH, + T_PRINT, + T_STOP, + T_ADDRESS_BOOK, + T_EXECUTE, + T_COMMON_PREFS, + T_ACCOUNT_PREFS, + -1 +}; + +static gint compose_available_items[] = +{ + T_SEPARATOR, + T_SEND, + T_SEND_LATER, + T_DRAFT, + T_INSERT_FILE, + T_ATTACH_FILE, + T_SIGNATURE, + T_EDITOR, + T_LINEWRAP, + T_ADDRESS_BOOK, + T_COMMON_PREFS, + T_ACCOUNT_PREFS, + -1 +}; + +static gint default_main_items[] = +{ + T_GET, + T_GET_ALL, + T_SEPARATOR, + T_SEND_QUEUE, + T_SEPARATOR, + T_COMPOSE, + T_REPLY, + T_REPLY_ALL, + T_FORWARD, + T_SEPARATOR, + T_DELETE, + T_JUNK, + T_SEPARATOR, + T_NEXT, + T_SEPARATOR, + T_SEARCH, + T_PRINT, + T_ADDRESS_BOOK, + -1 +}; + +static gint default_compose_items[] = +{ + T_SEND, + T_SEND_LATER, + T_DRAFT, + T_SEPARATOR, + T_INSERT_FILE, + T_ATTACH_FILE, + T_SEPARATOR, + T_SIGNATURE, + T_SEPARATOR, + T_EDITOR, + T_LINEWRAP, + T_SEPARATOR, + T_ADDRESS_BOOK, + -1 +}; + +gint prefs_toolbar_open(ToolbarType type, gint *visible_items, + GList **item_list) +{ + PrefsDisplayItemsDialog *dialog; + GList *list; + gint ret = 0; + + dialog = prefs_display_items_dialog_create(); + gtk_window_set_title(GTK_WINDOW(dialog->window), + _("Customize toolbar")); + gtk_label_set_text(GTK_LABEL(dialog->label), + _("Select items to be displayed on the toolbar. You can modify\n" + "the order by using the Up / Down button.")); + + switch (type) { + case TOOLBAR_MAIN: + prefs_display_items_dialog_set_available + (dialog, all_items, main_available_items); + prefs_display_items_dialog_set_default_visible + (dialog, default_main_items); + break; + case TOOLBAR_COMPOSE: + default: + prefs_display_items_dialog_set_available + (dialog, all_items, compose_available_items); + prefs_display_items_dialog_set_default_visible + (dialog, default_compose_items); + break; + } + + prefs_display_items_dialog_set_visible(dialog, visible_items); + + gtk_widget_show(dialog->window); + + while (dialog->finished == FALSE) + gtk_main_iteration(); + + if (dialog->cancelled) { + ret = -1; + *item_list = NULL; + } else { + list = dialog->visible_items; + dialog->visible_items = NULL; + *item_list = list; + } + + prefs_display_items_dialog_destroy(dialog); + + return ret; +} + +const PrefsDisplayItem *prefs_toolbar_get_item_from_name(const gchar *name) +{ + gint i; + + for (i = 0; all_items[i].id != -1; i++) { + if (!strcmp(name, all_items[i].name)) + return &all_items[i]; + } + + return NULL; +} + +const PrefsDisplayItem *prefs_toolbar_get_item_from_id(gint id) +{ + gint i; + + for (i = 0; all_items[i].id != -1; i++) { + if (id == all_items[i].id) + return &all_items[i]; + } + + return NULL; +} + +GList *prefs_toolbar_get_item_list_from_name_list(const gchar *name_list) +{ + gint i; + gchar **array; + GList *list = NULL; + + array = g_strsplit(name_list, ",", 0); + + for (i = 0; array[i] != NULL; i++) { + gchar *name = array[i]; + const PrefsDisplayItem *item; + + g_strstrip(name); + item = prefs_toolbar_get_item_from_name(name); + if (item) + list = g_list_append(list, (gpointer)item); + } + + g_strfreev(array); + + return list; +} + +gint *prefs_toolbar_get_id_list_from_name_list(const gchar *name_list) +{ + gint i; + gchar **array; + GArray *iarray; + + iarray = g_array_new(FALSE, FALSE, sizeof(gint)); + array = g_strsplit(name_list, ",", 0); + + for (i = 0; array[i] != NULL; i++) { + gchar *name = array[i]; + const PrefsDisplayItem *item; + + g_strstrip(name); + item = prefs_toolbar_get_item_from_name(name); + if (item) + g_array_append_val(iarray, item->id); + } + + i = -1; + g_array_append_val(iarray, i); + + g_strfreev(array); + + return (gint *)g_array_free(iarray, FALSE); +} + +gchar *prefs_toolbar_get_name_list_from_item_list(GList *item_list) +{ + GString *str; + GList *cur; + + str = g_string_new(NULL); + + for (cur = item_list; cur != NULL; cur = cur->next) { + const PrefsDisplayItem *item = cur->data; + + g_string_append(str, item->name); + if (cur->next) + g_string_append_c(str, ','); + } + + return g_string_free(str, FALSE); +} + +const gchar *prefs_toolbar_get_default_main_setting_name_list(void) +{ + GString *str; + gint i; + static gchar *default_name_list = NULL; + + if (default_name_list) + return default_name_list; + + str = g_string_new(NULL); + + for (i = 0; default_main_items[i] != -1; i++) { + const PrefsDisplayItem *item; + + item = prefs_toolbar_get_item_from_id(default_main_items[i]); + if (item) { + g_string_append(str, item->name); + if (default_main_items[i + 1] != -1) + g_string_append_c(str, ','); + } + } + + default_name_list = g_string_free(str, FALSE); + + return default_name_list; +} + +const gchar *prefs_toolbar_get_default_compose_setting_name_list(void) +{ + GString *str; + gint i; + static gchar *default_name_list = NULL; + + if (default_name_list) + return default_name_list; + + str = g_string_new(NULL); + + for (i = 0; default_compose_items[i] != -1; i++) { + const PrefsDisplayItem *item; + + item = prefs_toolbar_get_item_from_id(default_compose_items[i]); + if (item) { + g_string_append(str, item->name); + if (default_compose_items[i + 1] != -1) + g_string_append_c(str, ','); + } + } + + default_name_list = g_string_free(str, FALSE); + + return default_name_list; +} diff --git a/src/prefs_toolbar.h b/src/prefs_toolbar.h new file mode 100644 index 0000000..5625417 --- /dev/null +++ b/src/prefs_toolbar.h @@ -0,0 +1,99 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2008 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PREFS_TOOLBAR_H__ +#define __PREFS_TOOLBAR_H__ + +#include +#include + +typedef struct _PrefsToolbarItem PrefsToolbarItem; + +#include "prefs_display_items.h" + +typedef enum +{ + TOOLBAR_MAIN, + TOOLBAR_COMPOSE +} ToolbarType; + +typedef enum +{ + T_SEPARATOR, + + /* Main */ + T_GET, + T_GET_ALL, + T_REMOTE_MAILBOX, + T_SEND_QUEUE, + T_COMPOSE, + T_REPLY, + T_REPLY_ALL, + T_FORWARD, + T_DELETE, + T_JUNK, + T_NOTJUNK, + T_EXECUTE, + T_NEXT, + T_PREV, + T_ADDRESS_BOOK, + T_SEARCH, + T_PRINT, + T_STOP, + T_COMMON_PREFS, + T_ACCOUNT_PREFS, + + /* Compose */ + T_SEND, + T_SEND_LATER, + T_DRAFT, + T_INSERT_FILE, + T_ATTACH_FILE, + T_SIGNATURE, + T_EDITOR, + T_LINEWRAP +} ToolbarItems; + +struct _PrefsToolbarItem +{ + gint id; + gboolean is_important; + void (*callback) (GtkWidget *widget, gpointer data); + gpointer data; +}; + +gint prefs_toolbar_open (ToolbarType type, + gint *visible_items, + GList **item_list); + +const PrefsDisplayItem *prefs_toolbar_get_item_from_name + (const gchar *name); +const PrefsDisplayItem *prefs_toolbar_get_item_from_id (gint id); + +GList *prefs_toolbar_get_item_list_from_name_list + (const gchar *name_list); +gint *prefs_toolbar_get_id_list_from_name_list + (const gchar *name_list); + +gchar *prefs_toolbar_get_name_list_from_item_list (GList *item_list); + +const gchar *prefs_toolbar_get_default_main_setting_name_list (void); +const gchar *prefs_toolbar_get_default_compose_setting_name_list(void); + +#endif /* __PREFS_TOOLBAR_H__ */ diff --git a/src/prefs_ui.c b/src/prefs_ui.c new file mode 100644 index 0000000..a3a8f74 --- /dev/null +++ b/src/prefs_ui.c @@ -0,0 +1,578 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2017 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prefs.h" +#include "prefs_ui.h" +#include "menu.h" +#include "codeconv.h" +#include "utils.h" +#include "gtkutils.h" + +typedef enum +{ + DUMMY_PARAM +} DummyEnum; + +void prefs_dialog_create(PrefsDialog *dialog) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *notebook; + + GtkWidget *confirm_area; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *apply_btn; + + g_return_if_fail(dialog != NULL); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (window), 6); + gtk_window_set_position (GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal (GTK_WINDOW (window), TRUE); + gtk_window_set_policy (GTK_WINDOW(window), FALSE, TRUE, FALSE); + + vbox = gtk_vbox_new (FALSE, 6); + gtk_widget_show(vbox); + gtk_container_add (GTK_CONTAINER (window), vbox); + + notebook = gtk_notebook_new (); + gtk_widget_show(notebook); + gtk_box_pack_start (GTK_BOX (vbox), notebook, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (notebook), 2); + /* GTK_WIDGET_UNSET_FLAGS (notebook, GTK_CAN_FOCUS); */ + gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE); + + gtkut_stock_button_set_create(&confirm_area, + &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + &apply_btn, GTK_STOCK_APPLY); + gtk_widget_show(confirm_area); + gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0); + gtk_widget_grab_default(ok_btn); + + dialog->window = window; + dialog->notebook = notebook; + dialog->confirm_area = confirm_area; + dialog->ok_btn = ok_btn; + dialog->cancel_btn = cancel_btn; + dialog->apply_btn = apply_btn; +} + +void prefs_dialog_destroy(PrefsDialog *dialog) +{ + gtk_widget_destroy(dialog->window); + dialog->window = NULL; + dialog->notebook = NULL; + dialog->ok_btn = NULL; + dialog->cancel_btn = NULL; + dialog->apply_btn = NULL; +} + +void prefs_button_toggled(GtkToggleButton *toggle_btn, GtkWidget *widget) +{ + gboolean is_active; + + is_active = gtk_toggle_button_get_active(toggle_btn); + gtk_widget_set_sensitive(widget, is_active); +} + +void prefs_button_toggled_rev(GtkToggleButton *toggle_btn, GtkWidget *widget) +{ + gboolean is_active; + + is_active = gtk_toggle_button_get_active(toggle_btn); + gtk_widget_set_sensitive(widget, !is_active); +} + +void prefs_register_ui(PrefParam *param, PrefsUIData *ui_data) +{ + GHashTable *param_table; + PrefParam *param_; + gint i; + + param_table = prefs_param_table_get(param); + + for (i = 0; ui_data[i].name != NULL; i++) { + param_ = g_hash_table_lookup(param_table, ui_data[i].name); + if (param_) { + param_->ui_data = &ui_data[i]; + } + } + + prefs_param_table_destroy(param_table); +} + +void prefs_set_dialog(PrefParam *param) +{ + PrefsUIData *ui_data; + gint i; + + for (i = 0; param[i].name != NULL; i++) { + ui_data = (PrefsUIData *)param[i].ui_data; + if (ui_data && ui_data->widget_set_func) + ui_data->widget_set_func(¶m[i]); + } +} + +void prefs_set_data_from_dialog(PrefParam *param) +{ + PrefsUIData *ui_data; + gint i; + + for (i = 0; param[i].name != NULL; i++) { + ui_data = (PrefsUIData *)param[i].ui_data; + if (ui_data && ui_data->data_set_func) + ui_data->data_set_func(¶m[i]); + } +} + +void prefs_set_dialog_to_default(PrefParam *param) +{ + gint i; + PrefsUIData *ui_data; + PrefParam tmpparam; + gchar *str_data = NULL; + gint int_data; + gushort ushort_data; + gboolean bool_data; + DummyEnum enum_data; + + for (i = 0; param[i].name != NULL; i++) { + ui_data = (PrefsUIData *)param[i].ui_data; + if (!ui_data || !ui_data->widget_set_func) continue; + + tmpparam = param[i]; + + switch (tmpparam.type) { + case P_STRING: + if (tmpparam.defval) { + if (!g_ascii_strncasecmp + (tmpparam.defval, "ENV_", 4)) { + str_data = g_strdup + (g_getenv(param[i].defval + 4)); + tmpparam.data = &str_data; + break; + } else if (tmpparam.defval[0] == '~') { + str_data = +#ifdef G_OS_WIN32 + g_strconcat(get_rc_dir(), +#else + g_strconcat(get_home_dir(), +#endif + param[i].defval + 1, + NULL); + tmpparam.data = &str_data; + break; + } + } + tmpparam.data = &tmpparam.defval; + break; + case P_INT: + if (tmpparam.defval) + int_data = atoi(tmpparam.defval); + else + int_data = 0; + tmpparam.data = &int_data; + break; + case P_USHORT: + if (tmpparam.defval) + ushort_data = atoi(tmpparam.defval); + else + ushort_data = 0; + tmpparam.data = &ushort_data; + break; + case P_BOOL: + if (tmpparam.defval) { + if (!g_ascii_strcasecmp(tmpparam.defval, "TRUE")) + bool_data = TRUE; + else + bool_data = atoi(tmpparam.defval) + ? TRUE : FALSE; + } else + bool_data = FALSE; + tmpparam.data = &bool_data; + break; + case P_ENUM: + if (tmpparam.defval) + enum_data = (DummyEnum)atoi(tmpparam.defval); + else + enum_data = 0; + tmpparam.data = &enum_data; + break; + case P_OTHER: + break; + } + ui_data->widget_set_func(&tmpparam); + g_free(str_data); + str_data = NULL; + } +} + +void prefs_set_data_from_entry(PrefParam *pparam) +{ + PrefsUIData *ui_data; + gchar **str; + const gchar *entry_str; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + entry_str = gtk_entry_get_text(GTK_ENTRY(*ui_data->widget)); + + switch (pparam->type) { + case P_STRING: + str = (gchar **)pparam->data; + g_free(*str); + *str = entry_str[0] ? g_strdup(entry_str) : NULL; + break; + case P_USHORT: + *((gushort *)pparam->data) = atoi(entry_str); + break; + case P_INT: + *((gint *)pparam->data) = atoi(entry_str); + break; + default: + g_warning("Invalid PrefType for GtkEntry widget: %d\n", + pparam->type); + } +} + +void prefs_set_entry(PrefParam *pparam) +{ + PrefsUIData *ui_data; + gchar **str; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + switch (pparam->type) { + case P_STRING: + str = (gchar **)pparam->data; + gtk_entry_set_text(GTK_ENTRY(*ui_data->widget), + *str ? *str : ""); + break; + case P_INT: + gtk_entry_set_text(GTK_ENTRY(*ui_data->widget), + itos(*((gint *)pparam->data))); + break; + case P_USHORT: + gtk_entry_set_text(GTK_ENTRY(*ui_data->widget), + itos(*((gushort *)pparam->data))); + break; + default: + g_warning("Invalid PrefType for GtkEntry widget: %d\n", + pparam->type); + } +} + +gchar *prefs_get_escaped_str_from_text(GtkWidget *widget) +{ + gchar *text = NULL, *tp = NULL; + gchar *str, *p; + + g_return_val_if_fail(widget != NULL, NULL); + + if (GTK_IS_EDITABLE(widget)) { + tp = text = gtk_editable_get_chars + (GTK_EDITABLE(widget), 0, -1); + } else if (GTK_IS_TEXT_VIEW(widget)) { + GtkTextView *textview = GTK_TEXT_VIEW(widget); + GtkTextBuffer *buffer; + GtkTextIter start, end; + + buffer = gtk_text_view_get_buffer(textview); + gtk_text_buffer_get_start_iter(buffer, &start); + gtk_text_buffer_get_iter_at_offset(buffer, &end, -1); + tp = text = gtk_text_buffer_get_text + (buffer, &start, &end, FALSE); + } + + g_return_val_if_fail(tp != NULL && text != NULL, NULL); + + if (text[0] == '\0') { + g_free(text); + return NULL; + } + + p = str = g_malloc(strlen(text) * 2 + 1); + while (*tp) { + if (*tp == '\n') { + *p++ = '\\'; + *p++ = 'n'; + tp++; + } else + *p++ = *tp++; + } + *p = '\0'; + g_free(text); + + return str; +} + +void prefs_set_data_from_text(PrefParam *pparam) +{ + PrefsUIData *ui_data; + gchar **str; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + g_return_if_fail(GTK_IS_EDITABLE(*ui_data->widget) || + GTK_IS_TEXT_VIEW(*ui_data->widget)); + + switch (pparam->type) { + case P_STRING: + str = (gchar **)pparam->data; + g_free(*str); + *str = prefs_get_escaped_str_from_text(*ui_data->widget); + break; + default: + g_warning("Invalid PrefType for GtkTextView widget: %d\n", + pparam->type); + } +} + +void prefs_set_escaped_str_to_text(GtkWidget *widget, const gchar *str) +{ + gchar *buf, *bufp; + const gchar *sp; + GtkTextView *text; + GtkTextBuffer *buffer; + GtkTextIter iter; + + g_return_if_fail(widget != NULL); + + if (str) { + bufp = buf = g_malloc(strlen(str) + 1); + sp = str; + while (*sp) { + if (*sp == '\\' && *(sp + 1) == 'n') { + *bufp++ = '\n'; + sp += 2; + } else + *bufp++ = *sp++; + } + *bufp = '\0'; + } else + buf = g_strdup(""); + + text = GTK_TEXT_VIEW(widget); + buffer = gtk_text_view_get_buffer(text); + gtk_text_buffer_set_text(buffer, "", 0); + gtk_text_buffer_get_start_iter(buffer, &iter); + gtk_text_buffer_insert(buffer, &iter, buf, -1); + + g_free(buf); +} + +void prefs_set_text(PrefParam *pparam) +{ + PrefsUIData *ui_data; + gchar **str; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + switch (pparam->type) { + case P_STRING: + str = (gchar **)pparam->data; + prefs_set_escaped_str_to_text(*ui_data->widget, *str); + break; + default: + g_warning("Invalid PrefType for GtkTextView widget: %d\n", + pparam->type); + } +} + +void prefs_set_data_from_toggle(PrefParam *pparam) +{ + PrefsUIData *ui_data; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(pparam->type == P_BOOL); + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + *((gboolean *)pparam->data) = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(*ui_data->widget)); +} + +void prefs_set_toggle(PrefParam *pparam) +{ + PrefsUIData *ui_data; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(pparam->type == P_BOOL); + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(*ui_data->widget), + *((gboolean *)pparam->data)); +} + +void prefs_set_data_from_spinbtn(PrefParam *pparam) +{ + PrefsUIData *ui_data; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + switch (pparam->type) { + case P_INT: + *((gint *)pparam->data) = + gtk_spin_button_get_value_as_int + (GTK_SPIN_BUTTON(*ui_data->widget)); + break; + case P_USHORT: + *((gushort *)pparam->data) = + (gushort)gtk_spin_button_get_value_as_int + (GTK_SPIN_BUTTON(*ui_data->widget)); + break; + default: + g_warning("Invalid PrefType for GtkSpinButton widget: %d\n", + pparam->type); + } +} + +void prefs_set_spinbtn(PrefParam *pparam) +{ + PrefsUIData *ui_data; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + switch (pparam->type) { + case P_INT: + gtk_spin_button_set_value(GTK_SPIN_BUTTON(*ui_data->widget), + (gfloat)*((gint *)pparam->data)); + break; + case P_USHORT: + gtk_spin_button_set_value(GTK_SPIN_BUTTON(*ui_data->widget), + (gfloat)*((gushort *)pparam->data)); + break; + default: + g_warning("Invalid PrefType for GtkSpinButton widget: %d\n", + pparam->type); + } +} + +void prefs_set_data_from_fontbtn(PrefParam *pparam) +{ + PrefsUIData *ui_data; + gchar **str; + const gchar *font_str; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + font_str = gtk_font_button_get_font_name + (GTK_FONT_BUTTON(*ui_data->widget)); + + switch (pparam->type) { + case P_STRING: + str = (gchar **)pparam->data; + g_free(*str); + *str = font_str[0] ? g_strdup(font_str) : NULL; + break; + default: + g_warning("Invalid PrefType for GtkFontButton widget: %d\n", + pparam->type); + } +} + +void prefs_set_fontbtn(PrefParam *pparam) +{ + PrefsUIData *ui_data; + gchar **str; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + switch (pparam->type) { + case P_STRING: + str = (gchar **)pparam->data; + gtk_font_button_set_font_name(GTK_FONT_BUTTON(*ui_data->widget), + *str ? *str : ""); + break; + default: + g_warning("Invalid PrefType for GtkFontButton widget: %d\n", + pparam->type); + } +} + +void prefs_set_data_from_optmenu(PrefParam *pparam) +{ + PrefsUIData *ui_data; + GtkWidget *menu; + GtkWidget *menuitem; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(*ui_data->widget)); + menuitem = gtk_menu_get_active(GTK_MENU(menu)); + *((DummyEnum *)pparam->data) = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID)); +} + +void prefs_set_optmenu(PrefParam *pparam) +{ + PrefsUIData *ui_data; + DummyEnum val = *((DummyEnum *)pparam->data); + GtkOptionMenu *optmenu; + gint index; + + ui_data = (PrefsUIData *)pparam->ui_data; + g_return_if_fail(ui_data != NULL); + g_return_if_fail(*ui_data->widget != NULL); + + optmenu = GTK_OPTION_MENU(*ui_data->widget); + g_return_if_fail(optmenu != NULL); + + index = menu_find_option_menu_index(optmenu, GINT_TO_POINTER(val), + NULL); + if (index >= 0) + gtk_option_menu_set_history(optmenu, index); + else { + gtk_option_menu_set_history(optmenu, 0); + prefs_set_data_from_optmenu(pparam); + } +} diff --git a/src/prefs_ui.h b/src/prefs_ui.h new file mode 100644 index 0000000..cfdf353 --- /dev/null +++ b/src/prefs_ui.h @@ -0,0 +1,183 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2017 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PREFS_UI_H__ +#define __PREFS_UI_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _PrefsDialog PrefsDialog; +typedef struct _PrefsUIData PrefsUIData; + +#include "prefs.h" +#include "gtkutils.h" + +#define VSPACING 10 +#define VSPACING_NARROW 4 +#define VSPACING_NARROW_2 2 +#define VBOX_BORDER 12 +#define DEFAULT_ENTRY_WIDTH 80 + +struct _PrefsUIData +{ + gchar *name; + GtkWidget **widget; + DataSetFunc data_set_func; + WidgetSetFunc widget_set_func; +}; + +struct _PrefsDialog +{ + GtkWidget *window; + GtkWidget *notebook; + + GtkWidget *confirm_area; + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + GtkWidget *apply_btn; +}; + +#define SET_NOTEBOOK_LABEL(notebook, str, page_num) \ +{ \ + GtkWidget *label; \ + \ + label = gtk_label_new (str); \ + gtk_widget_show (label); \ + gtk_notebook_set_tab_label \ + (GTK_NOTEBOOK (notebook), \ + gtk_notebook_get_nth_page \ + (GTK_NOTEBOOK (notebook), page_num), \ + label); \ +} + +#define APPEND_SUB_NOTEBOOK(notebook, vbox, str) \ +{ \ + GtkWidget *label; \ + \ + label = gtk_label_new(str); \ + gtk_widget_show(label); \ + vbox = gtk_vbox_new(FALSE, VSPACING); \ + gtk_widget_show(vbox); \ + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label); \ + gtk_container_set_border_width(GTK_CONTAINER(vbox), VBOX_BORDER); \ +} + +#define PACK_CHECK_BUTTON(box, chkbtn, label) \ +{ \ + chkbtn = gtk_check_button_new_with_label(label); \ + gtk_widget_show(chkbtn); \ + gtk_box_pack_start(GTK_BOX(box), chkbtn, FALSE, TRUE, 0); \ +} + +#define PACK_END_CHECK_BUTTON(box, chkbtn, label) \ +{ \ + chkbtn = gtk_check_button_new_with_label(label); \ + gtk_widget_show(chkbtn); \ + gtk_box_pack_end(GTK_BOX(box), chkbtn, FALSE, TRUE, 0); \ +} + +#define PACK_FRAME(box, frame, label) \ +{ \ + frame = gtk_frame_new(label); \ + gtk_widget_show(frame); \ + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); \ + gtk_frame_set_label_align(GTK_FRAME(frame), 0.01, 0.5); \ +} + +#define PACK_FRAME_WITH_CHECK_BUTTON(box, frame, chkbtn, label) \ +{ \ + chkbtn = gtk_check_button_new_with_label(label); \ + gtk_widget_show(chkbtn); \ + PACK_FRAME(box, frame, NULL); \ + gtk_frame_set_label_widget(GTK_FRAME(frame), chkbtn); \ +} + +#define PACK_SMALL_LABEL(box, label, str) \ +{ \ + label = gtk_label_new(str); \ + gtk_widget_show(label); \ + gtk_box_pack_start(GTK_BOX(box), label, FALSE, TRUE, 0); \ + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); \ + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); \ + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); \ + gtkut_widget_set_small_font_size(label); \ +} + +#define PACK_VSPACER(box, vbox, spacing) \ +{ \ + vbox = gtk_vbox_new(FALSE, 0); \ + gtk_widget_show(vbox); \ + gtk_box_pack_start(GTK_BOX(box), vbox, FALSE, TRUE, spacing); \ +} + +#define SET_TOGGLE_SENSITIVITY(togglewid, targetwid) \ +{ \ + gtk_widget_set_sensitive(targetwid, FALSE); \ + g_signal_connect(G_OBJECT(togglewid), "toggled", \ + G_CALLBACK(prefs_button_toggled), targetwid); \ +} + +#define SET_TOGGLE_SENSITIVITY_REV(togglewid, targetwid) \ +{ \ + gtk_widget_set_sensitive(targetwid, TRUE); \ + g_signal_connect(G_OBJECT(togglewid), "toggled", \ + G_CALLBACK(prefs_button_toggled_rev), targetwid); \ +} + +void prefs_dialog_create (PrefsDialog *dialog); +void prefs_dialog_destroy (PrefsDialog *dialog); + +void prefs_button_toggled (GtkToggleButton *toggle_btn, + GtkWidget *widget); +void prefs_button_toggled_rev (GtkToggleButton *toggle_btn, + GtkWidget *widget); + +void prefs_register_ui (PrefParam *param, + PrefsUIData *ui_data); + +void prefs_set_dialog (PrefParam *param); +void prefs_set_data_from_dialog (PrefParam *param); +void prefs_set_dialog_to_default(PrefParam *param); + +void prefs_set_data_from_entry (PrefParam *pparam); +void prefs_set_entry (PrefParam *pparam); +void prefs_set_data_from_text (PrefParam *pparam); +void prefs_set_text (PrefParam *pparam); +void prefs_set_data_from_toggle (PrefParam *pparam); +void prefs_set_toggle (PrefParam *pparam); +void prefs_set_data_from_spinbtn(PrefParam *pparam); +void prefs_set_spinbtn (PrefParam *pparam); +void prefs_set_data_from_fontbtn(PrefParam *pparam); +void prefs_set_fontbtn (PrefParam *pparam); +void prefs_set_data_from_optmenu(PrefParam *pparam); +void prefs_set_optmenu (PrefParam *pparam); + +gchar *prefs_get_escaped_str_from_text (GtkWidget *widget); +void prefs_set_escaped_str_to_text (GtkWidget *widget, + const gchar *str); + +#endif /* __PREFS_UI_H__ */ diff --git a/src/printing.c b/src/printing.c new file mode 100644 index 0000000..ca0f55f --- /dev/null +++ b/src/printing.c @@ -0,0 +1,747 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2017 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" +#include "printing.h" + +#include +#include +#if GTK_CHECK_VERSION(2, 10, 0) +#include +#endif + +#include + +#include "mainwindow.h" +#include "procmsg.h" +#include "procheader.h" +#include "prefs_common.h" +#include "alertpanel.h" +#include "utils.h" + +#if GTK_CHECK_VERSION(2, 10, 0) + +#define SPACING 2.0 + +typedef struct +{ + MsgInfo *msginfo; + gint n_pages; + gchar *hdr_data; + gchar *msg_text_file; + FILE *fp; +} MsgPrintInfo; + +typedef struct +{ + gint page_nr_per_msg; + MsgPrintInfo *mpinfo; + glong pos; + gint lines; +} PageInfo; + +typedef struct +{ + GSList *mlist; + gint n_msgs; + MsgPrintInfo *msgs; + GPtrArray *pages; + gint n_pages; + + MimeInfo *partinfo; + + gdouble line_h; + gint lines_per_page; + + gboolean all_headers; +} PrintData; + +static GtkPrintSettings *settings = NULL; +static GtkPageSetup *page_setup = NULL; + +static gint get_header_data(MsgPrintInfo *mpinfo, PrintData *print_data) +{ + MsgInfo *msginfo; + FILE *fp; + GPtrArray *headers; + GString *str; + gint i; + + msginfo = mpinfo->msginfo; + + if ((fp = procmsg_open_message(msginfo)) == NULL) + return -1; + + if (print_data->all_headers) + headers = procheader_get_header_array_asis(fp, NULL); + else + headers = procheader_get_header_array_for_display(fp, NULL); + + fclose(fp); + + str = g_string_new(NULL); + + for (i = 0; i < headers->len; i++) { + Header *hdr; + gchar *text; + const gchar *body; + + hdr = g_ptr_array_index(headers, i); + + if (!g_ascii_strcasecmp(hdr->name, "Subject")) + body = msginfo->subject; + else if (!g_ascii_strcasecmp(hdr->name, "From")) + body = msginfo->from; + else if (!g_ascii_strcasecmp(hdr->name, "To")) + body = msginfo->to; + else if (!g_ascii_strcasecmp(hdr->name, "Cc")) { + unfold_line(hdr->body); + body = hdr->body; + while (g_ascii_isspace(*body)) + body++; + } else { + body = hdr->body; + while (g_ascii_isspace(*body)) + body++; + } + + if (body && *body != '\0') + text = g_markup_printf_escaped("%s: %s\n", + hdr->name, body); + else + text = g_markup_printf_escaped("%s: (none)\n", + hdr->name); + g_string_append(str, text); + g_free(text); + } + + /* dummy character for correct separation */ + g_string_append_c(str, ' '); + + mpinfo->hdr_data = g_string_free(str, FALSE); + procheader_header_array_destroy(headers); + + return 0; +} + +static gint layout_set_headers(PangoLayout *layout, MsgPrintInfo *mpinfo, + PrintData *print_data) +{ + PangoFontDescription *desc; + gint size; + + g_return_val_if_fail(mpinfo->hdr_data != NULL, -1); + + desc = pango_font_description_from_string(prefs_common_get()->textfont); + size = pango_font_description_get_size(desc); + pango_font_description_free(desc); + desc = gtkut_get_default_font_desc(); + debug_print("layout_set_headers: using font %s (style %d, %g pt)\n", pango_font_description_get_family(desc), pango_font_description_get_style(desc), (gdouble)size / PANGO_SCALE); + pango_font_description_set_size(desc, size); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + + pango_layout_set_markup(layout, mpinfo->hdr_data, -1); + + return 0; +} + +static gint message_count_page(MsgPrintInfo *mpinfo, GtkPrintContext *context, + PrintData *print_data) +{ + cairo_t *cr; + gdouble width, height, line_h, hdr_h = 0.0, body_h; + gdouble dpi_x, dpi_y; + PangoLayout *layout; + PangoFontDescription *desc; + gint layout_h; + gint lines_per_page, lines_left; + gint n_pages = 1; + PageInfo *pinfo; + gint i; + FILE *fp = NULL; + gchar buf[BUFFSIZE]; + glong pos = 0; + + cr = gtk_print_context_get_cairo_context(context); + width = gtk_print_context_get_width(context); + height = gtk_print_context_get_height(context); + dpi_x = gtk_print_context_get_dpi_x(context); + dpi_y = gtk_print_context_get_dpi_y(context); + + layout = gtk_print_context_create_pango_layout(context); + pango_layout_set_width(layout, width * PANGO_SCALE); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_spacing(layout, (SPACING / 72 * dpi_y) * PANGO_SCALE); + + if (!print_data->partinfo) { + if (get_header_data(mpinfo, print_data) < 0) { + g_object_unref(layout); + return 0; + } + layout_set_headers(layout, mpinfo, print_data); + + pango_layout_get_size(layout, NULL, &layout_h); + hdr_h = (gdouble)layout_h / PANGO_SCALE; + } + + pango_layout_set_attributes(layout, NULL); + desc = pango_font_description_from_string(prefs_common_get()->textfont); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + + pango_layout_set_text(layout, "Test", -1); + pango_layout_get_size(layout, NULL, &layout_h); + if (layout_h <= 0) { + g_warning("invalid layout_h (%d) ! falling back to default height (%d)\n", layout_h, 12 * PANGO_SCALE); + layout_h = 12 * PANGO_SCALE; + } + line_h = (gdouble)layout_h / PANGO_SCALE + (SPACING / 72 * dpi_y); + print_data->line_h = line_h; + lines_per_page = (height - line_h) / line_h; + print_data->lines_per_page = lines_per_page; + body_h = height - hdr_h - line_h; + if (body_h < 0) + body_h = 0.0; + lines_left = body_h / line_h; + + debug_print("width = %g, height = %g\n", width, height); + debug_print("dpi_x = %g, dpi_y = %g\n", dpi_x, dpi_y); + debug_print("layout_h = %d, line_h = %g, lines_per_page = %d\n", layout_h, line_h, lines_per_page); + debug_print("hdr_h = %g, body_h = %g, lines_left = %d\n", hdr_h, body_h, lines_left); + + if (print_data->partinfo) { + FILE *msgfp; + + if ((msgfp = procmsg_open_message(mpinfo->msginfo)) == NULL) + return -1; + fp = procmime_get_text_content(print_data->partinfo, msgfp, + NULL); + fclose(msgfp); + } else { + mpinfo->msg_text_file = get_tmp_file(); + if (procmsg_save_message_as_text(mpinfo->msginfo, mpinfo->msg_text_file, NULL, print_data->all_headers) == 0) { + if ((fp = g_fopen(mpinfo->msg_text_file, "rb")) != NULL) { + while (fgets(buf, sizeof(buf), fp) != NULL) + if (buf[0] == '\r' || buf[0] == '\n') + break; + } + } + } + if (!fp) { + g_warning("Can't get text part\n"); + return -1; + } + + i = 0; + pos = ftell(fp); + pinfo = g_new(PageInfo, 1); + pinfo->page_nr_per_msg = 0; + pinfo->mpinfo = mpinfo; + pinfo->pos = pos; + pinfo->lines = lines_left; + g_ptr_array_add(print_data->pages, pinfo); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + gint lines; + gint line_offset = 0; + + strretchomp(buf); + pango_layout_set_text(layout, buf, -1); + lines = pango_layout_get_line_count(layout); + + while (lines_left < lines) { + PangoLayoutLine *line; + + debug_print("page increment: %d: lines_left = %d, lines = %d\n", i, lines_left, lines); + line_offset += lines_left; + line = pango_layout_get_line(layout, line_offset); + lines -= lines_left; + lines_left = lines_per_page; + + pinfo = g_new(PageInfo, 1); + pinfo->page_nr_per_msg = ++i; + pinfo->mpinfo = mpinfo; + pinfo->pos = pos + line->start_index; + pinfo->lines = lines_left; + g_ptr_array_add(print_data->pages, pinfo); + } + + lines_left -= lines; + pos = ftell(fp); + } + + rewind(fp); + mpinfo->fp = fp; + + g_object_unref(layout); + + n_pages = i + 1; + debug_print("n_pages = %d\n", n_pages); + + return n_pages; +} + +static void begin_print(GtkPrintOperation *operation, GtkPrintContext *context, + gpointer data) +{ + PrintData *print_data = data; + gint n_pages = 0; + gint i; + + debug_print("begin_print\n"); + + for (i = 0; i < print_data->n_msgs; i++) { + n_pages += message_count_page(&print_data->msgs[i], context, + print_data); + } + + print_data->n_pages = n_pages; + gtk_print_operation_set_n_pages(operation, n_pages); +} + +static void draw_page(GtkPrintOperation *operation, GtkPrintContext *context, + gint page_nr, gpointer data) +{ + PrintData *print_data = data; + PageInfo *pinfo; + MsgPrintInfo *mpinfo; + MsgInfo *msginfo; + cairo_t *cr; + PangoLayout *layout; + gdouble width, height; + gdouble dpi; + gint layout_h; + PangoFontDescription *desc; + gint font_size; + gchar buf[BUFFSIZE]; + gint count = 0; + + if (page_nr >= print_data->n_pages) + return; + + pinfo = g_ptr_array_index(print_data->pages, page_nr); + mpinfo = pinfo->mpinfo; + msginfo = mpinfo->msginfo; + + debug_print("draw_page: %d (%d), pos = %ld\n", page_nr, pinfo->page_nr_per_msg, pinfo->pos); + debug_print("msg pages = %d\n", mpinfo->n_pages); + + cr = gtk_print_context_get_cairo_context(context); + width = gtk_print_context_get_width(context); + height = gtk_print_context_get_height(context); + dpi = gtk_print_context_get_dpi_y(context); + +#if 0 + cairo_set_source_rgb(cr, 0.8, 0.8, 0.8); + cairo_rectangle(cr, 0, 0, width, font_size * 5); + cairo_fill(cr); +#endif + + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_move_to(cr, 0, 0); + + layout = gtk_print_context_create_pango_layout(context); + pango_layout_set_width(layout, width * PANGO_SCALE); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_spacing(layout, (SPACING / 72 * dpi) * PANGO_SCALE); + + if (pinfo->page_nr_per_msg == 0 && mpinfo->hdr_data) { + if (layout_set_headers(layout, mpinfo, print_data) < 0) { + g_object_unref(layout); + return; + } + + pango_cairo_show_layout(cr, layout); + pango_layout_get_size(layout, NULL, &layout_h); + cairo_rel_move_to(cr, 0, (double)layout_h / PANGO_SCALE); + } + + pango_layout_set_attributes(layout, NULL); + desc = pango_font_description_from_string(prefs_common_get()->textfont); + font_size = pango_font_description_get_size(desc); + debug_print("draw_page: using font %s (style %d, %g pt)\n", pango_font_description_get_family(desc), pango_font_description_get_style(desc), (gdouble)font_size / PANGO_SCALE); + pango_font_description_set_size(desc, font_size); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + + if (fseek(mpinfo->fp, pinfo->pos, SEEK_SET) < 0) { + FILE_OP_ERROR("draw_page", "fseek"); + g_object_unref(layout); + return; + } + + while (count < pinfo->lines) { + gint lines; + gint i; + PangoLayoutIter *iter; + PangoLayoutLine *layout_line; + gint baseline; + gdouble x, y; + + if (fgets(buf, sizeof(buf), mpinfo->fp) == NULL) + break; + strretchomp(buf); + if (buf[0] == '\0') { + /* dummy character for correct empty line height */ + buf[0] = ' '; + buf[1] = '\0'; + } + + pango_layout_set_text(layout, buf, -1); + lines = pango_layout_get_line_count(layout); + iter = pango_layout_get_iter(layout); + cairo_get_current_point(cr, &x, &y); + + for (i = 0; i < lines; i++) { + layout_line = pango_layout_iter_get_line(iter); + baseline = pango_layout_iter_get_baseline(iter); + cairo_move_to(cr, 0, + y + (gdouble)baseline / PANGO_SCALE); + pango_cairo_show_layout_line(cr, layout_line); + count++; + if (count >= pinfo->lines && i + 1 < lines) + break; + pango_layout_iter_next_line(iter); + } + + pango_layout_iter_free(iter); + + pango_layout_get_size(layout, NULL, &layout_h); + cairo_move_to(cr, 0, + y + (gdouble)layout_h / PANGO_SCALE + (SPACING / 72 * dpi)); + } + debug_print("count = %d\n", count); + + desc = gtkut_get_default_font_desc(); + pango_font_description_set_size(desc, font_size); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + g_snprintf(buf, sizeof(buf), "- %d -", pinfo->page_nr_per_msg + 1); + pango_layout_set_text(layout, buf, -1); + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + cairo_move_to(cr, 0, height - (gdouble)font_size / PANGO_SCALE / 72 * dpi); + pango_cairo_show_layout(cr, layout); + + g_object_unref(layout); +} + +static gboolean printing_load_settings(void) +{ +#if GTK_CHECK_VERSION(2, 12, 0) + gchar *file; + GtkPrintSettings *new_settings; + + debug_print("printing_load_settings: load settings\n"); + + file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "printingrc", NULL); + if (!is_file_exist(file)) { + debug_print("printing_load_settings: %s not exist\n", file); + g_free(file); + return FALSE; + } + + if ((new_settings = gtk_print_settings_new_from_file(file, NULL)) == NULL) { + g_warning("printing_load_settings: gtk_print_settings_new_from_file: %s: error", file); + g_free(file); + return FALSE; + } + g_free(file); + + if (settings) + g_object_unref(settings); + settings = new_settings; + + return TRUE; +#else + return FALSE; +#endif /* GTK_CHECK_VERSION(2, 12, 0) */ +} + +static gboolean printing_save_settings(void) +{ +#if GTK_CHECK_VERSION(2, 12, 0) + gchar *file; + gboolean ret = TRUE; + + g_return_val_if_fail(settings != NULL, FALSE); + + debug_print("printing_save_settings: save settings\n"); + + file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "printingrc", NULL); + if (gtk_print_settings_to_file(settings, file, NULL) != TRUE) { + g_warning("printing_save_settings: gtk_print_settings_to_file: %s: error", file); + ret = FALSE; + } + g_free(file); + + return ret; +#else + return FALSE; +#endif /* GTK_CHECK_VERSION(2, 12, 0) */ +} + +static gboolean printing_load_page_setup(void) +{ +#if GTK_CHECK_VERSION(2, 12, 0) + gchar *file; + GtkPageSetup *new_setup; + + debug_print("printing_load_page_setup: load page setup\n"); + + file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "pagesetuprc", NULL); + if (!is_file_exist(file)) { + debug_print("printing_load_page_setup: %s not exist\n", file); + g_free(file); + return FALSE; + } + + if ((new_setup = gtk_page_setup_new_from_file(file, NULL)) == NULL) { + g_warning("printing_load_page_setup: gtk_page_setup_new_from_file: %s: error", file); + g_free(file); + return FALSE; + } + g_free(file); + + if (page_setup) + g_object_unref(page_setup); + page_setup = new_setup; + + return TRUE; +#else + return FALSE; +#endif /* GTK_CHECK_VERSION(2, 12, 0) */ +} + +static gboolean printing_save_page_setup(void) +{ +#if GTK_CHECK_VERSION(2, 12, 0) + gchar *file; + gboolean ret = TRUE; + + g_return_val_if_fail(page_setup != NULL, FALSE); + + debug_print("printing_save_page_setup: save page setup\n"); + + file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "pagesetuprc", NULL); + if (gtk_page_setup_to_file(page_setup, file, NULL) != TRUE) { + g_warning("printing_save_page_setup: gtk_page_setup_to_file: %s: error", file); + ret = FALSE; + } + g_free(file); + + return ret; +#else + return FALSE; +#endif /* GTK_CHECK_VERSION(2, 12, 0) */ +} + +gint printing_print_messages_gtk(GSList *mlist, MimeInfo *partinfo, + gboolean all_headers) +{ + GtkPrintOperation *op; + GtkPrintOperationResult res; + PrintData *print_data; + GSList *cur; + gchar *prev_dir; + gint i; + + g_return_val_if_fail(mlist != NULL, -1); + + debug_print("printing start\n"); + + prev_dir = g_get_current_dir(); + change_dir(get_document_dir()); + + print_data = g_new0(PrintData, 1); + print_data->mlist = mlist; + print_data->n_msgs = g_slist_length(mlist); + print_data->msgs = g_new0(MsgPrintInfo, print_data->n_msgs); + for (i = 0, cur = mlist; cur != NULL; i++, cur = cur->next) { + print_data->msgs[i].msginfo = (MsgInfo *)cur->data; + } + print_data->pages = g_ptr_array_new(); + print_data->n_pages = 0; + print_data->partinfo = partinfo; + print_data->line_h = 0.0; + print_data->all_headers = all_headers; + + op = gtk_print_operation_new(); + + /* GTK_UNIT_POINTS introduces text corruption bug, so use pixel unit */ + gtk_print_operation_set_unit(op, GTK_UNIT_PIXEL); + + g_signal_connect(op, "begin-print", G_CALLBACK(begin_print), + print_data); + g_signal_connect(op, "draw-page", G_CALLBACK(draw_page), print_data); + + if (!settings) + printing_load_settings(); + if (settings) + gtk_print_operation_set_print_settings(op, settings); + if (!page_setup) + printing_load_page_setup(); + if (page_setup) + gtk_print_operation_set_default_page_setup(op, page_setup); + + res = gtk_print_operation_run + (op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, + GTK_WINDOW(main_window_get()->window), NULL); + + if (res == GTK_PRINT_OPERATION_RESULT_APPLY) { + if (settings) + g_object_unref(settings); + settings = g_object_ref(gtk_print_operation_get_print_settings(op)); + printing_save_settings(); + } + + g_object_unref(op); + for (i = 0; i < print_data->pages->len; i++) { + PageInfo *pinfo; + pinfo = g_ptr_array_index(print_data->pages, i); + g_free(pinfo); + } + g_ptr_array_free(print_data->pages, TRUE); + for (i = 0; i < print_data->n_msgs; i++) { + g_free(print_data->msgs[i].hdr_data); + if (print_data->msgs[i].fp) + fclose(print_data->msgs[i].fp); + if (print_data->msgs[i].msg_text_file) { + g_unlink(print_data->msgs[i].msg_text_file); + g_free(print_data->msgs[i].msg_text_file); + } + } + g_free(print_data); + + change_dir(prev_dir); + g_free(prev_dir); + + debug_print("printing finished\n"); + + return 0; +} + +void printing_page_setup_gtk(void) +{ + GtkPageSetup *new_page_setup; + + if (!settings) + printing_load_settings(); + if (!settings) + settings = gtk_print_settings_new(); + if (!page_setup) + printing_load_page_setup(); + + new_page_setup = gtk_print_run_page_setup_dialog + (GTK_WINDOW(main_window_get()->window), page_setup, settings); + + if (page_setup) + g_object_unref(page_setup); + page_setup = new_page_setup; + + printing_save_page_setup(); +} + +#endif /* GTK_CHECK_VERSION(2, 10, 0) */ + +static gint check_command_line(const gchar *cmdline) +{ + gchar *msg; + + msg = g_strconcat + (_("The message will be printed with the following command:"), + "\n\n", cmdline ? cmdline : _("(Default print command)"), + NULL); + if (alertpanel(_("Print"), msg, GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL) + != G_ALERTDEFAULT) { + g_free(msg); + return -2; + } + g_free(msg); + + if (cmdline && str_find_format_times(cmdline, 's') != 1) { + alertpanel_error(_("Print command line is invalid:\n`%s'"), + cmdline); + return -1; + } + + return 0; +} + +gint printing_print_messages_with_command(GSList *mlist, gboolean all_headers, + const gchar *cmdline) +{ + MsgInfo *msginfo; + GSList *cur; + + g_return_val_if_fail(mlist != NULL, -1); + + if (check_command_line(cmdline) < 0) + return -1; + + for (cur = mlist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + if (msginfo) + procmsg_print_message(msginfo, cmdline, all_headers); + } + + return 0; +} + +gint printing_print_messages(GSList *mlist, gboolean all_headers) +{ +#if GTK_CHECK_VERSION(2, 10, 0) + if (!prefs_common.use_print_cmd) + return printing_print_messages_gtk(mlist, NULL, all_headers); + else +#endif /* GTK_CHECK_VERSION(2, 10, 0) */ + return printing_print_messages_with_command + (mlist, all_headers, prefs_common.print_cmd); +} + +gint printing_print_message(MsgInfo *msginfo, gboolean all_headers) +{ + GSList mlist; + + mlist.data = msginfo; + mlist.next = NULL; + return printing_print_messages(&mlist, all_headers); +} + +gint printing_print_message_part(MsgInfo *msginfo, MimeInfo *partinfo) +{ +#if GTK_CHECK_VERSION(2, 10, 0) + if (!prefs_common.use_print_cmd) { + GSList mlist; + + mlist.data = msginfo; + mlist.next = NULL; + return printing_print_messages_gtk(&mlist, partinfo, FALSE); + } +#endif /* GTK_CHECK_VERSION(2, 10, 0) */ + if (check_command_line(prefs_common.print_cmd) < 0) + return -1; + procmsg_print_message_part(msginfo, partinfo, prefs_common.print_cmd, + FALSE); + return 0; +} diff --git a/src/printing.h b/src/printing.h new file mode 100644 index 0000000..c5f1168 --- /dev/null +++ b/src/printing.h @@ -0,0 +1,49 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2006 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PRINTING_H__ +#define __PRINTING_H__ + +#include + +#include + +#include "procmsg.h" +#include "procmime.h" + +#if GTK_CHECK_VERSION(2, 10, 0) +gint printing_print_messages_gtk (GSList *mlist, + MimeInfo *partinfo, + gboolean all_headers); + +void printing_page_setup_gtk (void); +#endif + +gint printing_print_messages_with_command (GSList *mlist, + gboolean all_headers, + const gchar *cmdline); + +gint printing_print_messages (GSList *mlist, + gboolean all_headers); +gint printing_print_message (MsgInfo *msginfo, + gboolean all_headers); +gint printing_print_message_part (MsgInfo *msginfo, + MimeInfo *partinfo); + +#endif /* __PRINTING_H__ */ diff --git a/src/progressdialog.c b/src/progressdialog.c new file mode 100644 index 0000000..2107da4 --- /dev/null +++ b/src/progressdialog.c @@ -0,0 +1,300 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "progressdialog.h" +#include "gtkutils.h" +#include "utils.h" + +#define PROGRESS_DIALOG_WIDTH 460 +#define PROGRESS_TREE_VIEW_HEIGHT 120 + +ProgressDialog *progress_dialog_create(void) +{ + ProgressDialog *progress; + GtkWidget *scrolledwin; + GtkWidget *treeview; + GtkListStore *store; + GtkTreeSelection *selection; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + progress = progress_dialog_simple_create(); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(scrolledwin); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(progress->window)->vbox), + scrolledwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + + store = gtk_list_store_new(PROG_N_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER); + + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + gtk_widget_show(treeview); + gtk_container_add(GTK_CONTAINER(scrolledwin), treeview); + gtk_widget_set_size_request(treeview, -1, PROGRESS_TREE_VIEW_HEIGHT * gtkut_get_dpi_multiplier()); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); + + renderer = gtk_cell_renderer_pixbuf_new(); + g_object_set(renderer, "xalign", 0.5, NULL); + column = gtk_tree_view_column_new_with_attributes + (NULL, renderer, "pixbuf", PROG_COL_PIXBUF, NULL); + gtk_tree_view_column_set_alignment(column, 0.5); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(column, 20); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes + (_("Account"), renderer, "text", PROG_COL_NAME, NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(column, 120 * gtkut_get_dpi_multiplier()); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes + (_("Status"), renderer, "text", PROG_COL_STATUS, NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(column, 100 * gtkut_get_dpi_multiplier()); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes + (_("Progress"), renderer, "text", PROG_COL_PROGRESS, NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + progress->treeview = treeview; + progress->store = store; + + return progress; +} + +ProgressDialog *progress_dialog_simple_create(void) +{ + ProgressDialog *progress; + GtkWidget *dialog; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *cancel_btn; + GtkWidget *progressbar; + + debug_print("Creating progress dialog\n"); + progress = g_new0(ProgressDialog, 1); + + dialog = gtk_dialog_new(); + gtk_widget_set_size_request(dialog, PROGRESS_DIALOG_WIDTH * gtkut_get_dpi_multiplier(), -1); + gtk_container_set_border_width(GTK_CONTAINER(dialog), 8); + gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); + gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, TRUE, TRUE); + gtk_widget_realize(dialog); + + gtk_container_set_border_width + (GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), 0); + gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), 8); + gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, + FALSE, FALSE, 8); + gtk_widget_show(hbox); + + label = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 8); + gtk_widget_show(label); + + cancel_btn = gtk_dialog_add_button(GTK_DIALOG(dialog), + GTK_STOCK_CANCEL, + GTK_RESPONSE_NONE); + gtk_widget_grab_default(cancel_btn); + gtk_widget_grab_focus(cancel_btn); + + progressbar = gtk_progress_bar_new(); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), progressbar, + FALSE, FALSE, 0); + gtk_widget_show(progressbar); + + progress->window = dialog; + progress->label = label; + progress->cancel_btn = cancel_btn; + progress->progressbar = progressbar; + progress->treeview = NULL; + progress->store = NULL; + + return progress; +} + +void progress_dialog_destroy(ProgressDialog *progress) +{ + if (progress) { + gtk_widget_destroy(progress->window); + g_free(progress); + } +} + +void progress_dialog_set_label(ProgressDialog *progress, gchar *str) +{ + gtk_label_set_text(GTK_LABEL(progress->label), str); +} + +void progress_dialog_set_value(ProgressDialog *progress, gfloat value) +{ + gtk_progress_set_value(GTK_PROGRESS(progress->progressbar), value); +} + +void progress_dialog_set_percentage(ProgressDialog *progress, + gfloat percentage) +{ + gtk_progress_set_percentage(GTK_PROGRESS(progress->progressbar), + percentage); +} + +void progress_dialog_append(ProgressDialog *progress, GdkPixbuf *pixbuf, + const gchar *name, const gchar *status, + const gchar *progress_str, gpointer data) +{ + GtkListStore *store = progress->store; + GtkTreeIter iter; + + gtk_list_store_append(store, &iter); + + gtk_list_store_set(store, &iter, + PROG_COL_PIXBUF, pixbuf, + PROG_COL_NAME, name, + PROG_COL_STATUS, status, + PROG_COL_PROGRESS, progress_str, + PROG_COL_POINTER, data, + -1); +} + +void progress_dialog_set_row(ProgressDialog *progress, gint row, + GdkPixbuf *pixbuf, const gchar *name, + const gchar *status, const gchar *progress_str, + gpointer data) +{ + GtkListStore *store = progress->store; + GtkTreeIter iter; + + if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), + &iter, NULL, row)) { + gtk_list_store_set(store, &iter, + PROG_COL_PIXBUF, pixbuf, + PROG_COL_NAME, name, + PROG_COL_STATUS, status, + PROG_COL_PROGRESS, progress_str, + PROG_COL_POINTER, data, + -1); + } +} + +void progress_dialog_set_row_pixbuf(ProgressDialog *progress, gint row, + GdkPixbuf *pixbuf) +{ + GtkListStore *store = progress->store; + GtkTreeIter iter; + + if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), + &iter, NULL, row)) { + gtk_list_store_set(store, &iter, PROG_COL_PIXBUF, pixbuf, -1); + } +} + +void progress_dialog_set_row_name(ProgressDialog *progress, gint row, + const gchar *name) +{ + GtkListStore *store = progress->store; + GtkTreeIter iter; + + if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), + &iter, NULL, row)) { + gtk_list_store_set(store, &iter, PROG_COL_NAME, name, -1); + } +} + +void progress_dialog_set_row_status(ProgressDialog *progress, gint row, + const gchar *status) +{ + GtkListStore *store = progress->store; + GtkTreeIter iter; + + if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), + &iter, NULL, row)) { + gtk_list_store_set(store, &iter, PROG_COL_STATUS, status, -1); + } +} + +void progress_dialog_set_row_progress(ProgressDialog *progress, gint row, + const gchar *progress_str) +{ + GtkListStore *store = progress->store; + GtkTreeIter iter; + + if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), + &iter, NULL, row)) { + gtk_list_store_set(store, &iter, PROG_COL_PROGRESS, + progress_str, -1); + } +} + +void progress_dialog_scroll_to_row(ProgressDialog *progress, gint row) +{ + GtkTreeModel *model = GTK_TREE_MODEL(progress->store); + GtkTreeIter iter; + GtkTreePath *path; + + if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, row)) + return; + + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(progress->treeview), + path, NULL, FALSE, 0.0, 0.0); + gtk_tree_path_free(path); +} diff --git a/src/progressdialog.h b/src/progressdialog.h new file mode 100644 index 0000000..fd5a327 --- /dev/null +++ b/src/progressdialog.h @@ -0,0 +1,93 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2009 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PROGRESS_H__ +#define __PROGRESS_H__ + +#include +#include +#include +#include + +typedef struct _ProgressDialog ProgressDialog; + +struct _ProgressDialog +{ + GtkWidget *window; + GtkWidget *label; + GtkWidget *cancel_btn; + GtkWidget *progressbar; + + GtkWidget *treeview; + GtkListStore *store; +}; + +typedef enum +{ + PROG_COL_PIXBUF, + PROG_COL_NAME, + PROG_COL_STATUS, + PROG_COL_PROGRESS, + PROG_COL_POINTER, + PROG_N_COLS +} ProgressColumn; + +ProgressDialog *progress_dialog_create (void); +ProgressDialog *progress_dialog_simple_create (void); + +void progress_dialog_destroy (ProgressDialog *progress); + +void progress_dialog_set_label (ProgressDialog *progress, + gchar *str); +void progress_dialog_set_value (ProgressDialog *progress, + gfloat value); +void progress_dialog_set_percentage (ProgressDialog *progress, + gfloat percentage); + +void progress_dialog_append (ProgressDialog *progress, + GdkPixbuf *pixbuf, + const gchar *name, + const gchar *status, + const gchar *progress_str, + gpointer data); +void progress_dialog_set_row (ProgressDialog *progress, + gint row, + GdkPixbuf *pixbuf, + const gchar *name, + const gchar *status, + const gchar *progress_str, + gpointer data); + +void progress_dialog_set_row_pixbuf (ProgressDialog *progress, + gint row, + GdkPixbuf *pixbuf); +void progress_dialog_set_row_name (ProgressDialog *progress, + gint row, + const gchar *name); +void progress_dialog_set_row_status (ProgressDialog *progress, + gint row, + const gchar *status); +void progress_dialog_set_row_progress (ProgressDialog *progress, + gint row, + const gchar *progress_str); + +void progress_dialog_scroll_to_row (ProgressDialog *progress, + gint row); + +#endif /* __PROGRESS_H__ */ diff --git a/src/query_search.c b/src/query_search.c new file mode 100644 index 0000000..0b6170e --- /dev/null +++ b/src/query_search.c @@ -0,0 +1,1698 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2017 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "query_search.h" +#include "summaryview.h" +#include "messageview.h" +#include "mainwindow.h" +#include "folderview.h" +#include "menu.h" +#include "utils.h" +#include "gtkutils.h" +#include "manage_window.h" +#include "alertpanel.h" +#include "foldersel.h" +#include "statusbar.h" +#include "procmsg.h" +#include "procheader.h" +#include "folder.h" +#include "filter.h" +#include "prefs_common.h" +#include "prefs_filter.h" +#include "prefs_filter_edit.h" +#include "compose.h" +#include "sourcewindow.h" +#include "printing.h" + +enum +{ + COL_FOLDER, + COL_SUBJECT, + COL_FROM, + COL_DATE, + COL_MSGINFO, + N_COLS +}; + +static struct QuerySearchWindow { + GtkWidget *window; + + GtkWidget *bool_optmenu; + + FilterCondEdit *cond_edit; + + GtkWidget *folder_entry; + GtkWidget *folder_btn; + + GtkWidget *subfolder_checkbtn; + GtkWidget *case_checkbtn; + + GtkWidget *treeview; + GtkListStore *store; + + GtkWidget *status_label; + + GtkWidget *clear_btn; + GtkWidget *search_btn; + GtkWidget *save_btn; + GtkWidget *close_btn; + + GtkActionGroup *group; + GtkUIManager *ui; + + FilterRule *rule; + gboolean requires_full_headers; + + gboolean exclude_trash; + + gint n_found; + + gboolean on_search; + gboolean cancelled; +} search_window; + +typedef struct { + GtkWidget *window; + + GtkWidget *folder_entry; + GtkWidget *name_entry; + + GtkWidget *ok_btn; + GtkWidget *cancel_btn; + + gboolean cancelled; + gboolean finished; +} QuerySearchSaveDialog; + +static const gchar *ui_def = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +static void query_search_create (void); + +static FilterRule *query_search_dialog_to_rule (const gchar *name, + FolderItem **item); + +static void query_search_query (void); +static void query_search_folder (FolderItem *item); + +static gboolean query_search_recursive_func (GNode *node, + gpointer data); + +static void query_search_append_msg (MsgInfo *msginfo); +static void query_search_clear_list (void); + +static void query_search_hbox_added (CondHBox *hbox); + +static void row_activated (GtkTreeView *treeview, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data); + +static gboolean row_selected (GtkTreeSelection *selection, + GtkTreeModel *model, + GtkTreePath *path, + gboolean cur_selected, + gpointer data); + +static gboolean treeview_button_pressed (GtkWidget *widget, + GdkEventButton *event, + gpointer data); + +static void query_search_clear (GtkButton *button, + gpointer data); +static void query_select_folder (GtkButton *button, + gpointer data); +static void query_search_clicked (GtkButton *button, + gpointer data); +static void query_search_save (GtkButton *button, + gpointer data); +static void query_search_close (GtkButton *button, + gpointer data); + +static void query_search_entry_activated(GtkWidget *widget, + gpointer data); + +static gint query_search_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); + +static void query_search_open_msg (gboolean new_window); + +/* Popup menu callbacks */ +static void reply_cb(void); +static void reply_all_cb(void); +static void reply_sender_cb(void); +static void reply_list_cb(void); +static void forward_cb(void); +static void forward_attach_cb(void); +static void redirect_cb(void); +static void move_cb(void); +static void copy_cb(void); +static void add_address_cb(void); +static void create_filter_auto_cb(void); +static void create_filter_from_cb(void); +static void create_filter_to_cb(void); +static void create_filter_subject_cb(void); +static void open_cb (void); +static void open_source_cb (void); +static void print_cb (void); + +static gint query_search_cmp_by_folder (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint query_search_cmp_by_subject (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint query_search_cmp_by_from (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint query_search_cmp_by_date (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); + +static GtkActionEntry action_entries[] = { + {"ReplyAction", NULL, N_("_Reply"), NULL, NULL, reply_cb}, + {"ReplyToAction", NULL, N_("Repl_y to"), NULL, NULL, NULL}, + {"ReplyAllAction", NULL, N_("Reply to _all"), NULL, NULL, reply_all_cb}, + {"ReplySenderAction", NULL, N_("Reply to _sender"), NULL, NULL, reply_sender_cb}, + {"ReplyListAction", NULL, N_("Reply to _list"), NULL, NULL, reply_list_cb}, + + {"ForwardAction", NULL, N_("_Forward"), NULL, NULL, forward_cb}, + {"ForwardAsAttachAction", NULL, N_("For_ward as attachment"), NULL, NULL, forward_attach_cb}, + {"RedirectAction", NULL, N_("Redirec_t"), NULL, NULL, redirect_cb}, + + {"MoveMsgAction", NULL, N_("M_ove messages to..."), NULL, NULL, move_cb}, + {"CopyMsgAction", NULL, N_("_Copy messages to..."), NULL, NULL, copy_cb}, + + {"AddAddressAction", NULL, N_("Add sender to address boo_k..."), NULL, NULL, add_address_cb}, + {"CreateFilterAction", NULL, N_("Create f_ilter rule"), NULL, NULL, NULL}, + {"FilterAutoAction", NULL, N_("_Automatically"), NULL, NULL, create_filter_auto_cb}, + {"FilterFromAction", NULL, N_("by _From"), NULL, NULL, create_filter_from_cb}, + {"FilterToAction", NULL, N_("by _To"), NULL, NULL, create_filter_to_cb}, + {"FilterSubjectAction", NULL, N_("by _Subject"), NULL, NULL, create_filter_subject_cb}, + + {"OpenAction", GTK_STOCK_OPEN, N_("Open in _new window"), NULL, NULL, open_cb}, + {"OpenSourceAction", NULL, N_("View mess_age source"), NULL, NULL, open_source_cb}, + + {"PrintAction", GTK_STOCK_PRINT, N_("_Print..."), NULL, NULL, print_cb}, +}; + + +void query_search(FolderItem *item) +{ + gchar *id; + + if (!search_window.window) + query_search_create(); + else + gtk_window_present(GTK_WINDOW(search_window.window)); + + if (item && item->stype != F_VIRTUAL) { + id = folder_item_get_identifier(item); + gtk_entry_set_text(GTK_ENTRY(search_window.folder_entry), id); + g_free(id); + } else + gtk_entry_set_text(GTK_ENTRY(search_window.folder_entry), ""); + + gtk_widget_grab_focus(search_window.search_btn); + gtk_widget_show(search_window.window); +} + +static void query_search_create(void) +{ + GtkWidget *window; + GtkWidget *vbox1; + GtkWidget *bool_hbox; + GtkWidget *bool_optmenu; + GtkWidget *bool_menu; + GtkWidget *menuitem; + GtkWidget *clear_btn; + GtkWidget *search_btn; + + GtkWidget *scrolledwin; + FilterCondEdit *cond_edit; + CondHBox *cond_hbox; + + GtkWidget *folder_hbox; + GtkWidget *folder_label; + GtkWidget *folder_entry; + GtkWidget *folder_btn; + + GtkWidget *checkbtn_hbox; + GtkWidget *subfolder_checkbtn; + GtkWidget *case_checkbtn; + + GtkWidget *treeview; + GtkListStore *store; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + + GtkWidget *confirm_area; + + GtkWidget *status_label; + + GtkWidget *btn_hbox; + GtkWidget *hbbox; + GtkWidget *save_btn; + GtkWidget *close_btn; + + GtkActionGroup *group; + GtkUIManager *ui; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW (window), _("Search messages")); + gtk_widget_set_size_request(window, 600 * gtkut_get_dpi_multiplier(), -1); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, TRUE); + gtk_container_set_border_width(GTK_CONTAINER (window), 8); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(query_search_deleted), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + + vbox1 = gtk_vbox_new (FALSE, 6); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (window), vbox1); + + bool_hbox = gtk_hbox_new(FALSE, 12); + gtk_widget_show(bool_hbox); + gtk_box_pack_start(GTK_BOX(vbox1), bool_hbox, FALSE, FALSE, 0); + + bool_optmenu = gtk_option_menu_new(); + gtk_widget_show(bool_optmenu); + gtk_box_pack_start(GTK_BOX(bool_hbox), bool_optmenu, FALSE, FALSE, 0); + + bool_menu = gtk_menu_new(); + MENUITEM_ADD(bool_menu, menuitem, _("Match any of the following"), + FLT_OR); + MENUITEM_ADD(bool_menu, menuitem, _("Match all of the following"), + FLT_AND); + gtk_option_menu_set_menu(GTK_OPTION_MENU(bool_optmenu), bool_menu); + gtk_option_menu_set_history(GTK_OPTION_MENU(bool_optmenu), FLT_AND); + + hbbox = gtk_hbutton_box_new(); + gtk_widget_show(hbbox); + gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(hbbox), 6); + gtk_box_pack_end(GTK_BOX(bool_hbox), hbbox, FALSE, FALSE, 0); + + clear_btn = gtk_button_new_from_stock(GTK_STOCK_CLEAR); + gtk_widget_show(clear_btn); + gtk_box_pack_start(GTK_BOX(hbbox), clear_btn, FALSE, FALSE, 0); + + search_btn = gtk_button_new_from_stock(GTK_STOCK_FIND); + GTK_WIDGET_SET_FLAGS(search_btn, GTK_CAN_DEFAULT); + gtk_widget_show(search_btn); + gtk_box_pack_start(GTK_BOX(hbbox), search_btn, FALSE, FALSE, 0); + gtk_widget_grab_default(search_btn); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(scrolledwin); + gtk_box_pack_start(GTK_BOX(vbox1), scrolledwin, FALSE, FALSE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_widget_set_size_request(scrolledwin, -1, 120 * gtkut_get_dpi_multiplier()); + + cond_edit = prefs_filter_edit_cond_edit_create(); + cond_edit->add_hbox = query_search_hbox_added; + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolledwin), + cond_edit->cond_vbox); + prefs_filter_set_header_list(NULL); + prefs_filter_edit_set_header_list(cond_edit, NULL); + cond_hbox = prefs_filter_edit_cond_hbox_create(cond_edit); + prefs_filter_edit_set_cond_hbox_widgets(cond_hbox, PF_COND_HEADER); + prefs_filter_edit_insert_cond_hbox(cond_edit, cond_hbox, -1); + if (cond_edit->add_hbox) + cond_edit->add_hbox(cond_hbox); + + folder_hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (folder_hbox); + gtk_box_pack_start (GTK_BOX (vbox1), folder_hbox, FALSE, FALSE, 0); + + folder_label = gtk_label_new (_("Folder:")); + gtk_widget_show (folder_label); + gtk_box_pack_start (GTK_BOX (folder_hbox), folder_label, + FALSE, FALSE, 0); + + folder_entry = gtk_entry_new (); + gtk_widget_show (folder_entry); + gtk_box_pack_start (GTK_BOX (folder_hbox), folder_entry, TRUE, TRUE, 0); + + folder_btn = gtk_button_new_with_label("..."); + gtk_widget_show (folder_btn); + gtk_box_pack_start (GTK_BOX (folder_hbox), folder_btn, FALSE, FALSE, 0); + + checkbtn_hbox = gtk_hbox_new (FALSE, 12); + gtk_widget_show (checkbtn_hbox); + gtk_box_pack_start (GTK_BOX (vbox1), checkbtn_hbox, FALSE, FALSE, 0); + + subfolder_checkbtn = + gtk_check_button_new_with_label (_("Search subfolders")); + gtk_widget_show (subfolder_checkbtn); + gtk_box_pack_start (GTK_BOX (checkbtn_hbox), subfolder_checkbtn, + FALSE, FALSE, 0); + + case_checkbtn = gtk_check_button_new_with_label (_("Case sensitive")); + gtk_widget_show (case_checkbtn); + gtk_box_pack_start (GTK_BOX (checkbtn_hbox), case_checkbtn, + FALSE, FALSE, 0); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_box_pack_start(GTK_BOX(vbox1), scrolledwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + gtk_widget_set_size_request(scrolledwin, -1, 150 * gtkut_get_dpi_multiplier()); + + store = gtk_list_store_new(N_COLS, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_POINTER); + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(store); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + g_signal_connect(G_OBJECT(treeview), "row-activated", + G_CALLBACK(row_activated), NULL); + g_signal_connect(G_OBJECT(treeview), "button-press-event", + G_CALLBACK(treeview_button_pressed), NULL); + + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_FOLDER, + query_search_cmp_by_folder, NULL, NULL); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_SUBJECT, + query_search_cmp_by_subject, + NULL, NULL); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_FROM, + query_search_cmp_by_from, NULL, NULL); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_DATE, + query_search_cmp_by_date, NULL, NULL); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + gtk_tree_selection_set_select_function(selection, row_selected, + NULL, NULL); + + gtk_container_add(GTK_CONTAINER(scrolledwin), treeview); + +#define APPEND_COLUMN(label, col, width) \ +{ \ + renderer = gtk_cell_renderer_text_new(); \ + column = gtk_tree_view_column_new_with_attributes \ + (label, renderer, "text", col, NULL); \ + gtk_tree_view_column_set_resizable(column, TRUE); \ + if (width) { \ + gtk_tree_view_column_set_sizing \ + (column, GTK_TREE_VIEW_COLUMN_FIXED); \ + gtk_tree_view_column_set_fixed_width(column, width); \ + } \ + gtk_tree_view_column_set_sort_column_id(column, col); \ + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); \ +} + + APPEND_COLUMN(_("Folder"), COL_FOLDER, 0); + APPEND_COLUMN(_("Subject"), COL_SUBJECT, 200); +#if GTK_CHECK_VERSION(2, 14, 0) + gtk_tree_view_column_set_expand(column, TRUE); +#endif + APPEND_COLUMN(_("From"), COL_FROM, 180); + APPEND_COLUMN(_("Date"), COL_DATE, 0); + + gtk_widget_show_all(scrolledwin); + + confirm_area = gtk_hbox_new(FALSE, 12); + gtk_widget_show(confirm_area); + gtk_box_pack_start(GTK_BOX(vbox1), confirm_area, FALSE, FALSE, 0); + + status_label = gtk_label_new(""); + gtk_widget_show(status_label); + gtk_box_pack_start(GTK_BOX(confirm_area), status_label, + FALSE, FALSE, 0); + + btn_hbox = gtk_hbox_new(FALSE, 6); + gtk_widget_show(btn_hbox); + gtk_box_pack_end(GTK_BOX(confirm_area), btn_hbox, FALSE, FALSE, 0); + + gtkut_stock_button_set_create(&hbbox, &close_btn, GTK_STOCK_CLOSE, + NULL, NULL, NULL, NULL); + gtk_widget_show(hbbox); + gtk_box_pack_end(GTK_BOX(btn_hbox), hbbox, FALSE, FALSE, 0); + + save_btn = gtk_button_new_with_mnemonic(_("_Save as search folder")); + gtk_widget_show(save_btn); + gtk_box_pack_end(GTK_BOX(btn_hbox), save_btn, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(clear_btn), "clicked", + G_CALLBACK(query_search_clear), NULL); + g_signal_connect(G_OBJECT(folder_btn), "clicked", + G_CALLBACK(query_select_folder), NULL); + g_signal_connect(G_OBJECT(search_btn), "clicked", + G_CALLBACK(query_search_clicked), NULL); + g_signal_connect(G_OBJECT(save_btn), "clicked", + G_CALLBACK(query_search_save), NULL); + g_signal_connect(G_OBJECT(close_btn), "clicked", + G_CALLBACK(query_search_close), NULL); + + group = gtk_action_group_new("main"); + gtk_action_group_set_translation_domain(group, GETTEXT_PACKAGE); + gtk_action_group_add_actions(group, action_entries, + sizeof(action_entries) / + sizeof(action_entries[0]), NULL); + + ui = gtk_ui_manager_new(); + gtk_ui_manager_insert_action_group(ui, group, 0); + gtk_ui_manager_add_ui_from_string(ui, ui_def, -1, NULL); + + search_window.window = window; + search_window.bool_optmenu = bool_optmenu; + + search_window.cond_edit = cond_edit; + + search_window.folder_entry = folder_entry; + search_window.folder_btn = folder_btn; + search_window.subfolder_checkbtn = subfolder_checkbtn; + search_window.case_checkbtn = case_checkbtn; + + search_window.treeview = treeview; + search_window.store = store; + + search_window.status_label = status_label; + + search_window.clear_btn = clear_btn; + search_window.search_btn = search_btn; + search_window.save_btn = save_btn; + search_window.close_btn = close_btn; + + search_window.group = group; + search_window.ui = ui; +} + +static FilterRule *query_search_dialog_to_rule(const gchar *name, + FolderItem **item) +{ + const gchar *id; + FolderItem *item_; + FilterBoolOp bool_op; + gboolean recursive; + gboolean case_sens; + GSList *cond_list; + FilterRule *rule; + + id = gtk_entry_get_text(GTK_ENTRY(search_window.folder_entry)); + item_ = folder_find_item_from_identifier(id); + if (!item_) + return NULL; + if (item) + *item = item_; + + bool_op = menu_get_option_menu_active_index + (GTK_OPTION_MENU(search_window.bool_optmenu)); + recursive = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(search_window.subfolder_checkbtn)); + case_sens = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(search_window.case_checkbtn)); + + cond_list = prefs_filter_edit_cond_edit_to_list(search_window.cond_edit, + case_sens); + if (!cond_list) + return NULL; + + rule = filter_rule_new(name, bool_op, cond_list, NULL); + rule->target_folder = g_strdup(id); + rule->recursive = recursive; + + return rule; +} + +static void query_search_query(void) +{ + FolderItem *item; + gchar *msg; + + if (search_window.on_search) + return; + + search_window.on_search = TRUE; + + search_window.rule = query_search_dialog_to_rule("Query rule", &item); + if (!search_window.rule) { + search_window.on_search = FALSE; + return; + } + search_window.requires_full_headers = + filter_rule_requires_full_headers(search_window.rule); + + if (search_window.rule->recursive) { + if (item->stype == F_TRASH) + search_window.exclude_trash = FALSE; + else + search_window.exclude_trash = TRUE; + } else + search_window.exclude_trash = FALSE; + + search_window.n_found = 0; + search_window.cancelled = FALSE; + + gtk_widget_set_sensitive(search_window.clear_btn, FALSE); + gtk_button_set_label(GTK_BUTTON(search_window.search_btn), + GTK_STOCK_STOP); + query_search_clear_list(); + + if (search_window.rule->recursive) + g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + query_search_recursive_func, NULL); + else + query_search_folder(item); + + filter_rule_free(search_window.rule); + search_window.rule = NULL; + search_window.requires_full_headers = FALSE; + search_window.exclude_trash = FALSE; + + gtk_widget_set_sensitive(search_window.clear_btn, TRUE); + gtk_button_set_label(GTK_BUTTON(search_window.search_btn), + GTK_STOCK_FIND); + if (search_window.n_found == 0) + msg = g_strdup_printf(_("Message not found.")); + else if (search_window.n_found == 1) + msg = g_strdup_printf(_("1 message found.")); + else + msg = g_strdup_printf(_("%d messages found."), + search_window.n_found); + gtk_label_set_text(GTK_LABEL(search_window.status_label), msg); + g_free(msg); + statusbar_pop_all(); + + if (search_window.cancelled) + debug_print("* query search cancelled.\n"); + debug_print("query search finished.\n"); + + search_window.n_found = 0; + search_window.on_search = FALSE; + search_window.cancelled = FALSE; +} + +typedef struct _QueryData +{ + FolderItem *item; + gchar *folder_name; + gint count; + gint total; + gint flag; + GTimeVal tv_prev; + GSList *mlist; +#if USE_THREADS + GAsyncQueue *queue; + guint timer_tag; +#endif +} QueryData; + +static void query_search_folder_show_progress(const gchar *name, gint count, + gint total) +{ + gchar *str; + + str = g_strdup_printf(_("Searching %s (%d / %d)..."), + name, count, total); + gtk_label_set_text(GTK_LABEL(search_window.status_label), str); + g_free(str); +#ifndef USE_THREADS + ui_update(); +#endif +} + +#if USE_THREADS +static gboolean query_search_progress_func(gpointer data) +{ + QueryData *qdata = (QueryData *)data; + MsgInfo *msginfo; + + gdk_threads_enter(); + query_search_folder_show_progress(qdata->folder_name, + g_atomic_int_get(&qdata->count), + qdata->total); + while ((msginfo = g_async_queue_try_pop(qdata->queue))) + query_search_append_msg(msginfo); + gdk_threads_leave(); + + return TRUE; +} +#endif + +static gpointer query_search_folder_func(gpointer data) +{ + QueryData *qdata = (QueryData *)data; + GSList *mlist, *cur; + FilterInfo fltinfo; + GTimeVal tv_cur; + + debug_print("query_search_folder_func start\n"); + +#if USE_THREADS + g_async_queue_ref(qdata->queue); +#endif + + mlist = qdata->mlist; + + memset(&fltinfo, 0, sizeof(FilterInfo)); + + debug_print("requires_full_headers: %d\n", + search_window.requires_full_headers); + debug_print("start query search: %s\n", + qdata->item->path ? qdata->item->path : ""); + + for (cur = mlist; cur != NULL; cur = cur->next) { + MsgInfo *msginfo = (MsgInfo *)cur->data; + GSList *hlist; + + g_atomic_int_add(&qdata->count, 1); + + g_get_current_time(&tv_cur); + if ((tv_cur.tv_sec - qdata->tv_prev.tv_sec) * G_USEC_PER_SEC + + tv_cur.tv_usec - qdata->tv_prev.tv_usec > + PROGRESS_UPDATE_INTERVAL * 1000) { +#ifndef USE_THREADS + query_search_folder_show_progress(qdata->folder_name, + qdata->count, + qdata->total); +#endif + qdata->tv_prev = tv_cur; + } + + if (search_window.cancelled) + break; + + fltinfo.flags = msginfo->flags; + if (search_window.requires_full_headers) { + gchar *file; + + file = procmsg_get_message_file(msginfo); + hlist = procheader_get_header_list_from_file(file); + g_free(file); + } else + hlist = procheader_get_header_list_from_msginfo + (msginfo); + if (!hlist) + continue; + + if (filter_match_rule(search_window.rule, msginfo, hlist, + &fltinfo)) { +#if USE_THREADS + g_async_queue_push(qdata->queue, msginfo); +#else + query_search_append_msg(msginfo); +#endif + cur->data = NULL; + search_window.n_found++; + } + + procheader_header_list_destroy(hlist); + } + +#if USE_THREADS + g_async_queue_unref(qdata->queue); +#endif + + g_atomic_int_set(&qdata->flag, 1); + g_main_context_wakeup(NULL); + + debug_print("query_search_folder_func end\n"); + + return GINT_TO_POINTER(0); +} + +static void query_search_folder(FolderItem *item) +{ + gchar *str; + QueryData data = {item}; +#if USE_THREADS + GThread *thread; + MsgInfo *msginfo; +#endif + + if (!item->path || item->stype == F_VIRTUAL) + return; + + data.folder_name = g_path_get_basename(item->path); + str = g_strdup_printf(_("Searching %s ..."), data.folder_name); + gtk_label_set_text(GTK_LABEL(search_window.status_label), str); + g_free(str); + g_get_current_time(&data.tv_prev); +#ifndef USE_THREADS + ui_update(); +#endif + + if (search_window.cancelled) { + g_free(data.folder_name); + return; + } + + if (item->opened) + summary_write_cache(main_window_get()->summaryview); + + procmsg_set_auto_decrypt_message(FALSE); + + data.mlist = folder_item_get_msg_list(item, TRUE); + data.total = g_slist_length(data.mlist); + +#if USE_THREADS + data.queue = g_async_queue_new(); + data.timer_tag = g_timeout_add(PROGRESS_UPDATE_INTERVAL, + query_search_progress_func, &data); + thread = g_thread_create(query_search_folder_func, &data, TRUE, NULL); + + debug_print("query_search_folder: thread started\n"); + while (g_atomic_int_get(&data.flag) == 0) + gtk_main_iteration(); + log_window_flush(); + + while ((msginfo = g_async_queue_try_pop(data.queue))) + query_search_append_msg(msginfo); + + g_source_remove(data.timer_tag); + g_thread_join(thread); + debug_print("query_search_folder: thread exited\n"); + + g_async_queue_unref(data.queue); +#else /* !USE_THREADS */ + query_search_folder_func(&data); +#endif + + procmsg_msg_list_free(data.mlist); + procmsg_set_auto_decrypt_message(TRUE); + g_free(data.folder_name); +} + +static gboolean query_search_recursive_func(GNode *node, gpointer data) +{ + FolderItem *item; + + g_return_val_if_fail(node->data != NULL, FALSE); + + item = FOLDER_ITEM(node->data); + + if (!item->path) + return FALSE; + if (search_window.exclude_trash && item->stype == F_TRASH) + return FALSE; + + query_search_folder(item); + + if (search_window.cancelled) + return TRUE; + + return FALSE; +} + +static void query_search_append_msg(MsgInfo *msginfo) +{ + GtkListStore *store = search_window.store; + GtkTreeIter iter; + gchar *folder; + gchar date_buf[80]; + const gchar *subject, *from, *date; + gchar *id; + + id = folder_item_get_identifier(msginfo->folder); + folder = g_path_get_basename(id); + g_free(id); + subject = msginfo->subject ? msginfo->subject : _("(No Subject)"); + from = msginfo->from ? msginfo->from : _("(No From)"); + if (msginfo->date_t) { + procheader_date_get_localtime(date_buf, sizeof(date_buf), + msginfo->date_t); + date = date_buf; + } else if (msginfo->date) + date = msginfo->date; + else + date = _("(No Date)"); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + COL_FOLDER, folder, + COL_SUBJECT, subject, + COL_FROM, from, + COL_DATE, date, + COL_MSGINFO, msginfo, + -1); + + g_free(folder); +} + +static void query_search_clear_list(void) +{ + GtkTreeIter iter; + GtkTreeModel *model = GTK_TREE_MODEL(search_window.store); + MsgInfo *msginfo; + + gtkut_tree_sortable_unset_sort_column_id + (GTK_TREE_SORTABLE(search_window.store)); + + if (!gtk_tree_model_get_iter_first(model, &iter)) + return; + + do { + gtk_tree_model_get(model, &iter, COL_MSGINFO, &msginfo, -1); + procmsg_msginfo_free(msginfo); + } while (gtk_tree_model_iter_next(model, &iter)); + + gtk_list_store_clear(search_window.store); +} + +static void query_search_hbox_added(CondHBox *hbox) +{ + g_signal_connect(hbox->key_entry, "activate", + G_CALLBACK(query_search_entry_activated), NULL); +} + +static void row_activated(GtkTreeView *treeview, GtkTreePath *path, + GtkTreeViewColumn *column, gpointer data) +{ + query_search_open_msg(FALSE); +} + +static gboolean row_selected(GtkTreeSelection *selection, + GtkTreeModel *model, GtkTreePath *path, + gboolean cur_selected, gpointer data) +{ + return TRUE; +} + +static gboolean treeview_button_pressed(GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + GtkTreeIter iter; + GtkTreeModel *model = GTK_TREE_MODEL(search_window.store); + GtkWidget *menu; + GtkTreeSelection *selection; + GtkTreePath *path; + GtkTreeViewColumn *column = NULL; + gboolean is_selected; + gint px, py; + + debug_print("treeview_button_pressed\n"); + + if (!event) + return FALSE; + + if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), + event->x, event->y, &path, &column, + NULL, NULL)) + return FALSE; + + // ?? + gtk_widget_get_pointer(widget, &px, &py); + if (py == (gint)event->y) { + gtk_tree_path_free(path); + return FALSE; + } + + if (!gtk_tree_model_get_iter(model, &iter, path)) { + gtk_tree_path_free(path); + return FALSE; + } + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + is_selected = gtk_tree_selection_path_is_selected(selection, path); + gtk_tree_path_free(path); + + if (event->button == 3) { + menu = gtk_ui_manager_get_widget(search_window.ui, + "/PopUpMenu"); + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, + event->button, event->time); + if (is_selected) + return TRUE; + } + + return FALSE; +} + +static void query_search_clear(GtkButton *button, gpointer data) +{ + CondHBox *cond_hbox; + + if (search_window.on_search) + return; + + prefs_filter_edit_clear_cond_edit(search_window.cond_edit); + prefs_filter_set_header_list(NULL); + prefs_filter_edit_set_header_list(search_window.cond_edit, NULL); + cond_hbox = prefs_filter_edit_cond_hbox_create(search_window.cond_edit); + prefs_filter_edit_set_cond_hbox_widgets(cond_hbox, PF_COND_HEADER); + prefs_filter_edit_insert_cond_hbox + (search_window.cond_edit, cond_hbox, -1); + if (search_window.cond_edit->add_hbox) + search_window.cond_edit->add_hbox(cond_hbox); + + gtk_label_set_text(GTK_LABEL(search_window.status_label), ""); + + query_search_clear_list(); +} + +static void query_select_folder(GtkButton *button, gpointer data) +{ + FolderItem *item; + gchar *id; + + item = foldersel_folder_sel(NULL, FOLDER_SEL_ALL, NULL); + if (!item || item->stype == F_VIRTUAL) + return; + + id = folder_item_get_identifier(item); + if (id) { + gtk_entry_set_text(GTK_ENTRY(search_window.folder_entry), id); + g_free(id); + } +} + +static void query_search_clicked(GtkButton *button, gpointer data) +{ + if (search_window.on_search) + search_window.cancelled = TRUE; + else + query_search_query(); +} + +static gint query_search_save_dialog_deleted(GtkWidget *widget, + GdkEventAny *event, gpointer data) +{ + QuerySearchSaveDialog *dialog = (QuerySearchSaveDialog *)data; + + dialog->cancelled = TRUE; + dialog->finished = TRUE; + return TRUE; +} + +static gint query_search_save_dialog_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gpointer data) +{ + QuerySearchSaveDialog *dialog = (QuerySearchSaveDialog *)data; + + if (event && event->keyval == GDK_Escape) { + dialog->cancelled = TRUE; + dialog->finished = TRUE; + } + return FALSE; +} + +static void query_search_save_dialog_select_folder(GtkButton *button, + gpointer data) +{ + QuerySearchSaveDialog *dialog = (QuerySearchSaveDialog *)data; + FolderItem *item; + gchar *id; + + item = foldersel_folder_sel(NULL, FOLDER_SEL_ALL, NULL); + if (!item || item->no_sub || item->stype == F_VIRTUAL) + return; + + id = folder_item_get_identifier(item); + if (id) { + gtk_entry_set_text(GTK_ENTRY(dialog->folder_entry), id); + g_free(id); + } +} + +static void query_search_save_activated(GtkEditable *editable, gpointer data) +{ + QuerySearchSaveDialog *dialog = (QuerySearchSaveDialog *)data; + + gtk_button_clicked(GTK_BUTTON(dialog->ok_btn)); +} + +static void query_search_save_ok(GtkButton *button, gpointer data) +{ + QuerySearchSaveDialog *dialog = (QuerySearchSaveDialog *)data; + + dialog->finished = TRUE; +} + +static void query_search_save_cancel(GtkButton *button, gpointer data) +{ + QuerySearchSaveDialog *dialog = (QuerySearchSaveDialog *)data; + + dialog->cancelled = TRUE; + dialog->finished = TRUE; +} + +static QuerySearchSaveDialog *query_search_save_dialog_create(void) +{ + QuerySearchSaveDialog *dialog; + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *folder_entry; + GtkWidget *folder_btn; + GtkWidget *name_entry; + + GtkWidget *confirm_area; + GtkWidget *hbbox; + GtkWidget *cancel_btn; + GtkWidget *ok_btn; + + dialog = g_new0(QuerySearchSaveDialog, 1); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), _("Save as search folder")); + gtk_widget_set_size_request(window, 400 * gtkut_get_dpi_multiplier(), -1); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, FALSE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(query_search_save_dialog_deleted), + dialog); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(query_search_save_dialog_key_pressed), + dialog); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + manage_window_set_transient(GTK_WINDOW(window)); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add(GTK_CONTAINER(window), vbox); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new(_("Location:")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + folder_entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(hbox), folder_entry, TRUE, TRUE, 0); + + folder_btn = gtk_button_new_with_label("..."); + gtk_box_pack_start(GTK_BOX(hbox), folder_btn, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(folder_btn), "clicked", + G_CALLBACK(query_search_save_dialog_select_folder), + dialog); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new(_("Folder name:")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + name_entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(hbox), name_entry, TRUE, TRUE, 0); + g_signal_connect(G_OBJECT(name_entry), "activate", + G_CALLBACK(query_search_save_activated), dialog); + + confirm_area = gtk_hbox_new(FALSE, 12); + gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0); + + gtkut_stock_button_set_create(&hbbox, + &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(confirm_area), hbbox, FALSE, FALSE, 0); + GTK_WIDGET_SET_FLAGS(ok_btn, GTK_CAN_DEFAULT); + gtk_widget_grab_default(ok_btn); + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(query_search_save_ok), dialog); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(query_search_save_cancel), dialog); + + gtk_widget_grab_focus(name_entry); + + gtk_widget_show_all(window); + + dialog->window = window; + dialog->folder_entry = folder_entry; + dialog->name_entry = name_entry; + dialog->ok_btn = ok_btn; + dialog->cancel_btn = cancel_btn; + dialog->cancelled = FALSE; + dialog->finished = FALSE; + + return dialog; +} + +static void query_search_save_dialog_destroy(QuerySearchSaveDialog *dialog) +{ + gtk_widget_destroy(dialog->window); + g_free(dialog); +} + +static FolderItem *query_search_create_vfolder(FolderItem *parent, + const gchar *name) +{ + gchar *path; + gchar *fs_name; + gchar *fullpath; + FolderItem *item; + + g_return_val_if_fail(parent != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + path = folder_item_get_path(parent); + fs_name = g_filename_from_utf8(name, -1, NULL, NULL, NULL); + fullpath = g_strconcat(path, G_DIR_SEPARATOR_S, + fs_name ? fs_name : name, NULL); + g_free(fs_name); + g_free(path); + + if (make_dir_hier(fullpath) < 0) { + g_free(fullpath); + return NULL; + } + + if (parent->path) + path = g_strconcat(parent->path, "/", name, NULL); + else + path = g_strdup(name); + + item = folder_item_new(name, path); + item->stype = F_VIRTUAL; + item->no_sub = TRUE; + folder_item_append(parent, item); + + g_free(path); + + return item; +} + +static void query_search_vfolder_update_rule(FolderItem *item) +{ + GSList list; + FilterRule *rule; + gchar *file; + gchar *path; + + rule = query_search_dialog_to_rule(item->name, NULL); + list.data = rule; + list.next = NULL; + + path = folder_item_get_path(item); + file = g_strconcat(path, G_DIR_SEPARATOR_S, FILTER_LIST, NULL); + filter_write_file(&list, file); + g_free(file); + g_free(path); + + filter_rule_free(rule); +} + +static void query_search_save(GtkButton *button, gpointer data) +{ + QuerySearchSaveDialog *dialog; + const gchar *id, *name; + FolderItem *parent, *item; + + dialog = query_search_save_dialog_create(); + id = gtk_entry_get_text(GTK_ENTRY(search_window.folder_entry)); + if (id && *id) + gtk_entry_set_text(GTK_ENTRY(dialog->folder_entry), id); + + while (!dialog->finished) + gtk_main_iteration(); + + if (dialog->cancelled) { + query_search_save_dialog_destroy(dialog); + return; + } + + id = gtk_entry_get_text(GTK_ENTRY(dialog->folder_entry)); + parent = folder_find_item_from_identifier(id); + name = gtk_entry_get_text(GTK_ENTRY(dialog->name_entry)); + if (parent && name && *name) { + if (folder_find_child_item_by_name(parent, name)) { + alertpanel_error(_("The folder `%s' already exists."), + name); + } else { + item = query_search_create_vfolder(parent, name); + if (item) { + query_search_vfolder_update_rule(item); + folderview_append_item(folderview_get(), + NULL, item, TRUE); + folder_write_list(); + } + } + } + + query_search_save_dialog_destroy(dialog); +} + +static void query_search_close(GtkButton *button, gpointer data) +{ + if (search_window.on_search) + search_window.cancelled = TRUE; + gtk_widget_hide(search_window.window); +} + +static void query_search_entry_activated(GtkWidget *widget, gpointer data) +{ + gtk_button_clicked(GTK_BUTTON(search_window.search_btn)); +} + +static gint query_search_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + gtk_button_clicked(GTK_BUTTON(search_window.close_btn)); + return TRUE; +} + +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) { + if (search_window.on_search) + gtk_button_clicked + (GTK_BUTTON(search_window.search_btn)); + else + gtk_button_clicked(GTK_BUTTON(search_window.close_btn)); + return TRUE; + } + return FALSE; +} + +static GSList *query_search_get_selected_msg_list(void) +{ + GtkTreeModel *model = GTK_TREE_MODEL(search_window.store); + GtkTreeSelection *selection; + GList *list, *cur; + GtkTreePath *path; + GtkTreeIter iter; + GSList *mlist = NULL; + MsgInfo *msginfo; + + selection = gtk_tree_view_get_selection + (GTK_TREE_VIEW(search_window.treeview)); + list = gtk_tree_selection_get_selected_rows(selection, NULL); + if (!list) + return NULL; + + for (cur = list; cur != NULL; cur = cur->next) { + path = cur->data; + if (!gtk_tree_model_get_iter(model, &iter, path)) + break; + msginfo = NULL; + gtk_tree_model_get(model, &iter, COL_MSGINFO, &msginfo, -1); + if (msginfo) + mlist = g_slist_prepend(mlist, msginfo); + } + mlist = g_slist_reverse(mlist); + + g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL); + g_list_free(list); + + return mlist; +} + +static void query_search_open_msg(gboolean new_window) +{ + GSList *mlist; + MsgInfo *msginfo; + MessageView *msgview; + + mlist = query_search_get_selected_msg_list(); + if (!mlist) + return; + msginfo = (MsgInfo *)mlist->data; + g_slist_free(mlist); + + if (new_window || !summary_select_by_msginfo(main_window_get()->summaryview, msginfo)) { + msgview = messageview_create_with_new_window(); + messageview_show(msgview, msginfo, FALSE); + statusbar_pop_all(); + } +} + +static void query_search_reply_forward(ComposeMode mode) +{ + GSList *mlist; + MsgInfo *msginfo; + + mlist = query_search_get_selected_msg_list(); + if (!mlist) + return; + msginfo = (MsgInfo *)mlist->data; + + if (prefs_common.reply_with_quote) + mode |= COMPOSE_WITH_QUOTE; + + switch (COMPOSE_MODE(mode)) { + case COMPOSE_REPLY: + case COMPOSE_REPLY_TO_SENDER: + case COMPOSE_REPLY_TO_ALL: + case COMPOSE_REPLY_TO_LIST: + compose_reply(msginfo, msginfo->folder, mode, NULL); + break; + case COMPOSE_FORWARD: + compose_forward(mlist, NULL, FALSE, NULL); + break; + case COMPOSE_FORWARD_AS_ATTACH: + compose_forward(mlist, NULL, TRUE, NULL); + break; + case COMPOSE_REDIRECT: + compose_redirect(msginfo, msginfo->folder); + break; + default: + g_warning("query_search_reply_forward: invalid mode: %d", mode); + } + + g_slist_free(mlist); +} + +static void query_search_move_copy(gboolean is_copy) +{ + GSList *mlist, *cur; + FolderItem *dest; + MsgInfo *msginfo; + + debug_print("query_search_move_copy: is_copy: %d\n", is_copy); + + mlist = query_search_get_selected_msg_list(); + if (!mlist) + return; + + if (is_copy) + dest = foldersel_folder_sel_full(NULL, FOLDER_SEL_COPY, NULL, + _("Select folder to copy")); + else + dest = foldersel_folder_sel_full(NULL, FOLDER_SEL_MOVE, NULL, + _("Select folder to move")); + if (!dest || dest->stype == F_VIRTUAL || !FOLDER_ITEM_CAN_ADD(dest)) { + g_slist_free(mlist); + return; + } + if (summary_is_locked(main_window_get()->summaryview)) { + g_slist_free(mlist); + return; + } + + for (cur = mlist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + if (!msginfo) + continue; + + if (is_copy) { + folder_item_copy_msg(dest, msginfo); + } else { + folder_item_move_msg(dest, msginfo); + } + } + + folderview_update_all_updated(TRUE); + g_slist_free(mlist); +} + +static void query_search_create_filter(FilterCreateType type) +{ + GSList *mlist; + MsgInfo *msginfo; + gchar *header = NULL; + gchar *key = NULL; + + mlist = query_search_get_selected_msg_list(); + if (!mlist) + return; + msginfo = (MsgInfo *)mlist->data; + g_slist_free(mlist); + + filter_get_keyword_from_msg(msginfo, &header, &key, type); + prefs_filter_open(msginfo, header, key); + + g_free(header); + g_free(key); +} + +static void reply_cb(void) +{ + query_search_reply_forward(COMPOSE_REPLY); +} + +static void reply_all_cb(void) +{ + query_search_reply_forward(COMPOSE_REPLY_TO_ALL); +} + +static void reply_sender_cb(void) +{ + query_search_reply_forward(COMPOSE_REPLY_TO_SENDER); +} + +static void reply_list_cb(void) +{ + query_search_reply_forward(COMPOSE_REPLY_TO_LIST); +} + +static void forward_cb(void) +{ + query_search_reply_forward(COMPOSE_FORWARD); +} + +static void forward_attach_cb(void) +{ + query_search_reply_forward(COMPOSE_FORWARD_AS_ATTACH); +} + +static void redirect_cb(void) +{ + query_search_reply_forward(COMPOSE_REDIRECT); +} + +static void move_cb(void) +{ + query_search_move_copy(FALSE); +} + +static void copy_cb(void) +{ + query_search_move_copy(TRUE); +} + +static void add_address_cb(void) +{ + GSList *mlist; + MsgInfo *msginfo; + gchar from[BUFFSIZE]; + + mlist = query_search_get_selected_msg_list(); + if (!mlist) + return; + msginfo = (MsgInfo *)mlist->data; + g_slist_free(mlist); + + strncpy2(from, msginfo->from, sizeof(from)); + extract_address(from); + addressbook_add_contact(msginfo->fromname, from, NULL); +} + +static void create_filter_auto_cb(void) +{ + query_search_create_filter(FLT_BY_AUTO); +} + +static void create_filter_from_cb(void) +{ + query_search_create_filter(FLT_BY_FROM); +} + +static void create_filter_to_cb(void) +{ + query_search_create_filter(FLT_BY_TO); +} + +static void create_filter_subject_cb(void) +{ + query_search_create_filter(FLT_BY_SUBJECT); +} + +static void open_cb(void) +{ + query_search_open_msg(TRUE); +} + +static void open_source_cb(void) +{ + GSList *mlist; + MsgInfo *msginfo; + SourceWindow *srcwin; + + mlist = query_search_get_selected_msg_list(); + if (!mlist) + return; + msginfo = (MsgInfo *)mlist->data; + g_slist_free(mlist); + + srcwin = source_window_create(); + source_window_show_msg(srcwin, msginfo); + source_window_show(srcwin); +} + +static void print_cb(void) +{ + GSList *mlist; + + mlist = query_search_get_selected_msg_list(); + if (!mlist) + return; + printing_print_messages(mlist, FALSE); + g_slist_free(mlist); +} + +static gint query_search_cmp_by_folder(GtkTreeModel *model, + GtkTreeIter *a, GtkTreeIter *b, + gpointer data) +{ + gchar *folder_a = NULL, *folder_b = NULL; + MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; + gint ret; + + gtk_tree_model_get(model, a, COL_FOLDER, &folder_a, COL_MSGINFO, + &msginfo_a, -1); + gtk_tree_model_get(model, b, COL_FOLDER, &folder_b, COL_MSGINFO, + &msginfo_b, -1); + + if (!folder_a || !folder_b || !msginfo_a || !msginfo_b) + return 0; + + ret = g_ascii_strcasecmp(folder_a, folder_b); + return (ret != 0) ? ret : (msginfo_a->date_t - msginfo_b->date_t); +} + +static gint query_search_cmp_by_subject(GtkTreeModel *model, + GtkTreeIter *a, GtkTreeIter *b, + gpointer data) +{ + MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; + gint ret; + + gtk_tree_model_get(model, a, COL_MSGINFO, &msginfo_a, -1); + gtk_tree_model_get(model, b, COL_MSGINFO, &msginfo_b, -1); + + if (!msginfo_a || !msginfo_b) + return 0; + + if (!msginfo_a->subject) + return -(msginfo_b->subject != NULL); + if (!msginfo_b->subject) + return (msginfo_a->subject != NULL); + + ret = subject_compare_for_sort(msginfo_a->subject, msginfo_b->subject); + return (ret != 0) ? ret : (msginfo_a->date_t - msginfo_b->date_t); +} + +static gint query_search_cmp_by_from(GtkTreeModel *model, + GtkTreeIter *a, GtkTreeIter *b, + gpointer data) +{ + MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; + gint ret; + + gtk_tree_model_get(model, a, COL_MSGINFO, &msginfo_a, -1); + gtk_tree_model_get(model, b, COL_MSGINFO, &msginfo_b, -1); + + if (!msginfo_a || !msginfo_b) + return 0; + + if (!msginfo_a->fromname) + return -(msginfo_b->fromname != NULL); + if (!msginfo_b->fromname) + return (msginfo_a->fromname != NULL); + + ret = g_ascii_strcasecmp(msginfo_a->fromname, msginfo_b->fromname); + return (ret != 0) ? ret : (msginfo_a->date_t - msginfo_b->date_t); +} + +static gint query_search_cmp_by_date(GtkTreeModel *model, + GtkTreeIter *a, GtkTreeIter *b, + gpointer data) +{ + MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; + + gtk_tree_model_get(model, a, COL_MSGINFO, &msginfo_a, -1); + gtk_tree_model_get(model, b, COL_MSGINFO, &msginfo_b, -1); + + if (!msginfo_a || !msginfo_b) + return 0; + + return msginfo_a->date_t - msginfo_b->date_t; +} diff --git a/src/query_search.h b/src/query_search.h new file mode 100644 index 0000000..82f64d3 --- /dev/null +++ b/src/query_search.h @@ -0,0 +1,29 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2006 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __QUERY_SEARCH_H__ +#define __QUERY_SEARCH_H__ + +#include + +#include "folder.h" + +void query_search (FolderItem *item); + +#endif /* __QUERY_SEARCH_H__ */ diff --git a/src/quick_search.c b/src/quick_search.c new file mode 100644 index 0000000..c4a3ca8 --- /dev/null +++ b/src/quick_search.c @@ -0,0 +1,447 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "summaryview.h" +#include "quick_search.h" +#include "filter.h" +#include "procheader.h" +#include "menu.h" +#include "addressbook.h" + +static const struct { + QSearchCondType type; + FilterCondType ftype; +} qsearch_cond_types[] = { + {QS_ALL, -1}, + {QS_UNREAD, FLT_COND_UNREAD}, + {QS_MARK, FLT_COND_MARK}, + {QS_CLABEL, FLT_COND_COLOR_LABEL}, + {QS_MIME, FLT_COND_MIME}, + {QS_W1DAY, -1}, + {QS_LAST5, -1}, + {QS_LAST7, -1}, + {QS_IN_ADDRESSBOOK, -1}, + {QS_LAST30, -1} +}; + +static GdkColor dim_color = {0, COLOR_DIM, COLOR_DIM, COLOR_DIM}; + +static void menu_activated (GtkWidget *menuitem, + QuickSearch *qsearch); +static gboolean entry_focus_in (GtkWidget *entry, + GdkEventFocus *event, + QuickSearch *qsearch); +static gboolean entry_focus_out (GtkWidget *entry, + GdkEventFocus *event, + QuickSearch *qsearch); +static void entry_changed (GtkWidget *entry, + QuickSearch *qsearch); +static void entry_activated (GtkWidget *entry, + QuickSearch *qsearch); +static gboolean entry_key_pressed (GtkWidget *treeview, + GdkEventKey *event, + QuickSearch *qsearch); +static void clear_clicked (GtkWidget *button, + QuickSearch *qsearch); + + +QuickSearch *quick_search_create(SummaryView *summaryview) +{ + QuickSearch *qsearch; + GtkWidget *hbox; + GtkWidget *optmenu; + GtkWidget *menu; + GtkWidget *menuitem; + GtkWidget *hbox2; + GtkWidget *label; + GtkWidget *entry; + GtkTooltips *tip; + GtkWidget *vbox; + GtkWidget *clear_btn; + GtkWidget *image; + GtkWidget *status_label; + + qsearch = g_new0(QuickSearch, 1); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 2); + + optmenu = gtk_option_menu_new(); + gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0); + +#define COND_MENUITEM_ADD(str, action) \ +{ \ + MENUITEM_ADD(menu, menuitem, str, action); \ + g_signal_connect(G_OBJECT(menuitem), "activate", \ + G_CALLBACK(menu_activated), qsearch); \ +} + + menu = gtk_menu_new(); + COND_MENUITEM_ADD(_("All"), QS_ALL); + COND_MENUITEM_ADD(_("Unread"), QS_UNREAD); + COND_MENUITEM_ADD(_("Marked"), QS_MARK); + COND_MENUITEM_ADD(_("Have color label"), QS_CLABEL); + COND_MENUITEM_ADD(_("Have attachment"), QS_MIME); + MENUITEM_ADD(menu, menuitem, NULL, 0); + COND_MENUITEM_ADD(_("Within 1 day"), QS_W1DAY); + COND_MENUITEM_ADD(_("Last 5 days"), QS_LAST5); + COND_MENUITEM_ADD(_("Last 7 days"), QS_LAST7); + COND_MENUITEM_ADD(_("Last 30 days"), QS_LAST30); + MENUITEM_ADD(menu, menuitem, NULL, 0); + COND_MENUITEM_ADD(_("In addressbook"), QS_IN_ADDRESSBOOK); + gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu); + +#undef COND_MENUITEM_ADD + + hbox2 = gtk_hbox_new(FALSE, 0); + gtk_widget_set_size_request(hbox2, 8, -1); + gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 0); + + label = gtk_label_new(_("Search:")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + hbox2 = gtk_hbox_new(FALSE, 0); + gtk_widget_set_size_request(hbox2, 4, -1); + gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 0); + + entry = gtk_entry_new(); + gtk_widget_set_size_request(entry, 200 * gtkut_get_dpi_multiplier(), -1); + gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(entry), "focus-in-event", + G_CALLBACK(entry_focus_in), qsearch); + g_signal_connect(G_OBJECT(entry), "focus-out-event", + G_CALLBACK(entry_focus_out), qsearch); + g_signal_connect(G_OBJECT(entry), "changed", + G_CALLBACK(entry_changed), qsearch); + g_signal_connect(G_OBJECT(entry), "activate", + G_CALLBACK(entry_activated), qsearch); + g_signal_connect(G_OBJECT(entry), "key_press_event", + G_CALLBACK(entry_key_pressed), qsearch); + + tip = gtk_tooltips_new(); + gtk_tooltips_set_tip(tip, entry, _("Search for Subject or From"), NULL); + + hbox2 = gtk_hbox_new(FALSE, 0); + gtk_widget_set_size_request(hbox2, 2, -1); + gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 0); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0); + + clear_btn = gtk_button_new(); + gtk_button_set_relief(GTK_BUTTON(clear_btn), GTK_RELIEF_NONE); + gtk_widget_set_size_request(clear_btn, 20, 20); + image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU); + gtk_container_add(GTK_CONTAINER(clear_btn), image); + GTK_WIDGET_UNSET_FLAGS(clear_btn, GTK_CAN_FOCUS); + gtk_box_pack_start(GTK_BOX(vbox), clear_btn, TRUE, FALSE, 0); + g_signal_connect(G_OBJECT(clear_btn), "clicked", + G_CALLBACK(clear_clicked), qsearch); + + hbox2 = gtk_hbox_new(FALSE, 0); + gtk_widget_set_size_request(hbox2, 4, -1); + gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 0); + + status_label = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(hbox), status_label, FALSE, FALSE, 0); + + qsearch->hbox = hbox; + qsearch->optmenu = optmenu; + qsearch->menu = menu; + qsearch->label = label; + qsearch->entry = entry; + qsearch->clear_btn = clear_btn; + qsearch->status_label = status_label; + qsearch->summaryview = summaryview; + summaryview->qsearch = qsearch; + qsearch->entry_entered = FALSE; + + gtk_widget_show_all(hbox); + gtk_widget_hide(clear_btn); + + entry_focus_out(entry, NULL, qsearch); + + return qsearch; +} + +void quick_search_clear_entry(QuickSearch *qsearch) +{ + qsearch->entry_entered = FALSE; + if (GTK_WIDGET_HAS_FOCUS(qsearch->entry)) + entry_focus_in(qsearch->entry, NULL, qsearch); + else + entry_focus_out(qsearch->entry, NULL, qsearch); + + gtk_label_set_text(GTK_LABEL(qsearch->status_label), ""); + gtk_widget_hide(qsearch->clear_btn); +} + +GSList *quick_search_filter(QuickSearch *qsearch, QSearchCondType type, + const gchar *key) +{ + SummaryView *summaryview = qsearch->summaryview; + FilterCondType ftype; + FilterRule *status_rule = NULL; + FilterRule *rule = NULL; + FilterCond *cond; + FilterInfo fltinfo; + GSList *cond_list = NULL; + GSList *rule_list = NULL; + GSList *flt_mlist = NULL; + GSList *cur; + gint count = 0, total = 0; + gchar status_text[1024]; + gboolean dmode; + + if (!summaryview->all_mlist) + return NULL; + + debug_print("quick_search_filter: filtering summary (type: %d)\n", + type); + + switch (type) { + case QS_UNREAD: + case QS_MARK: + case QS_CLABEL: + case QS_MIME: + ftype = qsearch_cond_types[type].ftype; + cond = filter_cond_new(ftype, 0, 0, NULL, NULL); + cond_list = g_slist_append(cond_list, cond); + status_rule = filter_rule_new("Status filter rule", FLT_OR, + cond_list, NULL); + break; + case QS_W1DAY: + cond = filter_cond_new(FLT_COND_AGE_GREATER, 0, FLT_NOT_MATCH, + NULL, "1"); + cond_list = g_slist_append(cond_list, cond); + status_rule = filter_rule_new("Status filter rule", FLT_OR, + cond_list, NULL); + break; + case QS_LAST5: + cond = filter_cond_new(FLT_COND_AGE_GREATER, 0, FLT_NOT_MATCH, + NULL, "5"); + cond_list = g_slist_append(cond_list, cond); + status_rule = filter_rule_new("Status filter rule", FLT_OR, + cond_list, NULL); + break; + case QS_LAST7: + cond = filter_cond_new(FLT_COND_AGE_GREATER, 0, FLT_NOT_MATCH, + NULL, "7"); + cond_list = g_slist_append(cond_list, cond); + status_rule = filter_rule_new("Status filter rule", FLT_OR, + cond_list, NULL); + break; + case QS_LAST30: + cond = filter_cond_new(FLT_COND_AGE_GREATER, 0, FLT_NOT_MATCH, + NULL, "30"); + cond_list = g_slist_append(cond_list, cond); + status_rule = filter_rule_new("Status filter rule", FLT_OR, + cond_list, NULL); + break; + case QS_IN_ADDRESSBOOK: + cond = filter_cond_new(FLT_COND_HEADER, FLT_IN_ADDRESSBOOK, 0, + "From", NULL); + cond_list = g_slist_append(cond_list, cond); + status_rule = filter_rule_new("Status filter rule", FLT_OR, + cond_list, NULL); + break; + case QS_ALL: + default: + break; + } + + if (key) { + gchar **keys; + gint i; + + keys = g_strsplit(key, " ", -1); + for (i = 0; keys[i] != NULL; i++) { + cond_list = NULL; + + if (keys[i] == '\0') + continue; + + cond = filter_cond_new(FLT_COND_HEADER, FLT_CONTAIN, 0, + "Subject", keys[i]); + cond_list = g_slist_append(cond_list, cond); + cond = filter_cond_new(FLT_COND_HEADER, FLT_CONTAIN, 0, + "From", keys[i]); + cond_list = g_slist_append(cond_list, cond); + if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item)) { + cond = filter_cond_new(FLT_COND_TO_OR_CC, FLT_CONTAIN, + 0, NULL, keys[i]); + cond_list = g_slist_append(cond_list, cond); + } + + if (cond_list) { + rule = filter_rule_new("Quick search rule", + FLT_OR, cond_list, NULL); + rule_list = g_slist_append(rule_list, rule); + } + } + g_strfreev(keys); + } + + memset(&fltinfo, 0, sizeof(FilterInfo)); + dmode = get_debug_mode(); + set_debug_mode(FALSE); + + for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) { + MsgInfo *msginfo = (MsgInfo *)cur->data; + GSList *hlist = NULL; + gboolean matched = TRUE; + + total++; + + if (status_rule) { + if (type == QS_IN_ADDRESSBOOK) + hlist = procheader_get_header_list_from_msginfo + (msginfo); + if (!filter_match_rule(status_rule, msginfo, hlist, + &fltinfo)) { + if (hlist) + procheader_header_list_destroy(hlist); + continue; + } + } + + if (rule_list) { + GSList *rcur; + + if (!hlist) + hlist = procheader_get_header_list_from_msginfo + (msginfo); + + /* AND keyword match */ + for (rcur = rule_list; rcur != NULL; rcur = rcur->next) { + rule = (FilterRule *)rcur->data; + if (!filter_match_rule(rule, msginfo, hlist, &fltinfo)) { + matched = FALSE; + break; + } + } + } + + if (matched) { + flt_mlist = g_slist_prepend(flt_mlist, msginfo); + count++; + } + + if (hlist) + procheader_header_list_destroy(hlist); + } + flt_mlist = g_slist_reverse(flt_mlist); + + set_debug_mode(dmode); + + if (status_rule || rule) { + if (count > 0) + g_snprintf(status_text, sizeof(status_text), + _("%1$d in %2$d matched"), count, total); + else + g_snprintf(status_text, sizeof(status_text), + _("No messages matched")); + gtk_label_set_text(GTK_LABEL(qsearch->status_label), + status_text); + } else + gtk_label_set_text(GTK_LABEL(qsearch->status_label), ""); + + filter_rule_list_free(rule_list); + filter_rule_free(status_rule); + + return flt_mlist; +} + +static void menu_activated(GtkWidget *menuitem, QuickSearch *qsearch) +{ + summary_qsearch(qsearch->summaryview); +} + +static gboolean entry_focus_in(GtkWidget *entry, GdkEventFocus *event, + QuickSearch *qsearch) +{ + if (!qsearch->entry_entered) { + g_signal_handlers_block_by_func(entry, entry_changed, qsearch); + gtk_entry_set_text(GTK_ENTRY(entry), ""); + gtk_widget_modify_text(entry, GTK_STATE_NORMAL, NULL); + g_signal_handlers_unblock_by_func(entry, entry_changed, qsearch); + } + + return FALSE; +} + +static gboolean entry_focus_out(GtkWidget *entry, GdkEventFocus *event, + QuickSearch *qsearch) +{ + if (!qsearch->entry_entered) { + g_signal_handlers_block_by_func(entry, entry_changed, qsearch); + gtk_widget_modify_text(entry, GTK_STATE_NORMAL, &dim_color); + gtk_entry_set_text(GTK_ENTRY(entry), _("Search for Subject or From")); + g_signal_handlers_unblock_by_func(entry, entry_changed, qsearch); + } + + return FALSE; +} + +static void entry_changed(GtkWidget *entry, QuickSearch *qsearch) +{ + const gchar *text; + + text = gtk_entry_get_text(GTK_ENTRY(entry)); + if (text && *text != '\0') { + gtk_widget_show(qsearch->clear_btn); + qsearch->entry_entered = TRUE; + } else { + gtk_widget_hide(qsearch->clear_btn); + qsearch->entry_entered = FALSE; + } +} + +static void entry_activated(GtkWidget *entry, QuickSearch *qsearch) +{ + gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1); + summary_qsearch(qsearch->summaryview); +} + +static gboolean entry_key_pressed(GtkWidget *treeview, GdkEventKey *event, + QuickSearch *qsearch) +{ + if (event && event->keyval == GDK_Escape) { + summary_qsearch_clear_entry(qsearch->summaryview); + return TRUE; + } + return FALSE; +} + +static void clear_clicked(GtkWidget *button, QuickSearch *qsearch) +{ + summary_qsearch_clear_entry(qsearch->summaryview); +} diff --git a/src/quick_search.h b/src/quick_search.h new file mode 100644 index 0000000..40010fe --- /dev/null +++ b/src/quick_search.h @@ -0,0 +1,66 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __QUICK_SEARCH_H__ +#define __QUICK_SEARCH_H__ + +#include + +typedef struct _QuickSearch QuickSearch; + +#include "summaryview.h" + +typedef enum +{ + QS_ALL, + QS_UNREAD, + QS_MARK, + QS_CLABEL, + QS_MIME, + QS_W1DAY, + QS_LAST5, + QS_LAST7, + QS_IN_ADDRESSBOOK, + QS_LAST30 +} QSearchCondType; + +struct _QuickSearch +{ + GtkWidget *hbox; + GtkWidget *optmenu; + GtkWidget *menu; + GtkWidget *label; + GtkWidget *entry; + GtkWidget *clear_btn; + GtkWidget *status_label; + + SummaryView *summaryview; + + gboolean entry_entered; +}; + +QuickSearch *quick_search_create(SummaryView *summaryview); + +void quick_search_clear_entry (QuickSearch *qsearch); + +GSList *quick_search_filter (QuickSearch *qsearch, + QSearchCondType type, + const gchar *key); + +#endif /* __QUICK_SEARCH_H__ */ diff --git a/src/quote_fmt.h b/src/quote_fmt.h new file mode 100644 index 0000000..c9c4c00 --- /dev/null +++ b/src/quote_fmt.h @@ -0,0 +1,13 @@ +#ifndef __QUOTE_FMT_H__ + +#define __QUOTE_FMT_H__ + +#define quote_fmt_parse quote_fmtparse + +gchar *quote_fmt_get_buffer(void); +void quote_fmt_init(MsgInfo *info, const gchar *my_quote_str, + const gchar *my_body); +gint quote_fmtparse(void); +void quote_fmt_scan_string(const gchar *str); + +#endif /* __QUOTE_FMT_H__ */ diff --git a/src/quote_fmt_lex.c b/src/quote_fmt_lex.c new file mode 100644 index 0000000..b01fe4a --- /dev/null +++ b/src/quote_fmt_lex.c @@ -0,0 +1,1900 @@ +#line 2 "lex.yy.c" + +#line 4 "lex.yy.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 33 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE quote_fmtrestart(quote_fmtin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int quote_fmtleng; + +extern FILE *quote_fmtin, *quote_fmtout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up quote_fmttext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up quote_fmttext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef unsigned int yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via quote_fmtrestart()), so that the user can continue scanning by + * just pointing quote_fmtin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when quote_fmttext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int quote_fmtleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow quote_fmtwrap()'s to do buffer switches + * instead of setting up a fresh quote_fmtin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void quote_fmtrestart (FILE *input_file ); +void quote_fmt_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE quote_fmt_create_buffer (FILE *file,int size ); +void quote_fmt_delete_buffer (YY_BUFFER_STATE b ); +void quote_fmt_flush_buffer (YY_BUFFER_STATE b ); +void quote_fmtpush_buffer_state (YY_BUFFER_STATE new_buffer ); +void quote_fmtpop_buffer_state (void ); + +static void quote_fmtensure_buffer_stack (void ); +static void quote_fmt_load_buffer_state (void ); +static void quote_fmt_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER quote_fmt_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE quote_fmt_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE quote_fmt_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE quote_fmt_scan_bytes (yyconst char *bytes,int len ); + +void *quote_fmtalloc (yy_size_t ); +void *quote_fmtrealloc (void *,yy_size_t ); +void quote_fmtfree (void * ); + +#define yy_new_buffer quote_fmt_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + quote_fmtensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + quote_fmt_create_buffer(quote_fmtin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + quote_fmtensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + quote_fmt_create_buffer(quote_fmtin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +typedef unsigned char YY_CHAR; + +FILE *quote_fmtin = (FILE *) 0, *quote_fmtout = (FILE *) 0; + +typedef int yy_state_type; + +extern int quote_fmtlineno; + +int quote_fmtlineno = 1; + +extern char *quote_fmttext; +#define yytext_ptr quote_fmttext + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up quote_fmttext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + quote_fmtleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 35 +#define YY_END_OF_BUFFER 36 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[44] = + { 0, + 0, 0, 36, 34, 18, 19, 34, 34, 34, 32, + 33, 16, 4, 5, 12, 3, 13, 8, 1, 2, + 10, 14, 9, 15, 11, 6, 7, 25, 28, 23, + 24, 30, 29, 31, 26, 27, 20, 17, 19, 18, + 21, 22, 0 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 5, 1, 1, 1, 1, 1, 1, 6, + 1, 1, 7, 1, 1, 1, 8, 9, 1, 1, + 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 11, 1, 1, 1, 1, 1, 1, 12, 13, + + 1, 14, 1, 1, 15, 1, 1, 1, 16, 17, + 1, 1, 18, 19, 20, 21, 1, 1, 1, 1, + 1, 1, 22, 1, 23, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int32_t yy_meta[24] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1 + } ; + +static yyconst flex_int16_t yy_base[45] = + { 0, + 0, 22, 6, 99, 99, 99, 42, 58, 75, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 0 + } ; + +static yyconst flex_int16_t yy_def[45] = + { 0, + 44, 44, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 0, 43 + } ; + +static yyconst flex_int16_t yy_nxt[123] = + { 0, + 4, 5, 6, 7, 8, 43, 43, 43, 43, 43, + 9, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 10, 11, 5, 6, 7, 8, 43, 43, 43, + 43, 43, 9, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 10, 11, 12, 43, 13, 14, 15, + 16, 17, 43, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 28, 43, 28, 43, 43, 29, + 30, 31, 32, 43, 33, 43, 34, 35, 36, 37, + 43, 43, 43, 43, 43, 38, 43, 43, 43, 43, + 43, 39, 43, 43, 43, 40, 41, 42, 3, 43, + + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43 + } ; + +static yyconst flex_int16_t yy_chk[123] = + { 0, + 44, 1, 1, 1, 1, 3, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 2, 2, 2, 2, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 2, 7, 0, 7, 7, 7, + 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 8, 8, 0, 8, 0, 0, 8, + 8, 8, 8, 0, 8, 0, 8, 8, 8, 9, + 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, + 0, 9, 0, 0, 0, 9, 9, 9, 43, 43, + + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int quote_fmt_flex_debug; +int quote_fmt_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *quote_fmttext; +#line 1 "quote_fmt_lex.l" +#line 2 "quote_fmt_lex.l" +#include "quote_fmt_lex.h" +#include "quote_fmt_parse.h" +#line 497 "lex.yy.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int quote_fmtwrap (void ); +#else +extern int quote_fmtwrap (void ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( quote_fmttext, quote_fmtleng, 1, quote_fmtout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( quote_fmtin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( quote_fmtin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, quote_fmtin))==0 && ferror(quote_fmtin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(quote_fmtin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int quote_fmtlex (void); + +#define YY_DECL int quote_fmtlex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after quote_fmttext and quote_fmtleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 9 "quote_fmt_lex.l" + + +#line 653 "lex.yy.c" + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! quote_fmtin ) + quote_fmtin = stdin; + + if ( ! quote_fmtout ) + quote_fmtout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + quote_fmtensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + quote_fmt_create_buffer(quote_fmtin,YY_BUF_SIZE ); + } + + quote_fmt_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of quote_fmttext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 44 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 99 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 11 "quote_fmt_lex.l" +/* date */ return SHOW_DATE; + YY_BREAK +case 2: +YY_RULE_SETUP +#line 12 "quote_fmt_lex.l" +/* from */ return SHOW_FROM; + YY_BREAK +case 3: +YY_RULE_SETUP +#line 13 "quote_fmt_lex.l" +/* full name */ return SHOW_FULLNAME; + YY_BREAK +case 4: +YY_RULE_SETUP +#line 14 "quote_fmt_lex.l" +/* first name */ return SHOW_FIRST_NAME; + YY_BREAK +case 5: +YY_RULE_SETUP +#line 15 "quote_fmt_lex.l" +/* initial of sender */ return SHOW_SENDER_INITIAL; + YY_BREAK +case 6: +YY_RULE_SETUP +#line 16 "quote_fmt_lex.l" +/* subject */ return SHOW_SUBJECT; + YY_BREAK +case 7: +YY_RULE_SETUP +#line 17 "quote_fmt_lex.l" +/* to */ return SHOW_TO; + YY_BREAK +case 8: +YY_RULE_SETUP +#line 18 "quote_fmt_lex.l" +/* cc */ return SHOW_CC; + YY_BREAK +case 9: +YY_RULE_SETUP +#line 19 "quote_fmt_lex.l" +/* newsgroups */ return SHOW_NEWSGROUPS; + YY_BREAK +case 10: +YY_RULE_SETUP +#line 20 "quote_fmt_lex.l" +/* message-id */ return SHOW_MESSAGEID; + YY_BREAK +case 11: +YY_RULE_SETUP +#line 21 "quote_fmt_lex.l" +/* references */ return SHOW_REFERENCES; + YY_BREAK +case 12: +YY_RULE_SETUP +#line 22 "quote_fmt_lex.l" +/* message */ return SHOW_MESSAGE; + YY_BREAK +case 13: +YY_RULE_SETUP +#line 23 "quote_fmt_lex.l" +/* quoted message */ return SHOW_QUOTED_MESSAGE; + YY_BREAK +case 14: +YY_RULE_SETUP +#line 24 "quote_fmt_lex.l" +/* message with no signature */ return SHOW_MESSAGE_NO_SIGNATURE; + YY_BREAK +case 15: +YY_RULE_SETUP +#line 25 "quote_fmt_lex.l" +/* quoted message with no signature */ return SHOW_QUOTED_MESSAGE_NO_SIGNATURE; + YY_BREAK +case 16: +YY_RULE_SETUP +#line 26 "quote_fmt_lex.l" +/* % */ return SHOW_PERCENT; + YY_BREAK +case 17: +YY_RULE_SETUP +#line 27 "quote_fmt_lex.l" +/* \ */ return SHOW_BACKSLASH; + YY_BREAK +case 18: +YY_RULE_SETUP +#line 28 "quote_fmt_lex.l" +/* tab */ return SHOW_TAB; + YY_BREAK +case 19: +/* rule 19 can match eol */ +YY_RULE_SETUP +#line 29 "quote_fmt_lex.l" +/* return */ return SHOW_EOL; + YY_BREAK +case 20: +YY_RULE_SETUP +#line 30 "quote_fmt_lex.l" +/* ? */ return SHOW_QUESTION_MARK; + YY_BREAK +case 21: +YY_RULE_SETUP +#line 31 "quote_fmt_lex.l" +return SHOW_OPARENT; + YY_BREAK +case 22: +YY_RULE_SETUP +#line 32 "quote_fmt_lex.l" +return SHOW_CPARENT; + YY_BREAK +case 23: +YY_RULE_SETUP +#line 33 "quote_fmt_lex.l" +/* query date */ return QUERY_DATE; + YY_BREAK +case 24: +YY_RULE_SETUP +#line 34 "quote_fmt_lex.l" +/* query from */ return QUERY_FROM; + YY_BREAK +case 25: +YY_RULE_SETUP +#line 35 "quote_fmt_lex.l" +/* query from name */ return QUERY_FULLNAME; + YY_BREAK +case 26: +YY_RULE_SETUP +#line 36 "quote_fmt_lex.l" +/* query subject */ return QUERY_SUBJECT; + YY_BREAK +case 27: +YY_RULE_SETUP +#line 37 "quote_fmt_lex.l" +/* query to */ return QUERY_TO; + YY_BREAK +case 28: +YY_RULE_SETUP +#line 38 "quote_fmt_lex.l" +/* query cc */ return QUERY_CC; + YY_BREAK +case 29: +YY_RULE_SETUP +#line 39 "quote_fmt_lex.l" +/* query newsgroups */ return QUERY_NEWSGROUPS; + YY_BREAK +case 30: +YY_RULE_SETUP +#line 40 "quote_fmt_lex.l" +/* query message-id */ return QUERY_MESSAGEID; + YY_BREAK +case 31: +YY_RULE_SETUP +#line 41 "quote_fmt_lex.l" +/* query references */ return QUERY_REFERENCES; + YY_BREAK +case 32: +YY_RULE_SETUP +#line 42 "quote_fmt_lex.l" +return OPARENT; + YY_BREAK +case 33: +YY_RULE_SETUP +#line 43 "quote_fmt_lex.l" +return CPARENT; + YY_BREAK +case 34: +YY_RULE_SETUP +#line 44 "quote_fmt_lex.l" +{ yylval.chr = quote_fmttext[0]; return CHARACTER; } + YY_BREAK +case 35: +YY_RULE_SETUP +#line 46 "quote_fmt_lex.l" +ECHO; + YY_BREAK +#line 912 "lex.yy.c" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed quote_fmtin at a new source and called + * quote_fmtlex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = quote_fmtin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( quote_fmtwrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * quote_fmttext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of quote_fmtlex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + quote_fmtrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + quote_fmtrestart(quote_fmtin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 44 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + register char *yy_cp = (yy_c_buf_p); + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 44 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 43); + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp ) +{ + register char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up quote_fmttext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = (yy_n_chars) + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + quote_fmtrestart(quote_fmtin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( quote_fmtwrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve quote_fmttext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void quote_fmtrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + quote_fmtensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + quote_fmt_create_buffer(quote_fmtin,YY_BUF_SIZE ); + } + + quote_fmt_init_buffer(YY_CURRENT_BUFFER,input_file ); + quote_fmt_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void quote_fmt_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * quote_fmtpop_buffer_state(); + * quote_fmtpush_buffer_state(new_buffer); + */ + quote_fmtensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + quote_fmt_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (quote_fmtwrap()) processing, but the only time this flag + * is looked at is after quote_fmtwrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void quote_fmt_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + quote_fmtin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE quote_fmt_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) quote_fmtalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in quote_fmt_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) quote_fmtalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in quote_fmt_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + quote_fmt_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with quote_fmt_create_buffer() + * + */ + void quote_fmt_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + quote_fmtfree((void *) b->yy_ch_buf ); + + quote_fmtfree((void *) b ); +} + +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a quote_fmtrestart() or at EOF. + */ + static void quote_fmt_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + quote_fmt_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then quote_fmt_init_buffer was _probably_ + * called from quote_fmtrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void quote_fmt_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + quote_fmt_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void quote_fmtpush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + quote_fmtensure_buffer_stack(); + + /* This block is copied from quote_fmt_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from quote_fmt_switch_to_buffer. */ + quote_fmt_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void quote_fmtpop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + quote_fmt_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + quote_fmt_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void quote_fmtensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)quote_fmtalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)quote_fmtrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE quote_fmt_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) quote_fmtalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in quote_fmt_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + quote_fmt_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to quote_fmtlex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * quote_fmt_scan_bytes() instead. + */ +YY_BUFFER_STATE quote_fmt_scan_string (yyconst char * yystr ) +{ + + return quote_fmt_scan_bytes(yystr,strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to quote_fmtlex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE quote_fmt_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) quote_fmtalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in quote_fmt_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = quote_fmt_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in quote_fmt_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up quote_fmttext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + quote_fmttext[quote_fmtleng] = (yy_hold_char); \ + (yy_c_buf_p) = quote_fmttext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + quote_fmtleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int quote_fmtget_lineno (void) +{ + + return quote_fmtlineno; +} + +/** Get the input stream. + * + */ +FILE *quote_fmtget_in (void) +{ + return quote_fmtin; +} + +/** Get the output stream. + * + */ +FILE *quote_fmtget_out (void) +{ + return quote_fmtout; +} + +/** Get the length of the current token. + * + */ +int quote_fmtget_leng (void) +{ + return quote_fmtleng; +} + +/** Get the current token. + * + */ + +char *quote_fmtget_text (void) +{ + return quote_fmttext; +} + +/** Set the current line number. + * @param line_number + * + */ +void quote_fmtset_lineno (int line_number ) +{ + + quote_fmtlineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see quote_fmt_switch_to_buffer + */ +void quote_fmtset_in (FILE * in_str ) +{ + quote_fmtin = in_str ; +} + +void quote_fmtset_out (FILE * out_str ) +{ + quote_fmtout = out_str ; +} + +int quote_fmtget_debug (void) +{ + return quote_fmt_flex_debug; +} + +void quote_fmtset_debug (int bdebug ) +{ + quote_fmt_flex_debug = bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from quote_fmtlex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + quote_fmtin = stdin; + quote_fmtout = stdout; +#else + quote_fmtin = (FILE *) 0; + quote_fmtout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * quote_fmtlex_init() + */ + return 0; +} + +/* quote_fmtlex_destroy is for both reentrant and non-reentrant scanners. */ +int quote_fmtlex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + quote_fmt_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + quote_fmtpop_buffer_state(); + } + + /* Destroy the stack itself. */ + quote_fmtfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * quote_fmtlex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *quote_fmtalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *quote_fmtrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void quote_fmtfree (void * ptr ) +{ + free( (char *) ptr ); /* see quote_fmtrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 46 "quote_fmt_lex.l" + + + diff --git a/src/quote_fmt_lex.h b/src/quote_fmt_lex.h new file mode 100644 index 0000000..585751a --- /dev/null +++ b/src/quote_fmt_lex.h @@ -0,0 +1,47 @@ +/* The following defines shamelessly stolen from GDB sources... */ + +/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc), + as well as gratuitiously global symbol names, so we can have multiple + yacc generated parsers in gdb. Note that these are only the variables + produced by yacc. If other parser generators (bison, byacc, etc) produce + additional global names that conflict at link time, then those parser + generators need to be fixed instead of adding those names to this list. */ + +#define yymaxdepth quote_fmtmaxdepth +#define yyparse quote_fmtparse +#define yylex quote_fmtlex +#define yyerror quote_fmterror +#define yylval quote_fmtlval +#define yychar quote_fmtchar +#define yydebug quote_fmtdebug +#define yypact quote_fmtpact +#define yyr1 quote_fmtr1 +#define yyr2 quote_fmtr2 +#define yydef quote_fmtdef +#define yychk quote_fmtchk +#define yypgo quote_fmtpgo +#define yyact quote_fmtact +#define yyexca quote_fmtexca +#define yyerrflag quote_fmterrflag +#define yynerrs quote_fmtnerrs +#define yyps quote_fmtps +#define yypv quote_fmtpv +#define yys quote_fmts +#define yy_yys quote_fmtyys +#define yystate quote_fmtstate +#define yytmp quote_fmttmp +#define yyv quote_fmtv +#define yy_yyv quote_fmtyyv +#define yyval quote_fmtval +#define yylloc quote_fmtlloc +#define yyreds quote_fmtreds /* With YYDEBUG defined */ +#define yytoks quote_fmttoks /* With YYDEBUG defined */ +#define yylhs quote_fmtyylhs +#define yylen quote_fmtyylen +#define yydefred quote_fmtyydefred +#define yydgoto quote_fmtyydgoto +#define yysindex quote_fmtyysindex +#define yyrindex quote_fmtyyrindex +#define yygindex quote_fmtyygindex +#define yytable quote_fmtyytable +#define yycheck quote_fmtyycheck diff --git a/src/quote_fmt_lex.l b/src/quote_fmt_lex.l new file mode 100644 index 0000000..7f904c1 --- /dev/null +++ b/src/quote_fmt_lex.l @@ -0,0 +1,46 @@ +%{ +#include "quote_fmt_lex.h" +#include "quote_fmt_parse.h" +%} + +%option prefix="quote_fmt" +%option outfile="lex.yy.c" + +%% + +"%d" /* date */ return SHOW_DATE; +"%f" /* from */ return SHOW_FROM; +"%N" /* full name */ return SHOW_FULLNAME; +"%F" /* first name */ return SHOW_FIRST_NAME; +"%I" /* initial of sender */ return SHOW_SENDER_INITIAL; +"%s" /* subject */ return SHOW_SUBJECT; +"%t" /* to */ return SHOW_TO; +"%c" /* cc */ return SHOW_CC; +"%n" /* newsgroups */ return SHOW_NEWSGROUPS; +"%i" /* message-id */ return SHOW_MESSAGEID; +"%r" /* references */ return SHOW_REFERENCES; +"%M" /* message */ return SHOW_MESSAGE; +"%Q" /* quoted message */ return SHOW_QUOTED_MESSAGE; +"%m" /* message with no signature */ return SHOW_MESSAGE_NO_SIGNATURE; +"%q" /* quoted message with no signature */ return SHOW_QUOTED_MESSAGE_NO_SIGNATURE; +"%%" /* % */ return SHOW_PERCENT; +"\\\\" /* \ */ return SHOW_BACKSLASH; +"\\t"|"\t" /* tab */ return SHOW_TAB; +"\\n"|"\n" /* return */ return SHOW_EOL; +"\\?" /* ? */ return SHOW_QUESTION_MARK; +"\\{" return SHOW_OPARENT; +"\\}" return SHOW_CPARENT; +"?d" /* query date */ return QUERY_DATE; +"?f" /* query from */ return QUERY_FROM; +"?N"|"?F"|"?I" /* query from name */ return QUERY_FULLNAME; +"?s" /* query subject */ return QUERY_SUBJECT; +"?t" /* query to */ return QUERY_TO; +"?c" /* query cc */ return QUERY_CC; +"?n" /* query newsgroups */ return QUERY_NEWSGROUPS; +"?i" /* query message-id */ return QUERY_MESSAGEID; +"?r" /* query references */ return QUERY_REFERENCES; +"{" return OPARENT; +"}" return CPARENT; +. { yylval.chr = yytext[0]; return CHARACTER; } + +%% diff --git a/src/quote_fmt_parse.c b/src/quote_fmt_parse.c new file mode 100644 index 0000000..066033f --- /dev/null +++ b/src/quote_fmt_parse.c @@ -0,0 +1,2199 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + SHOW_NEWSGROUPS = 258, + SHOW_DATE = 259, + SHOW_FROM = 260, + SHOW_FULLNAME = 261, + SHOW_FIRST_NAME = 262, + SHOW_SENDER_INITIAL = 263, + SHOW_SUBJECT = 264, + SHOW_TO = 265, + SHOW_MESSAGEID = 266, + SHOW_PERCENT = 267, + SHOW_CC = 268, + SHOW_REFERENCES = 269, + SHOW_MESSAGE = 270, + SHOW_QUOTED_MESSAGE = 271, + SHOW_BACKSLASH = 272, + SHOW_TAB = 273, + SHOW_QUOTED_MESSAGE_NO_SIGNATURE = 274, + SHOW_MESSAGE_NO_SIGNATURE = 275, + SHOW_EOL = 276, + SHOW_QUESTION_MARK = 277, + SHOW_OPARENT = 278, + SHOW_CPARENT = 279, + QUERY_DATE = 280, + QUERY_FROM = 281, + QUERY_FULLNAME = 282, + QUERY_SUBJECT = 283, + QUERY_TO = 284, + QUERY_NEWSGROUPS = 285, + QUERY_MESSAGEID = 286, + QUERY_CC = 287, + QUERY_REFERENCES = 288, + OPARENT = 289, + CPARENT = 290, + CHARACTER = 291 + }; +#endif +/* Tokens. */ +#define SHOW_NEWSGROUPS 258 +#define SHOW_DATE 259 +#define SHOW_FROM 260 +#define SHOW_FULLNAME 261 +#define SHOW_FIRST_NAME 262 +#define SHOW_SENDER_INITIAL 263 +#define SHOW_SUBJECT 264 +#define SHOW_TO 265 +#define SHOW_MESSAGEID 266 +#define SHOW_PERCENT 267 +#define SHOW_CC 268 +#define SHOW_REFERENCES 269 +#define SHOW_MESSAGE 270 +#define SHOW_QUOTED_MESSAGE 271 +#define SHOW_BACKSLASH 272 +#define SHOW_TAB 273 +#define SHOW_QUOTED_MESSAGE_NO_SIGNATURE 274 +#define SHOW_MESSAGE_NO_SIGNATURE 275 +#define SHOW_EOL 276 +#define SHOW_QUESTION_MARK 277 +#define SHOW_OPARENT 278 +#define SHOW_CPARENT 279 +#define QUERY_DATE 280 +#define QUERY_FROM 281 +#define QUERY_FULLNAME 282 +#define QUERY_SUBJECT 283 +#define QUERY_TO 284 +#define QUERY_NEWSGROUPS 285 +#define QUERY_MESSAGEID 286 +#define QUERY_CC 287 +#define QUERY_REFERENCES 288 +#define OPARENT 289 +#define CPARENT 290 +#define CHARACTER 291 + + + + +/* Copy the first part of user declarations. */ +#line 1 "quote_fmt_parse.y" + + +#include "defs.h" + +#include +#include + +#include "procmsg.h" +#include "procmime.h" +#include "utils.h" + +#include "quote_fmt.h" +#include "quote_fmt_lex.h" + +/* decl */ +/* +flex quote_fmt.l +bison -p quote_fmt quote_fmt.y +*/ + +int yylex(void); + +static MsgInfo *msginfo = NULL; +static gboolean *visible = NULL; +static gint maxsize = 0; +static gint stacksize = 0; + +static gchar *buffer = NULL; +static gint bufmax = 0; +static gint bufsize = 0; +static const gchar *quote_str = NULL; +static const gchar *body = NULL; +static gint error = 0; + +static void add_visibility(gboolean val) +{ + stacksize++; + if (maxsize < stacksize) { + maxsize += 128; + visible = g_realloc(visible, maxsize * sizeof(gboolean)); + if (visible == NULL) + maxsize = 0; + } + + visible[stacksize - 1] = val; +} + +static void remove_visibility(void) +{ + stacksize--; +} + +static void add_buffer(const gchar *s) +{ + gint len; + + len = strlen(s); + if (bufsize + len + 1 > bufmax) { + if (bufmax == 0) + bufmax = 128; + while (bufsize + len + 1 > bufmax) + bufmax *= 2; + buffer = g_realloc(buffer, bufmax); + } + strcpy(buffer + bufsize, s); + bufsize += len; +} + +#if 0 +static void flush_buffer(void) +{ + if (buffer != NULL) + *buffer = '\0'; + bufsize = 0; +} +#endif + +gchar *quote_fmt_get_buffer(void) +{ + if (error != 0) + return NULL; + else + return buffer; +} + +#define INSERT(buf) \ + if (stacksize != 0 && visible[stacksize - 1]) \ + add_buffer(buf) + +#define INSERT_CHARACTER(chr) \ + if (stacksize != 0 && visible[stacksize - 1]) { \ + gchar tmp[2]; \ + tmp[0] = (chr); \ + tmp[1] = '\0'; \ + add_buffer(tmp); \ + } + +void quote_fmt_init(MsgInfo *info, const gchar *my_quote_str, + const gchar *my_body) +{ + quote_str = my_quote_str; + body = my_body; + msginfo = info; + stacksize = 0; + add_visibility(TRUE); + if (buffer != NULL) + *buffer = 0; + bufsize = 0; + error = 0; +} + +void quote_fmterror(char *str) +{ + g_warning("Error: %s\n", str); + error = 1; +} + +int quote_fmtwrap(void) +{ + return 1; +} + +static int isseparator(int ch) +{ + return g_ascii_isspace(ch) || ch == '.' || ch == '-'; +} + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 129 "quote_fmt_parse.y" +{ + char chr; +} +/* Line 187 of yacc.c. */ +#line 300 "quote_fmt_parse.c" + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ +#line 313 "quote_fmt_parse.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 48 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 61 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 37 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 16 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 48 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 77 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 291 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 5, 8, 10, 12, 14, 16, 18, + 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, + 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, + 60, 62, 63, 69, 70, 76, 77, 83, 84, 90, + 91, 97, 98, 104, 105, 111, 112, 118, 119 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 38, 0, -1, 39, -1, 40, 39, -1, 40, -1, + 42, -1, 41, -1, 43, -1, 36, -1, 3, -1, + 4, -1, 5, -1, 6, -1, 7, -1, 8, -1, + 9, -1, 10, -1, 11, -1, 12, -1, 13, -1, + 14, -1, 15, -1, 16, -1, 20, -1, 19, -1, + 17, -1, 18, -1, 21, -1, 22, -1, 23, -1, + 24, -1, -1, 25, 44, 34, 38, 35, -1, -1, + 26, 45, 34, 38, 35, -1, -1, 27, 46, 34, + 38, 35, -1, -1, 28, 47, 34, 38, 35, -1, + -1, 29, 48, 34, 38, 35, -1, -1, 30, 49, + 34, 38, 35, -1, -1, 31, 50, 34, 38, 35, + -1, -1, 32, 51, 34, 38, 35, -1, -1, 33, + 52, 34, 38, 35, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 154, 154, 157, 158, 161, 162, 166, 170, 174, + 179, 190, 195, 200, 216, 243, 248, 253, 258, 262, + 267, 272, 293, 316, 339, 364, 368, 372, 376, 380, + 384, 391, 390, 399, 398, 407, 406, 415, 414, 423, + 422, 431, 430, 439, 438, 447, 446, 455, 454 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "SHOW_NEWSGROUPS", "SHOW_DATE", + "SHOW_FROM", "SHOW_FULLNAME", "SHOW_FIRST_NAME", "SHOW_SENDER_INITIAL", + "SHOW_SUBJECT", "SHOW_TO", "SHOW_MESSAGEID", "SHOW_PERCENT", "SHOW_CC", + "SHOW_REFERENCES", "SHOW_MESSAGE", "SHOW_QUOTED_MESSAGE", + "SHOW_BACKSLASH", "SHOW_TAB", "SHOW_QUOTED_MESSAGE_NO_SIGNATURE", + "SHOW_MESSAGE_NO_SIGNATURE", "SHOW_EOL", "SHOW_QUESTION_MARK", + "SHOW_OPARENT", "SHOW_CPARENT", "QUERY_DATE", "QUERY_FROM", + "QUERY_FULLNAME", "QUERY_SUBJECT", "QUERY_TO", "QUERY_NEWSGROUPS", + "QUERY_MESSAGEID", "QUERY_CC", "QUERY_REFERENCES", "OPARENT", "CPARENT", + "CHARACTER", "$accept", "quote_fmt", + "character_or_special_or_query_list", "character_or_special_or_query", + "character", "special", "query", "@1", "@2", "@3", "@4", "@5", "@6", + "@7", "@8", "@9", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 37, 38, 39, 39, 40, 40, 40, 41, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 44, 43, 45, 43, 46, 43, 47, 43, 48, + 43, 49, 43, 50, 43, 51, 43, 52, 43 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 5, 0, 5, 0, 5, 0, 5, 0, + 5, 0, 5, 0, 5, 0, 5, 0, 5 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 25, 26, 24, 23, 27, + 28, 29, 30, 31, 33, 35, 37, 39, 41, 43, + 45, 47, 8, 0, 2, 4, 6, 5, 7, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 32, 34, + 36, 38, 40, 42, 44, 46, 48 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -17 +static const yytype_int8 yypact[] = +{ + -3, -17, -17, -17, -17, -17, -17, -17, -17, -17, + -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, + -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, + -17, -17, -17, 31, -17, -3, -17, -17, -17, -2, + 9, 10, 11, 12, 13, 14, 15, 16, -17, -17, + -3, -3, -3, -3, -3, -3, -3, -3, -3, 17, + 18, 19, 20, 21, 22, 23, 24, 25, -17, -17, + -17, -17, -17, -17, -17, -17, -17 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -17, -16, 26, -17, -17, -17, -17, -17, -17, -17, + -17, -17, -17, -17, -17, -17 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 48, 50, 32, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 51, 52, 53, 54, 55, 56, 57, + 58, 0, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 49 +}; + +static const yytype_int8 yycheck[] = +{ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 0, 34, 36, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 34, 34, 34, 34, 34, 34, 34, + 34, -1, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 36, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 0, 39, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 35, 35, + 35, 35, 35, 35, 35, 35, 35 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 6: +#line 163 "quote_fmt_parse.y" + { + INSERT_CHARACTER((yyvsp[(1) - (1)].chr)); + } + break; + + case 9: +#line 175 "quote_fmt_parse.y" + { + if (msginfo->newsgroups) + INSERT(msginfo->newsgroups); + } + break; + + case 10: +#line 180 "quote_fmt_parse.y" + { + if (msginfo->date) { + INSERT(msginfo->date); + } else if (msginfo->size == 0) { + gchar buf[64]; + + get_rfc822_date(buf, sizeof(buf)); + INSERT(buf); + } + } + break; + + case 11: +#line 191 "quote_fmt_parse.y" + { + if (msginfo->from) + INSERT(msginfo->from); + } + break; + + case 12: +#line 196 "quote_fmt_parse.y" + { + if (msginfo->fromname) + INSERT(msginfo->fromname); + } + break; + + case 13: +#line 201 "quote_fmt_parse.y" + { + if (msginfo->fromname) { + gchar *p; + gchar *str; + + str = alloca(strlen(msginfo->fromname) + 1); + if (str != NULL) { + strcpy(str, msginfo->fromname); + p = str; + while (*p && !g_ascii_isspace(*p)) p++; + *p = '\0'; + INSERT(str); + } + } + } + break; + + case 14: +#line 217 "quote_fmt_parse.y" + { +#define MAX_SENDER_INITIAL 20 + if (msginfo->fromname) { + gchar tmp[MAX_SENDER_INITIAL]; + gchar *p; + gchar *cur; + gint len = 0; + + p = msginfo->fromname; + cur = tmp; + while (*p) { + if (*p && g_ascii_isalnum(*p)) { + *cur = g_ascii_toupper(*p); + cur++; + len++; + if (len >= MAX_SENDER_INITIAL - 1) + break; + } else + break; + while (*p && !isseparator(*p)) p++; + while (*p && isseparator(*p)) p++; + } + *cur = '\0'; + INSERT(tmp); + } + } + break; + + case 15: +#line 244 "quote_fmt_parse.y" + { + if (msginfo->subject) + INSERT(msginfo->subject); + } + break; + + case 16: +#line 249 "quote_fmt_parse.y" + { + if (msginfo->to) + INSERT(msginfo->to); + } + break; + + case 17: +#line 254 "quote_fmt_parse.y" + { + if (msginfo->msgid) + INSERT(msginfo->msgid); + } + break; + + case 18: +#line 259 "quote_fmt_parse.y" + { + INSERT("%"); + } + break; + + case 19: +#line 263 "quote_fmt_parse.y" + { + if (msginfo->cc) + INSERT(msginfo->cc); + } + break; + + case 20: +#line 268 "quote_fmt_parse.y" + { + /* if (msginfo->references) + INSERT(msginfo->references); */ + } + break; + + case 21: +#line 273 "quote_fmt_parse.y" + { + gchar buf[BUFFSIZE]; + FILE *fp = NULL; + + if (body) + fp = str_open_as_stream(body); + else if (msginfo->size > 0) { + fp = procmime_get_first_text_content(msginfo, NULL); + if (fp == NULL) + g_warning("quote_fmt_parse.y: Can't get text part\n"); + } + + if (fp) { + while (fgets(buf, sizeof(buf), fp) != NULL) { + strcrchomp(buf); + INSERT(buf); + } + fclose(fp); + } + } + break; + + case 22: +#line 294 "quote_fmt_parse.y" + { + gchar buf[BUFFSIZE]; + FILE *fp = NULL; + + if (body) + fp = str_open_as_stream(body); + else if (msginfo->size > 0) { + fp = procmime_get_first_text_content(msginfo, NULL); + if (fp == NULL) + g_warning("quote_fmt_parse.y: Can't get text part\n"); + } + + if (fp) { + while (fgets(buf, sizeof(buf), fp) != NULL) { + strcrchomp(buf); + if (quote_str) + INSERT(quote_str); + INSERT(buf); + } + fclose(fp); + } + } + break; + + case 23: +#line 317 "quote_fmt_parse.y" + { + gchar buf[BUFFSIZE]; + FILE *fp = NULL; + + if (body) + fp = str_open_as_stream(body); + else if (msginfo->size > 0) { + fp = procmime_get_first_text_content(msginfo, NULL); + if (fp == NULL) + g_warning("quote_fmt_parse.y: Can't get text part\n"); + } + + if (fp) { + while (fgets(buf, sizeof(buf), fp) != NULL) { + strcrchomp(buf); + if (strncmp(buf, "-- \n", 4) == 0) + break; + INSERT(buf); + } + fclose(fp); + } + } + break; + + case 24: +#line 340 "quote_fmt_parse.y" + { + gchar buf[BUFFSIZE]; + FILE *fp = NULL; + + if (body) + fp = str_open_as_stream(body); + else if (msginfo->size > 0) { + fp = procmime_get_first_text_content(msginfo, NULL); + if (fp == NULL) + g_warning("Can't get text part\n"); + } + + if (fp) { + while (fgets(buf, sizeof(buf), fp) != NULL) { + strcrchomp(buf); + if (strncmp(buf, "-- \n", 4) == 0) + break; + if (quote_str) + INSERT(quote_str); + INSERT(buf); + } + fclose(fp); + } + } + break; + + case 25: +#line 365 "quote_fmt_parse.y" + { + INSERT("\\"); + } + break; + + case 26: +#line 369 "quote_fmt_parse.y" + { + INSERT("\t"); + } + break; + + case 27: +#line 373 "quote_fmt_parse.y" + { + INSERT("\n"); + } + break; + + case 28: +#line 377 "quote_fmt_parse.y" + { + INSERT("?"); + } + break; + + case 29: +#line 381 "quote_fmt_parse.y" + { + INSERT("{"); + } + break; + + case 30: +#line 385 "quote_fmt_parse.y" + { + INSERT("}"); + } + break; + + case 31: +#line 391 "quote_fmt_parse.y" + { + add_visibility(msginfo->date != NULL); + } + break; + + case 32: +#line 395 "quote_fmt_parse.y" + { + remove_visibility(); + } + break; + + case 33: +#line 399 "quote_fmt_parse.y" + { + add_visibility(msginfo->from != NULL); + } + break; + + case 34: +#line 403 "quote_fmt_parse.y" + { + remove_visibility(); + } + break; + + case 35: +#line 407 "quote_fmt_parse.y" + { + add_visibility(msginfo->fromname != NULL); + } + break; + + case 36: +#line 411 "quote_fmt_parse.y" + { + remove_visibility(); + } + break; + + case 37: +#line 415 "quote_fmt_parse.y" + { + add_visibility(msginfo->subject != NULL); + } + break; + + case 38: +#line 419 "quote_fmt_parse.y" + { + remove_visibility(); + } + break; + + case 39: +#line 423 "quote_fmt_parse.y" + { + add_visibility(msginfo->to != NULL); + } + break; + + case 40: +#line 427 "quote_fmt_parse.y" + { + remove_visibility(); + } + break; + + case 41: +#line 431 "quote_fmt_parse.y" + { + add_visibility(msginfo->newsgroups != NULL); + } + break; + + case 42: +#line 435 "quote_fmt_parse.y" + { + remove_visibility(); + } + break; + + case 43: +#line 439 "quote_fmt_parse.y" + { + add_visibility(msginfo->msgid != NULL); + } + break; + + case 44: +#line 443 "quote_fmt_parse.y" + { + remove_visibility(); + } + break; + + case 45: +#line 447 "quote_fmt_parse.y" + { + add_visibility(msginfo->cc != NULL); + } + break; + + case 46: +#line 451 "quote_fmt_parse.y" + { + remove_visibility(); + } + break; + + case 47: +#line 455 "quote_fmt_parse.y" + { + /* add_visibility(msginfo->references != NULL); */ + } + break; + + case 48: +#line 459 "quote_fmt_parse.y" + { + remove_visibility(); + } + break; + + +/* Line 1267 of yacc.c. */ +#line 1986 "quote_fmt_parse.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + diff --git a/src/quote_fmt_parse.h b/src/quote_fmt_parse.h new file mode 100644 index 0000000..f1057a7 --- /dev/null +++ b/src/quote_fmt_parse.h @@ -0,0 +1,132 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + SHOW_NEWSGROUPS = 258, + SHOW_DATE = 259, + SHOW_FROM = 260, + SHOW_FULLNAME = 261, + SHOW_FIRST_NAME = 262, + SHOW_SENDER_INITIAL = 263, + SHOW_SUBJECT = 264, + SHOW_TO = 265, + SHOW_MESSAGEID = 266, + SHOW_PERCENT = 267, + SHOW_CC = 268, + SHOW_REFERENCES = 269, + SHOW_MESSAGE = 270, + SHOW_QUOTED_MESSAGE = 271, + SHOW_BACKSLASH = 272, + SHOW_TAB = 273, + SHOW_QUOTED_MESSAGE_NO_SIGNATURE = 274, + SHOW_MESSAGE_NO_SIGNATURE = 275, + SHOW_EOL = 276, + SHOW_QUESTION_MARK = 277, + SHOW_OPARENT = 278, + SHOW_CPARENT = 279, + QUERY_DATE = 280, + QUERY_FROM = 281, + QUERY_FULLNAME = 282, + QUERY_SUBJECT = 283, + QUERY_TO = 284, + QUERY_NEWSGROUPS = 285, + QUERY_MESSAGEID = 286, + QUERY_CC = 287, + QUERY_REFERENCES = 288, + OPARENT = 289, + CPARENT = 290, + CHARACTER = 291 + }; +#endif +/* Tokens. */ +#define SHOW_NEWSGROUPS 258 +#define SHOW_DATE 259 +#define SHOW_FROM 260 +#define SHOW_FULLNAME 261 +#define SHOW_FIRST_NAME 262 +#define SHOW_SENDER_INITIAL 263 +#define SHOW_SUBJECT 264 +#define SHOW_TO 265 +#define SHOW_MESSAGEID 266 +#define SHOW_PERCENT 267 +#define SHOW_CC 268 +#define SHOW_REFERENCES 269 +#define SHOW_MESSAGE 270 +#define SHOW_QUOTED_MESSAGE 271 +#define SHOW_BACKSLASH 272 +#define SHOW_TAB 273 +#define SHOW_QUOTED_MESSAGE_NO_SIGNATURE 274 +#define SHOW_MESSAGE_NO_SIGNATURE 275 +#define SHOW_EOL 276 +#define SHOW_QUESTION_MARK 277 +#define SHOW_OPARENT 278 +#define SHOW_CPARENT 279 +#define QUERY_DATE 280 +#define QUERY_FROM 281 +#define QUERY_FULLNAME 282 +#define QUERY_SUBJECT 283 +#define QUERY_TO 284 +#define QUERY_NEWSGROUPS 285 +#define QUERY_MESSAGEID 286 +#define QUERY_CC 287 +#define QUERY_REFERENCES 288 +#define OPARENT 289 +#define CPARENT 290 +#define CHARACTER 291 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 129 "quote_fmt_parse.y" +{ + char chr; +} +/* Line 1489 of yacc.c. */ +#line 125 "y.tab.h" + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +extern YYSTYPE yylval; + diff --git a/src/quote_fmt_parse.y b/src/quote_fmt_parse.y new file mode 100644 index 0000000..336b481 --- /dev/null +++ b/src/quote_fmt_parse.y @@ -0,0 +1,461 @@ +%{ + +#include "defs.h" + +#include +#include + +#include "procmsg.h" +#include "procmime.h" +#include "utils.h" + +#include "quote_fmt.h" +#include "quote_fmt_lex.h" + +/* decl */ +/* +flex quote_fmt.l +bison -p quote_fmt quote_fmt.y +*/ + +int yylex(void); + +static MsgInfo *msginfo = NULL; +static gboolean *visible = NULL; +static gint maxsize = 0; +static gint stacksize = 0; + +static gchar *buffer = NULL; +static gint bufmax = 0; +static gint bufsize = 0; +static const gchar *quote_str = NULL; +static const gchar *body = NULL; +static gint error = 0; + +static void add_visibility(gboolean val) +{ + stacksize++; + if (maxsize < stacksize) { + maxsize += 128; + visible = g_realloc(visible, maxsize * sizeof(gboolean)); + if (visible == NULL) + maxsize = 0; + } + + visible[stacksize - 1] = val; +} + +static void remove_visibility(void) +{ + stacksize--; +} + +static void add_buffer(const gchar *s) +{ + gint len; + + len = strlen(s); + if (bufsize + len + 1 > bufmax) { + if (bufmax == 0) + bufmax = 128; + while (bufsize + len + 1 > bufmax) + bufmax *= 2; + buffer = g_realloc(buffer, bufmax); + } + strcpy(buffer + bufsize, s); + bufsize += len; +} + +#if 0 +static void flush_buffer(void) +{ + if (buffer != NULL) + *buffer = '\0'; + bufsize = 0; +} +#endif + +gchar *quote_fmt_get_buffer(void) +{ + if (error != 0) + return NULL; + else + return buffer; +} + +#define INSERT(buf) \ + if (stacksize != 0 && visible[stacksize - 1]) \ + add_buffer(buf) + +#define INSERT_CHARACTER(chr) \ + if (stacksize != 0 && visible[stacksize - 1]) { \ + gchar tmp[2]; \ + tmp[0] = (chr); \ + tmp[1] = '\0'; \ + add_buffer(tmp); \ + } + +void quote_fmt_init(MsgInfo *info, const gchar *my_quote_str, + const gchar *my_body) +{ + quote_str = my_quote_str; + body = my_body; + msginfo = info; + stacksize = 0; + add_visibility(TRUE); + if (buffer != NULL) + *buffer = 0; + bufsize = 0; + error = 0; +} + +void quote_fmterror(char *str) +{ + g_warning("Error: %s\n", str); + error = 1; +} + +int quote_fmtwrap(void) +{ + return 1; +} + +static int isseparator(int ch) +{ + return g_ascii_isspace(ch) || ch == '.' || ch == '-'; +} +%} + +%union { + char chr; +} + +%token SHOW_NEWSGROUPS +%token SHOW_DATE SHOW_FROM SHOW_FULLNAME SHOW_FIRST_NAME +%token SHOW_SENDER_INITIAL SHOW_SUBJECT SHOW_TO SHOW_MESSAGEID +%token SHOW_PERCENT SHOW_CC SHOW_REFERENCES SHOW_MESSAGE +%token SHOW_QUOTED_MESSAGE SHOW_BACKSLASH SHOW_TAB +%token SHOW_QUOTED_MESSAGE_NO_SIGNATURE SHOW_MESSAGE_NO_SIGNATURE +%token SHOW_EOL SHOW_QUESTION_MARK SHOW_OPARENT SHOW_CPARENT +%token QUERY_DATE QUERY_FROM +%token QUERY_FULLNAME QUERY_SUBJECT QUERY_TO QUERY_NEWSGROUPS +%token QUERY_MESSAGEID QUERY_CC QUERY_REFERENCES +%token OPARENT CPARENT +%token CHARACTER + +%start quote_fmt + +%token CHARACTER +%type character + +%% + +quote_fmt: + character_or_special_or_query_list; + +character_or_special_or_query_list: + character_or_special_or_query character_or_special_or_query_list + | character_or_special_or_query ; + +character_or_special_or_query: + special + | character + { + INSERT_CHARACTER($1); + } + | query ; + + +character: + CHARACTER + ; + +special: + SHOW_NEWSGROUPS + { + if (msginfo->newsgroups) + INSERT(msginfo->newsgroups); + } + | SHOW_DATE + { + if (msginfo->date) { + INSERT(msginfo->date); + } else if (msginfo->size == 0) { + gchar buf[64]; + + get_rfc822_date(buf, sizeof(buf)); + INSERT(buf); + } + } + | SHOW_FROM + { + if (msginfo->from) + INSERT(msginfo->from); + } + | SHOW_FULLNAME + { + if (msginfo->fromname) + INSERT(msginfo->fromname); + } + | SHOW_FIRST_NAME + { + if (msginfo->fromname) { + gchar *p; + gchar *str; + + str = alloca(strlen(msginfo->fromname) + 1); + if (str != NULL) { + strcpy(str, msginfo->fromname); + p = str; + while (*p && !g_ascii_isspace(*p)) p++; + *p = '\0'; + INSERT(str); + } + } + } + | SHOW_SENDER_INITIAL + { +#define MAX_SENDER_INITIAL 20 + if (msginfo->fromname) { + gchar tmp[MAX_SENDER_INITIAL]; + gchar *p; + gchar *cur; + gint len = 0; + + p = msginfo->fromname; + cur = tmp; + while (*p) { + if (*p && g_ascii_isalnum(*p)) { + *cur = g_ascii_toupper(*p); + cur++; + len++; + if (len >= MAX_SENDER_INITIAL - 1) + break; + } else + break; + while (*p && !isseparator(*p)) p++; + while (*p && isseparator(*p)) p++; + } + *cur = '\0'; + INSERT(tmp); + } + } + | SHOW_SUBJECT + { + if (msginfo->subject) + INSERT(msginfo->subject); + } + | SHOW_TO + { + if (msginfo->to) + INSERT(msginfo->to); + } + | SHOW_MESSAGEID + { + if (msginfo->msgid) + INSERT(msginfo->msgid); + } + | SHOW_PERCENT + { + INSERT("%"); + } + | SHOW_CC + { + if (msginfo->cc) + INSERT(msginfo->cc); + } + | SHOW_REFERENCES + { + /* if (msginfo->references) + INSERT(msginfo->references); */ + } + | SHOW_MESSAGE + { + gchar buf[BUFFSIZE]; + FILE *fp = NULL; + + if (body) + fp = str_open_as_stream(body); + else if (msginfo->size > 0) { + fp = procmime_get_first_text_content(msginfo, NULL); + if (fp == NULL) + g_warning("quote_fmt_parse.y: Can't get text part\n"); + } + + if (fp) { + while (fgets(buf, sizeof(buf), fp) != NULL) { + strcrchomp(buf); + INSERT(buf); + } + fclose(fp); + } + } + | SHOW_QUOTED_MESSAGE + { + gchar buf[BUFFSIZE]; + FILE *fp = NULL; + + if (body) + fp = str_open_as_stream(body); + else if (msginfo->size > 0) { + fp = procmime_get_first_text_content(msginfo, NULL); + if (fp == NULL) + g_warning("quote_fmt_parse.y: Can't get text part\n"); + } + + if (fp) { + while (fgets(buf, sizeof(buf), fp) != NULL) { + strcrchomp(buf); + if (quote_str) + INSERT(quote_str); + INSERT(buf); + } + fclose(fp); + } + } + | SHOW_MESSAGE_NO_SIGNATURE + { + gchar buf[BUFFSIZE]; + FILE *fp = NULL; + + if (body) + fp = str_open_as_stream(body); + else if (msginfo->size > 0) { + fp = procmime_get_first_text_content(msginfo, NULL); + if (fp == NULL) + g_warning("quote_fmt_parse.y: Can't get text part\n"); + } + + if (fp) { + while (fgets(buf, sizeof(buf), fp) != NULL) { + strcrchomp(buf); + if (strncmp(buf, "-- \n", 4) == 0) + break; + INSERT(buf); + } + fclose(fp); + } + } + | SHOW_QUOTED_MESSAGE_NO_SIGNATURE + { + gchar buf[BUFFSIZE]; + FILE *fp = NULL; + + if (body) + fp = str_open_as_stream(body); + else if (msginfo->size > 0) { + fp = procmime_get_first_text_content(msginfo, NULL); + if (fp == NULL) + g_warning("Can't get text part\n"); + } + + if (fp) { + while (fgets(buf, sizeof(buf), fp) != NULL) { + strcrchomp(buf); + if (strncmp(buf, "-- \n", 4) == 0) + break; + if (quote_str) + INSERT(quote_str); + INSERT(buf); + } + fclose(fp); + } + } + | SHOW_BACKSLASH + { + INSERT("\\"); + } + | SHOW_TAB + { + INSERT("\t"); + } + | SHOW_EOL + { + INSERT("\n"); + } + | SHOW_QUESTION_MARK + { + INSERT("?"); + } + | SHOW_OPARENT + { + INSERT("{"); + } + | SHOW_CPARENT + { + INSERT("}"); + }; + +query: + QUERY_DATE + { + add_visibility(msginfo->date != NULL); + } + OPARENT quote_fmt CPARENT + { + remove_visibility(); + } + | QUERY_FROM + { + add_visibility(msginfo->from != NULL); + } + OPARENT quote_fmt CPARENT + { + remove_visibility(); + } + | QUERY_FULLNAME + { + add_visibility(msginfo->fromname != NULL); + } + OPARENT quote_fmt CPARENT + { + remove_visibility(); + } + | QUERY_SUBJECT + { + add_visibility(msginfo->subject != NULL); + } + OPARENT quote_fmt CPARENT + { + remove_visibility(); + } + | QUERY_TO + { + add_visibility(msginfo->to != NULL); + } + OPARENT quote_fmt CPARENT + { + remove_visibility(); + } + | QUERY_NEWSGROUPS + { + add_visibility(msginfo->newsgroups != NULL); + } + OPARENT quote_fmt CPARENT + { + remove_visibility(); + } + | QUERY_MESSAGEID + { + add_visibility(msginfo->msgid != NULL); + } + OPARENT quote_fmt CPARENT + { + remove_visibility(); + } + | QUERY_CC + { + add_visibility(msginfo->cc != NULL); + } + OPARENT quote_fmt CPARENT + { + remove_visibility(); + } + | QUERY_REFERENCES + { + /* add_visibility(msginfo->references != NULL); */ + } + OPARENT quote_fmt CPARENT + { + remove_visibility(); + }; diff --git a/src/rfc2015.c b/src/rfc2015.c new file mode 100644 index 0000000..ebfa96c --- /dev/null +++ b/src/rfc2015.c @@ -0,0 +1,1992 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Werner Koch (dd9jn) + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if USE_GPGME + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "procmsg.h" +#include "procmime.h" +#include "procheader.h" +#include "base64.h" +#include "uuencode.h" +#include "unmime.h" +#include "codeconv.h" +#include "utils.h" +#include "prefs_common.h" +#include "passphrase.h" +#include "select-keys.h" +#include "sigstatus.h" +#include "rfc2015.h" + +#define DIM(v) (sizeof(v) / sizeof((v)[0])) + +static gboolean gpg_available = TRUE; + +static gchar *content_names[] = { + "Content-Type", + "Content-Disposition", + "Content-Transfer-Encoding", + NULL +}; + +static gchar *mime_version_name[] = { + "Mime-Version", + NULL +}; + +#if 0 +static void dump_mimeinfo(const gchar *text, MimeInfo *x) +{ + debug_print("MimeInfo[%s] %p level=%d\n", text, x, x ? x->level : 0); + if (!x) + return; + + debug_print(" enc=`%s' enc_type=%d mime_type=%d\n", + x->encoding, x->encoding_type, x->mime_type); + debug_print(" cont_type=`%s' cs=`%s' name=`%s' bnd=`%s'\n", + x->content_type, x->charset, x->name, x->boundary ); + debug_print(" cont_disp=`%s' fname=`%s' fpos=%ld size=%u, lvl=%d\n", + x->content_disposition, x->filename, x->fpos, x->size, + x->level ); + dump_mimeinfo(".main", x->main ); + dump_mimeinfo(".sub", x->sub ); + dump_mimeinfo(".next", x->next ); + debug_print("MimeInfo[.parent] %p\n", x ); + dump_mimeinfo(".children", x->children ); + dump_mimeinfo(".plaintext", x->plaintext ); +} + +static void dump_part(MimeInfo *mimeinfo, FILE *fp) +{ + guint size = mimeinfo->size; + gint c; + + if (fseek(fp, mimeinfo->fpos, SEEK_SET)) { + debug_print("dump_part: fseek error\n"); + return; + } + + debug_print("--- begin dump_part ----\n"); + while (size-- && (c = getc (fp)) != EOF) + putc(c, stderr); + if (ferror(fp)) + debug_print("dump_part: read error\n"); + debug_print("--- end dump_part ----\n"); +} +#endif + +void rfc2015_disable_all(void) +{ + gpg_available = FALSE; +} + +gboolean rfc2015_is_available(void) +{ + return gpg_available; +} + +void rfc2015_secure_remove(const gchar *fname) +{ + if (!fname) + return; + /* fixme: overwrite the file first */ + g_remove(fname); +} + +static void sig_status_for_key(GString *str, gpgme_ctx_t ctx, + gpgme_signature_t sig) +{ + gpgme_key_t key; + gpgme_user_id_t user; + gpgme_error_t err; + + err = gpgme_get_key(ctx, sig->fpr, &key, 0); + if (err || key == NULL || key->uids->uid == NULL) { + if (err) + debug_print("gpgme_get_key failed: %s\n", + gpgme_strerror(err)); + g_string_sprintfa(str, "%s\n", + gpgmegtk_sig_status_to_string (sig, FALSE)); + if ((sig->fpr != NULL) && (*(sig->fpr) != '\0')) + g_string_sprintfa + (str, "Key fingerprint: %s\n", sig->fpr); + g_string_append(str, _("Cannot find user ID for this key.")); + g_string_append(str, "\n"); + return; + } + user = key->uids; + g_string_sprintfa + (str, gpgmegtk_sig_status_to_string (sig, TRUE), user->uid); + g_string_append(str, "\n"); + + user = user->next; + while (user) { + g_string_sprintfa + (str, _(" aka \"%s\"\n"), user->uid); + user = user->next; + } +} + +static gchar *sig_status_full(gpgme_ctx_t ctx, gpgme_verify_result_t result) +{ + GString *str; + gpgme_signature_t sig; + time_t created; + struct tm *ctime_val; + gchar ctime_str[80]; + gchar *ctime_str_utf8; + gchar *retval; + + g_return_val_if_fail(result != NULL, NULL); + + str = g_string_new(""); + + sig = result->signatures; + while (sig != NULL) { + if (sig->timestamp != 0) { + created = sig->timestamp; + ctime_val = localtime(&created); + my_strftime(ctime_str, sizeof (ctime_str), "%c", + ctime_val); + ctime_str_utf8 = g_locale_to_utf8(ctime_str, -1, + NULL, NULL, NULL); + if (!ctime_str_utf8) + ctime_str_utf8 = g_strdup(ctime_str); + g_string_sprintfa(str, _("Signature made at %s\n"), + ctime_str_utf8); + g_free(ctime_str_utf8); + } + sig_status_for_key(str, ctx, sig); + if (sig->next) + g_string_append(str, "\n\n"); + sig = sig->next; + } + + retval = str->str; + g_string_free(str, FALSE); + return retval; +} + +static void check_signature(MimeInfo *mimeinfo, MimeInfo *partinfo, FILE *fp) +{ + gpgme_ctx_t ctx = NULL; + gpgme_error_t err; + gpgme_data_t sig = NULL, text = NULL; + gpgme_verify_result_t verifyresult = NULL; + GpgmegtkSigStatus statuswindow = NULL; + const gchar *result = NULL; + gchar *tmp_file; + gint n_exclude_chars = 0; + + if (prefs_common.gpg_signature_popup) + statuswindow = gpgmegtk_sig_status_create(); + + err = gpgme_new(&ctx); + if (err) { + debug_print("gpgme_new failed: %s\n", gpgme_strerror(err)); + goto leave; + } + + if (rfc2015_is_pkcs7_signature_part(partinfo)) { + debug_print("pkcs7 signature detected\n"); + gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS); + } + + /* don't include the last empty line. + It does not belong to the signed text */ + if (mimeinfo->children->size > 0) { + if (fseek(fp, mimeinfo->children->fpos + mimeinfo->children->size - 1, + SEEK_SET) < 0) { + perror("fseek"); + goto leave; + } + if (fgetc(fp) == '\n') { + n_exclude_chars++; + if (mimeinfo->children->size > 1) { + if (fseek(fp, mimeinfo->children->fpos + mimeinfo->children->size - 2, + SEEK_SET) < 0) { + perror("fseek"); + goto leave; + } + if (fgetc(fp) == '\r') + n_exclude_chars++; + } + } + } + + /* canonicalize the file part. */ + tmp_file = get_tmp_file(); + if (copy_file_part(fp, mimeinfo->children->fpos, + mimeinfo->children->size - n_exclude_chars, + tmp_file) < 0) { + g_free(tmp_file); + goto leave; + } + if (canonicalize_file_replace(tmp_file) < 0) { + g_unlink(tmp_file); + g_free(tmp_file); + goto leave; + } + + err = gpgme_data_new_from_file(&text, tmp_file, 1); + + g_unlink(tmp_file); + g_free(tmp_file); + + if (!err) + err = gpgme_data_new_from_filepart(&sig, NULL, fp, + partinfo->fpos, + partinfo->size); + if (err) { + debug_print("gpgme_data_new_from_filepart failed: %s\n", + gpgme_strerror (err)); + goto leave; + } + +#if 0 + if (partinfo->encoding_type == ENC_BASE64) { + err = gpgme_data_set_encoding(sig, GPGME_DATA_ENCODING_BASE64); + if (err) { + debug_print("gpgme_data_set_encoding failed: %s\n", + gpgme_strerror (err)); + goto leave; + } + } +#endif + + err = gpgme_op_verify(ctx, sig, text, NULL); + if (err) { + debug_print("gpgme_op_verify failed: %s\n", + gpgme_strerror (err)); + goto leave; + } + verifyresult = gpgme_op_verify_result(ctx); + + /* FIXME: check what the heck this sig_status_full stuff is. + * Maybe it belongs in sigstatus.c + * + * I think it belongs here as it is interfacing with gmime (Toshio). */ + g_free (partinfo->sigstatus_full); + partinfo->sigstatus_full = sig_status_full(ctx, verifyresult); + +leave: + if (verifyresult) { + result = gpgmegtk_sig_status_to_string + (verifyresult->signatures, FALSE); + } else { + result = _("Error verifying the signature"); + } + debug_print("verification status: %s\n", result); + if (prefs_common.gpg_signature_popup) + gpgmegtk_sig_status_update(statuswindow, ctx); + + g_free (partinfo->sigstatus); + partinfo->sigstatus = g_strdup (result); + + gpgme_data_release(sig); + gpgme_data_release(text); + if (ctx) + gpgme_release(ctx); + if (prefs_common.gpg_signature_popup) + gpgmegtk_sig_status_destroy(statuswindow); +} + +/* + * Copy a gpgme data object to a temporary file and + * return this filename + */ +#if 0 +static gchar *copy_gpgmedata_to_temp(GpgmeData data, guint *length) +{ + static gint id; + gchar *tmp; + FILE *fp; + gchar buf[100]; + size_t nread; + GpgmeError err; + + tmp = g_strdup_printf("%s%cgpgtmp.%08x", + get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id); + + if ((fp = g_fopen(tmp, "wb")) == NULL) { + FILE_OP_ERROR(tmp, "fopen"); + g_free(tmp); + return NULL; + } + + err = gpgme_data_rewind(data); + if (err) + debug_print("gpgme_data_rewind failed: %s\n", + gpgme_strerror(err)); + + while (!(err = gpgme_data_read(data, buf, 100, &nread))) { + fwrite(buf, nread, 1, fp); + } + + if (err != GPGME_EOF) + debug_print("gpgme_data_read failed: %s\n", + gpgme_strerror(err)); + + fclose (fp); + *length = nread; + + return tmp; +} +#endif + +static gpgme_data_t pgp_decrypt(MsgInfo *msginfo, MimeInfo *partinfo, FILE *fp) +{ + gpgme_ctx_t ctx = NULL; + gpgme_error_t err; + gpgme_data_t cipher = NULL, plain = NULL; + struct passphrase_cb_info_s info; + gpgme_verify_result_t verifyresult = NULL; + const gchar *result = NULL; + + memset(&info, 0, sizeof info); + + err = gpgme_new(&ctx); + if (err) { + debug_print("gpgme_new failed: %s\n", gpgme_strerror(err)); + goto leave; + } + + err = gpgme_data_new_from_filepart(&cipher, NULL, fp, + partinfo->fpos, partinfo->size); + if (err) { + debug_print("gpgme_data_new_from_filepart failed: %s\n", + gpgme_strerror(err)); + goto leave; + } + + err = gpgme_data_new(&plain); + if (err) { + debug_print("gpgme_new failed: %s\n", gpgme_strerror(err)); + goto leave; + } + + if (!g_getenv("GPG_AGENT_INFO")) { + info.c = ctx; + gpgme_set_passphrase_cb(ctx, gpgmegtk_passphrase_cb, &info); + } + + err = gpgme_op_decrypt_verify(ctx, cipher, plain); + + msginfo->encinfo = g_new0(MsgEncryptInfo, 1); + + if (err) { + gpgmegtk_free_passphrase(); + debug_print("decryption failed: %s\n", gpgme_strerror(err)); + gpgme_data_release(plain); + plain = NULL; + msginfo->encinfo->decryption_failed = TRUE; + goto leave; + } + + debug_print("** decryption succeeded\n"); + + verifyresult = gpgme_op_verify_result(ctx); + if (verifyresult && verifyresult->signatures) { + result = gpgmegtk_sig_status_to_string(verifyresult->signatures, + FALSE); + msginfo->encinfo->sigstatus = g_strdup(result); + msginfo->encinfo->sigstatus_full = + sig_status_full(ctx, verifyresult); + debug_print("verification status: %s\n", result); + debug_print("full status: %s\n", + msginfo->encinfo->sigstatus_full); + if (prefs_common.gpg_signature_popup) { + GpgmegtkSigStatus statuswindow; + statuswindow = gpgmegtk_sig_status_create(); + gpgmegtk_sig_status_update(statuswindow, ctx); + gpgmegtk_sig_status_destroy(statuswindow); + } + } + + +leave: + gpgme_data_release(cipher); + if (ctx) + gpgme_release(ctx); + return plain; +} + +MimeInfo **rfc2015_find_signature(MimeInfo *mimeinfo) +{ + MimeInfo *partinfo; + MimeInfo **signedinfo = NULL; + gint n = 0; + + if (!mimeinfo) + return NULL; + + /* We could have a signature nested within multipart/mixed so + * recurse to find it. + */ + if (!g_ascii_strcasecmp(mimeinfo->content_type, "multipart/mixed")) { + for (partinfo = mimeinfo->children; partinfo != NULL; + partinfo = partinfo->next) { + signedinfo = rfc2015_find_signature(partinfo); + if (signedinfo) { + return signedinfo; + } + } + return NULL; + } + if (g_ascii_strcasecmp(mimeinfo->content_type, "multipart/signed")) + return NULL; + + debug_print("** multipart/signed encountered\n"); + + /* check that we have at least 2 parts of the correct type */ + for (partinfo = mimeinfo->children; + partinfo != NULL; partinfo = partinfo->next) { + if (++n > 1 && rfc2015_is_signature_part(partinfo)) + break; + } + + if (partinfo) { + signedinfo = g_malloc(sizeof(MimeInfo *) * 2); + signedinfo[0] = mimeinfo; + signedinfo[1] = partinfo; + } + /* This is NULL if partinfo was not set */ + return signedinfo; +} + +gboolean rfc2015_has_signature(MimeInfo *mimeinfo) +{ + return rfc2015_find_signature(mimeinfo) != NULL; +} + +void rfc2015_check_signature(MimeInfo *mimeinfo, FILE *fp) +{ + MimeInfo **signedinfo; + + signedinfo = rfc2015_find_signature(mimeinfo); + if (!signedinfo) + return; + +#if 0 + g_message("** yep, it is a pgp signature"); + dump_mimeinfo("gpg-signature", partinfo ); + dump_part(partinfo, fp ); + dump_mimeinfo("signed text", mimeinfo->children ); + dump_part(mimeinfo->children, fp); +#endif + + check_signature(signedinfo[0], signedinfo[1], fp); + g_free(signedinfo); +} + +gboolean rfc2015_is_pgp_signature_part(MimeInfo *mimeinfo) +{ + if (!mimeinfo || !mimeinfo->content_type) + return FALSE; + + return !g_ascii_strcasecmp(mimeinfo->content_type, + "application/pgp-signature"); +} + +gboolean rfc2015_is_pkcs7_signature_part(MimeInfo *mimeinfo) +{ + const gchar *c_type; + + if (!mimeinfo || !mimeinfo->content_type) + return FALSE; + + c_type = mimeinfo->content_type; + + return (!g_ascii_strcasecmp(c_type, "application/pkcs7-signature") || + !g_ascii_strcasecmp(c_type, "application/x-pkcs7-signature")); +} + +gboolean rfc2015_is_signature_part(MimeInfo *mimeinfo) +{ + return (rfc2015_is_pgp_signature_part(mimeinfo) || + rfc2015_is_pkcs7_signature_part(mimeinfo)); +} + +gint rfc2015_is_encrypted(MimeInfo *mimeinfo) +{ + if (!mimeinfo || mimeinfo->mime_type != MIME_MULTIPART) + return 0; + if (g_ascii_strcasecmp(mimeinfo->content_type, "multipart/encrypted")) + return 0; + /* fixme: we should check the protocol parameter */ + return 1; +} + +gboolean rfc2015_msg_is_encrypted(const gchar *file) +{ + FILE *fp; + MimeInfo *mimeinfo; + gint ret; + + if ((fp = g_fopen(file, "rb")) == NULL) + return FALSE; + + mimeinfo = procmime_scan_mime_header(fp); + if(!mimeinfo) { + fclose(fp); + return FALSE; + } + + ret = rfc2015_is_encrypted(mimeinfo); + procmime_mimeinfo_free_all(mimeinfo); + fclose(fp); + + return ret != 0 ? TRUE : FALSE; +} + +static gint name_cmp(const gchar *a, const gchar *b) +{ + for( ; *a && *b; a++, b++) { + if (*a != *b && + g_ascii_toupper(*(guchar *)a) != g_ascii_toupper(*(guchar *)b)) + return 1; + } + + return *a != *b; +} + +static gint headerp(gchar *p, gchar **names) +{ + gint i, c; + gchar *p2; + + p2 = strchr(p, ':'); + if (!p2 || p == p2) { + return 0; + } + if (p2[-1] == ' ' || p2[-1] == '\t') { + return 0; + } + + if (!names[0]) + return 1; + + c = *p2; + *p2 = 0; + for(i = 0 ; names[i] != NULL; i++) { + if (!name_cmp(names[i], p)) + break; + } + *p2 = c; + + return names[i] != NULL; +} + + +#define DECRYPTION_ABORT() \ +{ \ + procmime_mimeinfo_free_all(tmpinfo); \ + if (msginfo->encinfo) \ + msginfo->encinfo->decryption_failed = TRUE; \ + return; \ +} + +void rfc2015_decrypt_message(MsgInfo *msginfo, MimeInfo *mimeinfo, FILE *fp) +{ + static gint id; + MimeInfo *tmpinfo, *partinfo; + gint ver_ok = 0; + gchar *fname; + gpgme_data_t plain; + FILE *dstfp; + ssize_t nread; + gchar buf[BUFFSIZE]; + gint in_cline; + gpgme_error_t err; + + g_return_if_fail(msginfo != NULL); + g_return_if_fail(mimeinfo != NULL); + g_return_if_fail(fp != NULL); + g_return_if_fail(mimeinfo->mime_type == MIME_MULTIPART); + + debug_print("** decrypting multipart/encrypted message\n"); + + /* skip headers */ + if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0) + perror("fseek"); + tmpinfo = procmime_scan_mime_header(fp); + if (!tmpinfo || tmpinfo->mime_type != MIME_MULTIPART) { + DECRYPTION_ABORT(); + } + + procmime_scan_multipart_message(tmpinfo, fp); + + /* check that we have the 2 parts */ + partinfo = tmpinfo->children; + if (!partinfo || !partinfo->next) { + DECRYPTION_ABORT(); + } + if (!g_ascii_strcasecmp(partinfo->content_type, + "application/pgp-encrypted")) { + /* Fixme: check that the version is 1 */ + ver_ok = 1; + } + partinfo = partinfo->next; + if (ver_ok && + !g_ascii_strcasecmp(partinfo->content_type, + "application/octet-stream")) { + if (partinfo->next) + g_warning("oops: pgp_encrypted with more than 2 parts"); + } else { + DECRYPTION_ABORT(); + } + + debug_print("** yep, it is pgp encrypted\n"); + + plain = pgp_decrypt(msginfo, partinfo, fp); + if (!plain) { + DECRYPTION_ABORT(); + } + + fname = g_strdup_printf("%s%cplaintext.%08x", + get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id); + + if ((dstfp = g_fopen(fname, "wb")) == NULL) { + FILE_OP_ERROR(fname, "fopen"); + g_free(fname); + DECRYPTION_ABORT(); + } + + /* write the orginal header to the new file */ + if (fseek(fp, tmpinfo->fpos, SEEK_SET) < 0) + perror("fseek"); + + in_cline = 0; + while (fgets(buf, sizeof(buf), fp)) { + if (headerp(buf, content_names)) { + in_cline = 1; + continue; + } + if (in_cline) { + if (buf[0] == ' ' || buf[0] == '\t') + continue; + in_cline = 0; + } + if (buf[0] == '\r' || buf[0] == '\n') + break; + fputs(buf, dstfp); + } + + err = (gpgme_data_seek(plain, 0, SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (err) + debug_print("gpgme_data_seek failed: %s\n", gpgme_strerror(err)); + + nread = gpgme_data_read(plain, buf, sizeof(buf)); + while (nread > 0) { + fwrite (buf, nread, 1, dstfp); + nread = gpgme_data_read(plain, buf, sizeof(buf)); + } + + if (nread != 0) { + debug_print("gpgme_data_read failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + } + + fclose(dstfp); + procmime_mimeinfo_free_all(tmpinfo); + + msginfo->encinfo->plaintext_file = fname; +} + +#undef DECRYPTION_ABORT + +FILE *rfc2015_open_message_decrypted(MsgInfo *msginfo, MimeInfo **mimeinfo) +{ + FILE *fp; + MimeInfo *mimeinfo_; + glong fpos; + + g_return_val_if_fail(msginfo != NULL, NULL); + + if (mimeinfo) *mimeinfo = NULL; + + if ((fp = procmsg_open_message(msginfo)) == NULL) return NULL; + + mimeinfo_ = procmime_scan_mime_header(fp); + if (!mimeinfo_) { + fclose(fp); + return NULL; + } + + if (!MSG_IS_ENCRYPTED(msginfo->flags) && + rfc2015_is_encrypted(mimeinfo_)) { + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_ENCRYPTED); + } + + if (MSG_IS_ENCRYPTED(msginfo->flags) && + (!msginfo->encinfo || + (!msginfo->encinfo->plaintext_file && + !msginfo->encinfo->decryption_failed))) { + fpos = ftell(fp); + rfc2015_decrypt_message(msginfo, mimeinfo_, fp); + if (msginfo->encinfo && + msginfo->encinfo->plaintext_file && + !msginfo->encinfo->decryption_failed) { + fclose(fp); + procmime_mimeinfo_free_all(mimeinfo_); + + if ((fp = procmsg_open_message(msginfo)) == NULL) + return NULL; + mimeinfo_ = procmime_scan_mime_header(fp); + if (!mimeinfo_) { + fclose(fp); + return NULL; + } + } else { + if (fseek(fp, fpos, SEEK_SET) < 0) + perror("fseek"); + } + } + + if (mimeinfo) *mimeinfo = mimeinfo_; + return fp; +} + + +/* + * plain contains an entire mime object. + * Encrypt it and return an GpgmeData object with the encrypted version of + * the file or NULL in case of error. + */ +static gpgme_data_t pgp_encrypt(gpgme_data_t plain, gpgme_key_t kset[]) +{ + gpgme_ctx_t ctx = NULL; + gpgme_error_t err; + gpgme_data_t cipher = NULL; + + err = gpgme_new(&ctx); + if (!err) + err = gpgme_data_new(&cipher); + if (!err) { + gpgme_set_armor(ctx, 1); + err = (gpgme_data_seek(plain, 0, SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (!err) { + /* + * Note -- it is currently the responsibility of select-keys.c:: + * gpgmegtk_recipient_selection() to prompt the user whether to + * encrypt to recipients whose key is not trusted. + */ + err = gpgme_op_encrypt(ctx, kset, + GPGME_ENCRYPT_ALWAYS_TRUST, + plain, cipher); + } + } + + if (err) { + g_warning("pgp_encrypt(): encryption failed: %s\n", + gpgme_strerror(err)); + gpgme_data_release(cipher); + cipher = NULL; + } else { + debug_print("** encryption succeeded\n"); + } + + if (ctx) + gpgme_release(ctx); + return cipher; +} + +/* + * Create and return a list of keys matching a key id + */ + +GSList *rfc2015_create_signers_list(const gchar *keyid) +{ + GSList *key_list = NULL; + gpgme_ctx_t list_ctx = NULL; + GSList *p; + gpgme_error_t err; + gpgme_key_t key; + + err = gpgme_new(&list_ctx); + if (err) + goto leave; + err = gpgme_op_keylist_start(list_ctx, keyid, 1); + if (err) + goto leave; + while (!(err = gpgme_op_keylist_next(list_ctx, &key))) { + key_list = g_slist_append(key_list, key); + } + if (gpgme_err_code(err) != GPG_ERR_EOF) + goto leave; + err = 0; + if (key_list == NULL) { + debug_print("no keys found for keyid \"%s\"\n", keyid); + } + +leave: + if (err) { + debug_print("rfc2015_create_signers_list failed: %s\n", + gpgme_strerror(err)); + for (p = key_list; p != NULL; p = p->next) + gpgme_key_unref((gpgme_key_t)p->data); + g_slist_free(key_list); + } + if (list_ctx) + gpgme_release(list_ctx); + return err ? NULL : key_list; +} + +/* + * Encrypt the file by extracting all recipients and finding the + * encryption keys for all of them. The file content is then replaced + * by the encrypted one. */ +gint rfc2015_encrypt(const gchar *file, GSList *recp_list) +{ + FILE *fp = NULL; + gchar buf[BUFFSIZE]; + gint i, clineidx, saved_last; + gchar *clines[3] = {NULL}; + gpgme_error_t err; + gpgme_data_t header = NULL; + gpgme_data_t plain = NULL; + gpgme_data_t cipher = NULL; + gpgme_key_t *kset = NULL; + ssize_t bytesRW = 0; + gint mime_version_seen = 0; + gchar *boundary; + + boundary = generate_mime_boundary("Encrypt"); + + /* Create the list of recipients */ + kset = gpgmegtk_recipient_selection(recp_list); + if (!kset) { + debug_print("error creating recipient list\n"); + goto failure; + } + + /* Open the source file */ + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + err = gpgme_data_new(&header); + if (!err) + err = gpgme_data_new(&plain); + if (err) { + debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err)); + goto failure; + } + + /* get the content header lines from the source */ + clineidx = 0; + saved_last = 0; + while (!err && fgets(buf, sizeof(buf), fp)) { + /* fixme: check for overlong lines */ + if (headerp(buf, content_names)) { + if (clineidx >= DIM(clines)) { + debug_print("rfc2015_encrypt: too many content lines\n"); + goto failure; + } + clines[clineidx++] = g_strdup(buf); + saved_last = 1; + continue; + } + if (saved_last) { + if (*buf == ' ' || *buf == '\t') { + gchar *last = clines[clineidx - 1]; + clines[clineidx - 1] = g_strconcat(last, buf, NULL); + g_free(last); + continue; + } + saved_last = 0; + } + + if (headerp(buf, mime_version_name)) + mime_version_seen = 1; + + if (buf[0] == '\r' || buf[0] == '\n') + break; + bytesRW = gpgme_data_write(header, buf, strlen(buf)); + } + if (ferror(fp)) { + FILE_OP_ERROR(file, "fgets"); + goto failure; + } + + /* write them to the temp data and add the rest of the message */ + for (i = 0; (bytesRW != -1) && i < clineidx; i++) { + debug_print("%% %s:%d: cline=`%s'", __FILE__ ,__LINE__, clines[i]); + bytesRW = gpgme_data_write(plain, clines[i], strlen(clines[i])); + } + if (bytesRW != -1) + bytesRW = gpgme_data_write (plain, "\r\n", 2); + while ((bytesRW != -1) && fgets(buf, sizeof(buf), fp)) { + bytesRW = gpgme_data_write(plain, buf, strlen(buf)); + } + if (ferror(fp)) { + FILE_OP_ERROR(file, "fgets"); + goto failure; + } + if (bytesRW == -1) { + debug_print("gpgme_data_write failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + cipher = pgp_encrypt(plain, kset); + gpgme_data_release(plain); + plain = NULL; + i = 0; + while (kset[i] != NULL) { + gpgme_key_unref(kset[i]); + i++; + } + g_free(kset); + kset = NULL; + if (!cipher) + goto failure; + + /* we have the encrypted message available in cipher and now we + * are going to rewrite the source file. To be sure that file has + * been truncated we use an approach which should work everywhere: + * close the file and then reopen it for writing. It is important + * that this works, otherwise it may happen that parts of the + * plaintext are still in the file (The encrypted stuff is, due to + * compression, usually shorter than the plaintext). + * + * Yes, there is a race condition here, but everyone, who is so + * stupid to store the temp file with the plaintext in a public + * directory has to live with this anyway. */ + if (fclose (fp)) { + FILE_OP_ERROR(file, "fclose"); + goto failure; + } + if ((fp = g_fopen(file, "wb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + /* Write the header, append new content lines, part 1 and part 2 header */ + err = (gpgme_data_seek(header, 0 , SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (err) { + debug_print("gpgme_data_seek failed: %s\n", + gpgme_strerror(err)); + goto failure; + } + bytesRW = gpgme_data_read(header, buf, BUFFSIZE); + while (bytesRW > 0) { + fwrite (buf, bytesRW, 1, fp); + bytesRW = gpgme_data_read(header, buf, BUFFSIZE); + } + + if (bytesRW != 0) { + debug_print("gpgme_data_read failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + if (ferror (fp)) { + FILE_OP_ERROR(file, "fwrite"); + goto failure; + } + gpgme_data_release(header); + header = NULL; + + if (!mime_version_seen) + fputs("MIME-Version: 1.0\r\n", fp); + + fprintf(fp, + "Content-Type: multipart/encrypted;" + " protocol=\"application/pgp-encrypted\";\r\n" + " boundary=\"%s\"\r\n" + "\r\n" + "--%s\r\n" + "Content-Type: application/pgp-encrypted\r\n" + "\r\n" + "Version: 1\r\n" + "\r\n" + "--%s\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n", + boundary, boundary, boundary); + + /* append the encrypted stuff */ + err = (gpgme_data_seek(cipher, 0 , SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (err) { + debug_print("** gpgme_data_seek on cipher failed: %s\n", + gpgme_strerror(err)); + debug_print("gpgme_data_seek failed: %s\n", + gpgme_strerror(err)); + goto failure; + } + + bytesRW = gpgme_data_read(cipher, buf, BUFFSIZE); + while (bytesRW > 0) { + fwrite(buf, bytesRW, 1, fp); + bytesRW = gpgme_data_read(cipher, buf, BUFFSIZE); + } + + if (bytesRW != 0) { + debug_print("** gpgme_data_read failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + /* and the final boundary */ + fprintf(fp, + "\r\n" + "--%s--\r\n", + boundary); + fflush(fp); + if (ferror(fp)) { + FILE_OP_ERROR(file, "fwrite"); + goto failure; + } + fclose(fp); + gpgme_data_release(cipher); + return 0; + +failure: + if (fp) + fclose (fp); + gpgme_data_release(header); + gpgme_data_release(plain); + gpgme_data_release(cipher); + + if (kset != NULL) { + i = 0; + while (kset[i] != NULL) { + gpgme_key_unref(kset[i]); + i++; + } + g_free(kset); + } + g_free(boundary); + return -1; /* error */ +} + +gint rfc2015_encrypt_armored(const gchar *file, GSList *recp_list) +{ + FILE *fp = NULL; + gchar buf[BUFFSIZE]; + gint i; + gpgme_error_t err; + gpgme_data_t plain = NULL; + gpgme_data_t cipher = NULL; + gpgme_key_t *kset = NULL; + ssize_t bytesRW = 0; + + kset = gpgmegtk_recipient_selection(recp_list); + if (!kset) { + debug_print("error creating recipient list\n"); + goto failure; + } + + /* Open the source file */ + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + err = gpgme_data_new(&plain); + if (err) { + g_warning("gpgme_data_new failed: %s\n", gpgme_strerror(err)); + goto failure; + } + + while (bytesRW != -1 && fgets(buf, sizeof(buf), fp)) { + bytesRW = gpgme_data_write(plain, buf, strlen(buf)); + } + if (ferror(fp)) { + FILE_OP_ERROR(file, "fgets"); + goto failure; + } + if (bytesRW == -1) { + debug_print("gpgme_data_write failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + cipher = pgp_encrypt(plain, kset); + gpgme_data_release(plain); + plain = NULL; + i = 0; + while (kset[i] != NULL) { + gpgme_key_unref(kset[i]); + i++; + } + g_free(kset); + kset = NULL; + if (!cipher) + goto failure; + + if (fclose(fp)) { + FILE_OP_ERROR(file, "fclose"); + goto failure; + } + if ((fp = g_fopen(file, "wb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + err = (gpgme_data_seek(cipher, 0 , SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (err) { + debug_print("** gpgme_data_seek on cipher failed: %s\n", + gpgme_strerror(err)); + debug_print("gpgme_data_seek failed: %s\n", + gpgme_strerror(err)); + goto failure; + } + + bytesRW = gpgme_data_read(cipher, buf, sizeof(buf)); + while (bytesRW > 0) { + fwrite(buf, bytesRW, 1, fp); + bytesRW = gpgme_data_read(cipher, buf, sizeof(buf)); + } + + if (bytesRW != 0) { + debug_print("** gpgme_data_read failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + fflush(fp); + if (ferror(fp)) { + FILE_OP_ERROR(file, "fwrite"); + goto failure; + } + fclose(fp); + gpgme_data_release(cipher); + return 0; + +failure: + if (fp) + fclose(fp); + gpgme_data_release(plain); + gpgme_data_release(cipher); + if (kset != NULL) { + i = 0; + while (kset[i] != NULL) { + gpgme_key_unref(kset[i]); + i++; + } + g_free(kset); + } + + return -1; +} + +/* + * plain contains an entire mime object. Sign it and return an + * GpgmeData object with the signature of it or NULL in case of error. + * micalg returns the micalg information about the signature. + */ +static gpgme_data_t pgp_sign(gpgme_data_t plain, GSList *key_list, + gboolean clearsign, gchar **micalg) +{ + GSList *p; + gpgme_ctx_t ctx = NULL; + gpgme_error_t err; + gpgme_data_t sig = NULL; + gpgme_sign_result_t result = NULL; + struct passphrase_cb_info_s info; + + *micalg = NULL; + memset(&info, 0, sizeof info); + + err = gpgme_new(&ctx); + if (err) + goto leave; + err = gpgme_data_new(&sig); + if (err) + goto leave; + + if (!g_getenv("GPG_AGENT_INFO")) { + info.c = ctx; + gpgme_set_passphrase_cb(ctx, gpgmegtk_passphrase_cb, &info); + } + gpgme_set_textmode(ctx, 1); + gpgme_set_armor(ctx, 1); + gpgme_signers_clear(ctx); + for (p = key_list; p != NULL; p = p->next) { + err = gpgme_signers_add(ctx, (gpgme_key_t) p->data); + if (err) + goto leave; + } + for (p = key_list; p != NULL; p = p->next) + gpgme_key_unref((gpgme_key_t) p->data); + g_slist_free(key_list); + + err = (gpgme_data_seek(plain, 0, SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (!err) { + err = gpgme_op_sign(ctx, plain, sig, + clearsign ? GPGME_SIG_MODE_CLEAR : GPGME_SIG_MODE_DETACH); + } + if (!err) { + result = gpgme_op_sign_result(ctx); + if (result && result->signatures) { + if (gpgme_get_protocol(ctx) == GPGME_PROTOCOL_OpenPGP) { + *micalg = g_strdup_printf + ("PGP-%s", gpgme_hash_algo_name(result->signatures->hash_algo)); + } else { + *micalg = g_strdup(gpgme_hash_algo_name(result->signatures->hash_algo)); + } + } else { + /* can't get result (maybe no signing key?) */ + err = GPG_ERR_USER_1; + } + } + +leave: + if (err) { + gpgmegtk_free_passphrase(); + g_warning("pgp_sign(): signing failed: %s\n", gpgme_strerror(err)); + gpgme_data_release(sig); + sig = NULL; + } else { + debug_print("signing succeeded\n"); + } + + if (ctx) + gpgme_release(ctx); + return sig; +} + +/* + * plain contains an entire mime object. Encrypt and sign it and return an + * GpgmeData object with the encrypted and signed version of it or NULL in + * case of error. + * micalg returns the micalg information about the signature. + */ +static gpgme_data_t pgp_encrypt_sign(gpgme_data_t plain, gpgme_key_t kset[], + GSList *key_list, gchar **micalg) +{ + GSList *p; + gpgme_ctx_t ctx = NULL; + gpgme_error_t err; + gpgme_data_t cipher = NULL; + gpgme_sign_result_t result = NULL; + struct passphrase_cb_info_s info; + + *micalg = NULL; + memset(&info, 0, sizeof info); + + err = gpgme_new(&ctx); + if (err) + goto leave; + err = gpgme_data_new(&cipher); + if (err) + goto leave; + + if (!g_getenv("GPG_AGENT_INFO")) { + info.c = ctx; + gpgme_set_passphrase_cb(ctx, gpgmegtk_passphrase_cb, &info); + } + gpgme_set_textmode(ctx, 1); + gpgme_set_armor(ctx, 1); + gpgme_signers_clear(ctx); + for (p = key_list; p != NULL; p = p->next) { + err = gpgme_signers_add(ctx, (gpgme_key_t) p->data); + if (err) + goto leave; + } + for (p = key_list; p != NULL; p = p->next) + gpgme_key_unref((gpgme_key_t) p->data); + g_slist_free(key_list); + + err = (gpgme_data_seek(plain, 0, SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (!err) { + err = gpgme_op_encrypt_sign(ctx, kset, GPGME_ENCRYPT_ALWAYS_TRUST, plain, cipher); + } + if (!err) { + result = gpgme_op_sign_result(ctx); + if (result && result->signatures) { + if (gpgme_get_protocol(ctx) == GPGME_PROTOCOL_OpenPGP) { + *micalg = g_strdup_printf + ("PGP-%s", gpgme_hash_algo_name(result->signatures->hash_algo)); + } else { + *micalg = g_strdup(gpgme_hash_algo_name(result->signatures->hash_algo)); + } + } else { + /* can't get result (maybe no signing key?) */ + err = GPG_ERR_USER_1; + } + } + +leave: + if (err) { + gpgmegtk_free_passphrase(); + g_warning("pgp_sign(): encryption and signing failed: %s\n", gpgme_strerror(err)); + gpgme_data_release(cipher); + cipher = NULL; + } else { + debug_print("encryption and signing succeeded\n"); + } + + if (ctx) + gpgme_release(ctx); + return cipher; +} + +/* + * Sign the file and replace its content with the signed one. + */ +gint rfc2015_sign(const gchar *file, GSList *key_list) +{ + FILE *fp = NULL; + gchar buf[BUFFSIZE]; + gint i, clineidx, saved_last; + gchar *clines[3] = {NULL}; + gpgme_error_t err; + gpgme_data_t header = NULL; + gpgme_data_t plain = NULL; + gpgme_data_t sigdata = NULL; + ssize_t bytesRW = -1; + gint mime_version_seen = 0; + gchar *boundary; + gchar *micalg = NULL; + + boundary = generate_mime_boundary("Signature"); + + /* Open the source file */ + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + err = gpgme_data_new(&header); + if (!err) + err = gpgme_data_new(&plain); + if (err) { + debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err)); + goto failure; + } + + /* get the content header lines from the source */ + clineidx = 0; + saved_last = 0; + while (!err && fgets(buf, sizeof(buf), fp)) { + /* fixme: check for overlong lines */ + if (headerp(buf, content_names)) { + if (clineidx >= DIM(clines)) { + debug_print("rfc2015_sign: too many content lines\n"); + goto failure; + } + clines[clineidx++] = g_strdup(buf); + saved_last = 1; + continue; + } + if (saved_last) { + if (*buf == ' ' || *buf == '\t') { + gchar *last = clines[clineidx - 1]; + clines[clineidx - 1] = g_strconcat(last, buf, NULL); + g_free(last); + continue; + } + saved_last = 0; + } + + if (headerp(buf, mime_version_name)) + mime_version_seen = 1; + + if (buf[0] == '\r' || buf[0] == '\n') + break; + bytesRW = gpgme_data_write(header, buf, strlen (buf)); + } + if (ferror (fp)) { + FILE_OP_ERROR(file, "fgets"); + goto failure; + } + + /* write them to the temp data and add the rest of the message */ + for (i = 0; (bytesRW != -1) && i < clineidx; i++) { + bytesRW = gpgme_data_write(plain, clines[i], strlen(clines[i])); + } + if (bytesRW != -1) + bytesRW = gpgme_data_write(plain, "\r\n", 2 ); + while ((bytesRW != -1) && fgets(buf, sizeof(buf), fp)) { + bytesRW = gpgme_data_write(plain, buf, strlen(buf)); + } + if (ferror(fp)) { + FILE_OP_ERROR(file, "fgets"); + goto failure; + } + if (bytesRW == -1) { + debug_print("gpgme_data_write failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + sigdata = pgp_sign(plain, key_list, FALSE, &micalg); + if (!sigdata) + goto failure; + + /* we have the signed message available in sigdata and now we are + * going to rewrite the original file. To be sure that file has + * been truncated we use an approach which should work everywhere: + * close the file and then reopen it for writing. */ + if (fclose(fp)) { + FILE_OP_ERROR(file, "fclose"); + goto failure; + } + if ((fp = g_fopen(file, "wb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + /* Write the rfc822 header and add new content lines */ + err = (gpgme_data_seek(header, 0, SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (err) { + debug_print("gpgme_data_seek failed: %s\n", + gpgme_strerror(err)); + goto failure; + } + bytesRW = gpgme_data_read(header, buf, BUFFSIZE); + while (bytesRW > 0) { + fwrite(buf, bytesRW, 1, fp); + bytesRW = gpgme_data_read(header, buf, BUFFSIZE); + } + if (bytesRW != 0) { + debug_print("gpgme_data_read failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + if (ferror(fp)) { + FILE_OP_ERROR(file, "fwrite"); + goto failure; + } + gpgme_data_release(header); + header = NULL; + + if (!mime_version_seen) + fputs("MIME-Version: 1.0\r\n", fp); + fprintf(fp, "Content-Type: multipart/signed; " + "protocol=\"application/pgp-signature\";\r\n"); + if (micalg) + fprintf(fp, " micalg=\"%s\";\r\n", micalg); + fprintf(fp, " boundary=\"%s\"\r\n", boundary); + + /* Part 1: signed material */ + fprintf(fp, "\r\n" + "--%s\r\n", + boundary); + err = (gpgme_data_seek(plain, 0, SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (err) { + debug_print("gpgme_data_seek on plain failed: %s\n", + gpgme_strerror(err)); + goto failure; + } + bytesRW = gpgme_data_read(plain, buf, BUFFSIZE); + while (bytesRW > 0) { + fwrite(buf, bytesRW, 1, fp); + bytesRW = gpgme_data_read(plain, buf, BUFFSIZE); + } + if (bytesRW != 0) { + debug_print("gpgme_data_read failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + /* Part 2: signature */ + fprintf(fp, "\r\n" + "--%s\r\n", + boundary); + fputs("Content-Type: application/pgp-signature\r\n" + "\r\n", fp); + + err = (gpgme_data_seek(sigdata, 0, SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (err) { + debug_print("gpgme_data_seek on sigdata failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + bytesRW = gpgme_data_read(sigdata, buf, BUFFSIZE); + while (bytesRW > 0) { + fwrite(buf, bytesRW, 1, fp); + bytesRW = gpgme_data_read(sigdata, buf, BUFFSIZE); + } + if (bytesRW != 0) { + debug_print("gpgme_data_read failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + /* Final boundary */ + fprintf(fp, "\r\n" + "--%s--\r\n", + boundary); + fflush(fp); + if (ferror(fp)) { + FILE_OP_ERROR(file, "fwrite"); + goto failure; + } + fclose(fp); + gpgme_data_release(header); + gpgme_data_release(plain); + gpgme_data_release(sigdata); + g_free(boundary); + g_free(micalg); + return 0; + +failure: + if (fp) + fclose(fp); + gpgme_data_release(header); + gpgme_data_release(plain); + gpgme_data_release(sigdata); + g_free(boundary); + g_free(micalg); + return -1; /* error */ +} + + +/* + * Sign the file with clear text and replace its content with the signed one. + */ +gint rfc2015_clearsign(const gchar *file, GSList *key_list) +{ + FILE *fp; + gchar buf[BUFFSIZE]; + gpgme_error_t err; + gpgme_data_t text = NULL; + gpgme_data_t sigdata = NULL; + ssize_t bytesRW = 0; + gchar *micalg = NULL; + + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + err = gpgme_data_new(&text); + if (err) { + debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err)); + goto failure; + } + + while ((bytesRW != -1) && fgets(buf, sizeof(buf), fp)) { + bytesRW = gpgme_data_write(text, buf, strlen(buf)); + } + if (ferror(fp)) { + FILE_OP_ERROR(file, "fgets"); + goto failure; + } + if (bytesRW == -1) { + debug_print("gpgme_data_write failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + sigdata = pgp_sign(text, key_list, TRUE, &micalg); + if (micalg) { + g_free(micalg); + } + if (!sigdata) + goto failure; + + if (fclose(fp) == EOF) { + FILE_OP_ERROR(file, "fclose"); + fp = NULL; + goto failure; + } + if ((fp = g_fopen(file, "wb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + err = (gpgme_data_seek(sigdata, 0, SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (err) { + debug_print("gpgme_data_seek on sigdata failed: %s\n", + gpgme_strerror(err)); + goto failure; + } + + bytesRW = gpgme_data_read(sigdata, buf, BUFFSIZE); + while (bytesRW > 0) { + fwrite(buf, bytesRW, 1, fp); + bytesRW = gpgme_data_read(sigdata, buf, BUFFSIZE); + } + if (bytesRW != 0) { + debug_print("gpgme_data_read failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + if (fclose(fp) == EOF) { + FILE_OP_ERROR(file, "fclose"); + fp = NULL; + goto failure; + } + gpgme_data_release(text); + gpgme_data_release(sigdata); + return 0; + +failure: + if (fp) + fclose(fp); + gpgme_data_release(text); + gpgme_data_release(sigdata); + return -1; +} + +/* + * Encrypt and sign the file and replace its content with the encrypted and + * signed one. + */ +gint rfc2015_encrypt_sign(const gchar *file, GSList *recp_list, + GSList *key_list) +{ + FILE *fp = NULL; + gchar buf[BUFFSIZE]; + gint i, clineidx, saved_last; + gchar *clines[3] = {NULL}; + gpgme_error_t err; + gpgme_data_t header = NULL; + gpgme_data_t plain = NULL; + gpgme_data_t cipher = NULL; + gpgme_key_t *kset = NULL; + ssize_t bytesRW = -1; + gint mime_version_seen = 0; + gchar *boundary; + gchar *micalg = NULL; + + boundary = generate_mime_boundary("Encrypt"); + + /* Create the list of recipients */ + kset = gpgmegtk_recipient_selection(recp_list); + if (!kset) { + debug_print("error creating recipient list\n"); + goto failure; + } + + /* Open the source file */ + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + err = gpgme_data_new(&header); + if (!err) + err = gpgme_data_new(&plain); + if (err) { + debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err)); + goto failure; + } + + /* get the content header lines from the source */ + clineidx = 0; + saved_last = 0; + while (!err && fgets(buf, sizeof(buf), fp)) { + /* fixme: check for overlong lines */ + if (headerp(buf, content_names)) { + if (clineidx >= DIM(clines)) { + debug_print("rfc2015_sign: too many content lines\n"); + goto failure; + } + clines[clineidx++] = g_strdup(buf); + saved_last = 1; + continue; + } + if (saved_last) { + if (*buf == ' ' || *buf == '\t') { + gchar *last = clines[clineidx - 1]; + clines[clineidx - 1] = g_strconcat(last, buf, NULL); + g_free(last); + continue; + } + saved_last = 0; + } + + if (headerp(buf, mime_version_name)) + mime_version_seen = 1; + + if (buf[0] == '\r' || buf[0] == '\n') + break; + bytesRW = gpgme_data_write(header, buf, strlen (buf)); + } + if (ferror (fp)) { + FILE_OP_ERROR(file, "fgets"); + goto failure; + } + + /* write them to the temp data and add the rest of the message */ + for (i = 0; (bytesRW != -1) && i < clineidx; i++) { + bytesRW = gpgme_data_write(plain, clines[i], strlen(clines[i])); + } + if (bytesRW != -1) + bytesRW = gpgme_data_write(plain, "\r\n", 2 ); + while ((bytesRW != -1) && fgets(buf, sizeof(buf), fp)) { + bytesRW = gpgme_data_write(plain, buf, strlen(buf)); + } + if (ferror(fp)) { + FILE_OP_ERROR(file, "fgets"); + goto failure; + } + if (bytesRW == -1) { + debug_print("gpgme_data_write failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + cipher = pgp_encrypt_sign(plain, kset, key_list, &micalg); + gpgme_data_release(plain); + plain = NULL; + for (i = 0; kset[i] != NULL; i++) + gpgme_key_unref(kset[i]); + g_free(kset); + kset = NULL; + if (!cipher) + goto failure; + + /* we have the signed message available in sigdata and now we are + * going to rewrite the original file. To be sure that file has + * been truncated we use an approach which should work everywhere: + * close the file and then reopen it for writing. */ + if (fclose(fp)) { + FILE_OP_ERROR(file, "fclose"); + goto failure; + } + if ((fp = g_fopen(file, "wb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + /* Write the rfc822 header and add new content lines */ + err = (gpgme_data_seek(header, 0, SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (err) { + debug_print("gpgme_data_seek failed: %s\n", + gpgme_strerror(err)); + goto failure; + } + bytesRW = gpgme_data_read(header, buf, BUFFSIZE); + while (bytesRW > 0) { + fwrite(buf, bytesRW, 1, fp); + bytesRW = gpgme_data_read(header, buf, BUFFSIZE); + } + if (bytesRW != 0) { + debug_print("gpgme_data_read failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + if (ferror(fp)) { + FILE_OP_ERROR(file, "fwrite"); + goto failure; + } + gpgme_data_release(header); + header = NULL; + + if (!mime_version_seen) + fputs("MIME-Version: 1.0\r\n", fp); + fprintf(fp, "Content-Type: multipart/encrypted;" + " protocol=\"application/pgp-encrypted\";\r\n" + " boundary=\"%s\"\r\n" + "\r\n" + "--%s\r\n" + "Content-Type: application/pgp-encrypted\r\n" + "\r\n" + "Version: 1\r\n" + "\r\n" + "--%s\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n", + boundary, boundary, boundary); + + /* append the encrypted stuff */ + err = (gpgme_data_seek(cipher, 0, SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (err) { + debug_print("gpgme_data_seek on cipher failed: %s\n", + gpgme_strerror(err)); + goto failure; + } + + bytesRW = gpgme_data_read(cipher, buf, BUFFSIZE); + while (bytesRW > 0) { + fwrite(buf, bytesRW, 1, fp); + bytesRW = gpgme_data_read(cipher, buf, BUFFSIZE); + } + if (bytesRW != 0) { + debug_print("gpgme_data_read failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + /* Final boundary */ + fprintf(fp, + "\r\n" + "--%s--\r\n", + boundary); + fflush(fp); + if (ferror(fp)) { + FILE_OP_ERROR(file, "fwrite"); + goto failure; + } + fclose(fp); + gpgme_data_release(cipher); + g_free(boundary); + g_free(micalg); + return 0; + +failure: + if (fp) + fclose(fp); + gpgme_data_release(header); + gpgme_data_release(plain); + gpgme_data_release(cipher); + + if (kset != NULL) { + for (i = 0; kset[i] != NULL; i++) + gpgme_key_unref(kset[i]); + g_free(kset); + } + g_free(boundary); + g_free(micalg); + return -1; /* error */ +} + +gint rfc2015_encrypt_sign_armored(const gchar *file, GSList *recp_list, + GSList *key_list) +{ + FILE *fp = NULL; + gchar buf[BUFFSIZE]; + gint i; + gpgme_error_t err; + gpgme_data_t plain = NULL; + gpgme_data_t cipher = NULL; + gpgme_key_t *kset = NULL; + ssize_t bytesRW = 0; + gchar *micalg = NULL; + + kset = gpgmegtk_recipient_selection(recp_list); + if (!kset) { + debug_print("error creating recipient list\n"); + goto failure; + } + + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + err = gpgme_data_new(&plain); + if (err) { + debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err)); + goto failure; + } + + while ((bytesRW != -1) && fgets(buf, sizeof(buf), fp)) { + bytesRW = gpgme_data_write(plain, buf, strlen(buf)); + } + if (ferror(fp)) { + FILE_OP_ERROR(file, "fgets"); + goto failure; + } + if (bytesRW == -1) { + debug_print("gpgme_data_write failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + cipher = pgp_encrypt_sign(plain, kset, key_list, &micalg); + if (micalg) + g_free(micalg); + if (!cipher) + goto failure; + + if (fclose(fp) == EOF) { + FILE_OP_ERROR(file, "fclose"); + fp = NULL; + goto failure; + } + if ((fp = g_fopen(file, "wb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + err = (gpgme_data_seek(cipher, 0, SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (err) { + debug_print("gpgme_data_seek on cipher failed: %s\n", + gpgme_strerror(err)); + goto failure; + } + + bytesRW = gpgme_data_read(cipher, buf, BUFFSIZE); + while (bytesRW > 0) { + fwrite(buf, bytesRW, 1, fp); + bytesRW = gpgme_data_read(cipher, buf, BUFFSIZE); + } + if (bytesRW != 0) { + debug_print("gpgme_data_read failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + if (fclose(fp) == EOF) { + FILE_OP_ERROR(file, "fclose"); + fp = NULL; + goto failure; + } + gpgme_data_release(plain); + gpgme_data_release(cipher); + for (i = 0; kset[i] != NULL; i++) + gpgme_key_unref(kset[i]); + g_free(kset); + return 0; + +failure: + if (fp) + fclose(fp); + gpgme_data_release(plain); + gpgme_data_release(cipher); + if (kset != NULL) { + for (i = 0; kset[i] != NULL; i++) + gpgme_key_unref(kset[i]); + g_free(kset); + } + return -1; +} + +#endif /* USE_GPGME */ diff --git a/src/rfc2015.h b/src/rfc2015.h new file mode 100644 index 0000000..167f388 --- /dev/null +++ b/src/rfc2015.h @@ -0,0 +1,66 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Werner Koch (dd9jn) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __RFC2015_H__ +#define __RFC2015_H__ + +#include +#include + +#include "procmsg.h" +#include "procmime.h" + +void rfc2015_disable_all (void); +gboolean rfc2015_is_available (void); + +void rfc2015_secure_remove (const gchar *fname); + +MimeInfo **rfc2015_find_signature (MimeInfo *mimeinfo); +gboolean rfc2015_has_signature (MimeInfo *mimeinfo); +void rfc2015_check_signature (MimeInfo *mimeinfo, + FILE *fp); +gboolean rfc2015_is_pgp_signature_part (MimeInfo *mimeinfo); +gboolean rfc2015_is_pkcs7_signature_part(MimeInfo *mimeinfo); +gboolean rfc2015_is_signature_part (MimeInfo *mimeinfo); + +gint rfc2015_is_encrypted (MimeInfo *mimeinfo); +gboolean rfc2015_msg_is_encrypted (const gchar *file); +void rfc2015_decrypt_message (MsgInfo *msginfo, + MimeInfo *mimeinfo, + FILE *fp); +FILE *rfc2015_open_message_decrypted (MsgInfo *msginfo, + MimeInfo **mimeinfo); + +GSList *rfc2015_create_signers_list (const gchar *keyid); +gint rfc2015_encrypt (const gchar *file, + GSList *recp_list); +gint rfc2015_encrypt_armored (const gchar *file, + GSList *recp_list); +gint rfc2015_sign (const gchar *file, + GSList *key_list); +gint rfc2015_clearsign (const gchar *file, + GSList *key_list); +gint rfc2015_encrypt_sign (const gchar *file, + GSList *recp_list, + GSList *key_list); +gint rfc2015_encrypt_sign_armored (const gchar *file, + GSList *recp_list, + GSList *key_list); + +#endif /* __RFC2015_H__ */ diff --git a/src/rpop3.c b/src/rpop3.c new file mode 100644 index 0000000..571671f --- /dev/null +++ b/src/rpop3.c @@ -0,0 +1,1418 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rpop3.h" +#include "mainwindow.h" +#include "folderview.h" +#include "prefs_account.h" +#include "socks.h" +#include "pop.h" +#include "procheader.h" +#include "procmsg.h" +#include "folder.h" +#include "inc.h" +#include "utils.h" +#include "gtkutils.h" +#include "manage_window.h" +#include "alertpanel.h" +#include "prefs_common.h" +#include "about.h" + +/* POP3 NOOP ping interval (sec) */ +#define POP3_PING_ITV 30 + +#define POP3_TOP (N_POP3_STATE + 1000) +#define POP3_TOP_RECV (N_POP3_STATE + 1001) +#define POP3_NOOP (N_POP3_STATE + 1002) +#define POP3_IDLE (N_POP3_STATE + 1003) + +enum +{ + COL_NUMBER, + COL_SUBJECT, + COL_FROM, + COL_DATE, + COL_SIZE, + COL_MSGINFO, + COL_DELETED, + N_COLS +}; + +static struct RPop3Window { + GtkWidget *window; + + GtkWidget *treeview; + GtkListStore *store; + + GtkWidget *status_label; + + GtkWidget *recv_btn; + GtkWidget *open_btn; + GtkWidget *delete_btn; + GtkWidget *stop_btn; + GtkWidget *close_btn; + + GtkAction *recv_action; + GtkAction *open_action; + GtkAction *delete_action; + GtkAction *update_action; + GtkAction *stop_action; + + Pop3Session *session; + guint ping_tag; + gboolean stop_load; + gboolean cancelled; + gboolean finished; + GArray *delete_array; + gint delete_cur; + GArray *recv_array; + gint recv_cur; +} rpop3_window; + +static const gchar *ui_def = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +static void rpop3_window_create (PrefsAccount *account); + +gint pop3_greeting_recv (Pop3Session *session, + const gchar *msg); +gint pop3_getauth_user_send (Pop3Session *session); +gint pop3_getauth_pass_send (Pop3Session *session); +gint pop3_getauth_apop_send (Pop3Session *session); +#if USE_SSL +gint pop3_stls_send (Pop3Session *session); +gint pop3_stls_recv (Pop3Session *session); +#endif +gint pop3_getrange_stat_send (Pop3Session *session); +gint pop3_getrange_stat_recv (Pop3Session *session, + const gchar *msg); +gint pop3_getrange_last_send (Pop3Session *session); +gint pop3_getrange_last_recv (Pop3Session *session, + const gchar *msg); +gint pop3_getrange_uidl_send (Pop3Session *session); +gint pop3_getrange_uidl_recv (Pop3Session *session, + const gchar *data, + guint len); +gint pop3_getsize_list_send (Pop3Session *session); +gint pop3_getsize_list_recv (Pop3Session *session, + const gchar *data, + guint len); +gint pop3_retr_send (Pop3Session *session); +gint pop3_retr_recv (Pop3Session *session, + FILE *fp, + guint len); +gint pop3_logout_send (Pop3Session *session); + +void pop3_gen_send (Pop3Session *session, + const gchar *format, ...); + +gint pop3_write_msg_to_file (const gchar *file, + FILE *src_fp, + guint len); + +Pop3ErrorValue pop3_ok (Pop3Session *session, + const gchar *msg); + +static gint rpop3_start (Session *session); +static void rpop3_status_label_set (const gchar *fmt, + ...) G_GNUC_PRINTF(1, 2); +static void rpop3_clear_list (void); + +static void rpop3_idle (gboolean is_idle); + +static gint rpop3_noop_send (Pop3Session *session); + +static gint rpop3_top_send (Pop3Session *session); +static gint rpop3_top_recv (Pop3Session *session, + FILE *fp, + guint len); +static gint rpop3_retr_send (Pop3Session *session); +static gint rpop3_retr_recv (Pop3Session *session, + FILE *fp, + guint len); +static gint rpop3_delete_send (Pop3Session *session); +static gint rpop3_delete_recv (Pop3Session *session); + +static gint rpop3_session_recv_msg (Session *session, + const gchar *msg); +static gint rpop3_session_recv_data_finished (Session *session, + guchar *data, + guint len); +static gint rpop3_session_recv_data_as_file_finished + (Session *session, + FILE *fp, + guint len); + +static gint window_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static gboolean key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); + +static void rpop3_row_activated (GtkTreeView *treeview, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data); + +static void rpop3_recv (GtkButton *button, + gpointer data); +static void rpop3_open (GtkButton *button, + gpointer data); +static void rpop3_delete (GtkButton *button, + gpointer data); +static void rpop3_stop (GtkButton *button, + gpointer data); +static void rpop3_close (GtkButton *button, + gpointer data); + +static void rpop3_recv_cb (void); +static void rpop3_open_cb (void); +static void rpop3_delete_cb (void); +static void rpop3_resume_cb (void); +static void rpop3_stop_cb (void); +static void rpop3_close_cb (void); +static void rpop3_about_cb (void); + +static gint cmp_by_subject (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint cmp_by_date (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint cmp_by_size (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); + +static GtkActionEntry action_entries[] = { + {"FileAction", NULL, N_("_File"), NULL, NULL, NULL}, + {"ReceiveAction", NULL, N_("_Get"), "G", NULL, rpop3_recv_cb}, + {"OpenAction", GTK_STOCK_OPEN, NULL, NULL, NULL, rpop3_open_cb}, + {"DeleteAction", GTK_STOCK_DELETE, NULL, "Delete", NULL, rpop3_delete_cb}, + {"ResumeAction", GTK_STOCK_REFRESH, N_("_Resume"), NULL, NULL, rpop3_resume_cb}, + {"StopAction", GTK_STOCK_STOP, NULL, NULL, NULL, rpop3_stop_cb}, + {"CloseAction", GTK_STOCK_CLOSE, NULL, NULL, NULL, rpop3_close_cb}, + {"HelpAction", NULL, N_("_Help"), NULL, NULL, NULL}, + {"AboutAction", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, + rpop3_about_cb} +}; + + +gint rpop3_account(PrefsAccount *account) +{ + Session *session; + gint ret; + + if (!account || account->protocol != A_POP3) + return -1; + if (inc_is_active()) + return -1; + if (rpop3_window.window) + return -1; + + inc_lock(); + + rpop3_window_create(account); + + session = pop3_session_new(account); + rpop3_window.session = POP3_SESSION(session); + rpop3_window.stop_load = FALSE; + rpop3_window.cancelled = FALSE; + rpop3_window.finished = FALSE; + if (POP3_SESSION(session)->uidl_table) { + hash_free_strings(POP3_SESSION(session)->uidl_table); + g_hash_table_destroy(POP3_SESSION(session)->uidl_table); + POP3_SESSION(session)->uidl_table = + g_hash_table_new(g_str_hash, g_str_equal); + } + + /* override Pop3Session handlers */ + session->recv_msg = rpop3_session_recv_msg; + session->send_data_finished = NULL; + session->recv_data_finished = rpop3_session_recv_data_finished; + session->recv_data_as_file_finished = + rpop3_session_recv_data_as_file_finished; + + if (!POP3_SESSION(session)->pass) { + gchar *pass; + + pass = input_query_password(account->recv_server, + account->userid); + if (pass) { + account->tmp_pass = g_strdup(pass); + POP3_SESSION(session)->pass = pass; + } + } + + session_set_timeout(session, prefs_common.io_timeout_secs * 1000); + + ret = rpop3_start(session); + + while (!rpop3_window.finished) + gtk_main_iteration(); + + if (POP3_SESSION(session)->error_val == PS_AUTHFAIL && + account->tmp_pass) { + debug_print("rpop3_account: remove temporary password because of authentication failure\n"); + g_free(account->tmp_pass); + account->tmp_pass = NULL; + } + + rpop3_idle(FALSE); + session_destroy(session); + rpop3_clear_list(); + gtk_widget_destroy(rpop3_window.window); + memset(&rpop3_window, 0, sizeof(rpop3_window)); + + inc_unlock(); + + return ret; +} + +static void rpop3_window_create(PrefsAccount *account) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkActionGroup *group; + GtkUIManager *ui; + GtkAction *action; + GtkWidget *menubar; + GtkWidget *vbox2; + GtkWidget *scrwin; + GtkWidget *treeview; + GtkListStore *store; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + GtkWidget *hbox; + GtkWidget *status_label; + GtkWidget *hbbox; + GtkWidget *recv_btn; + GtkWidget *open_btn; + GtkWidget *delete_btn; + GtkWidget *stop_btn; + GtkWidget *close_btn; + gchar buf[BUFFSIZE]; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + g_snprintf(buf, sizeof(buf), _("%s - POP3 Remote mailbox"), + account->account_name ? account->account_name : ""); + gtk_window_set_title(GTK_WINDOW(window), buf); + gtk_widget_set_size_request(window, 640 * gtkut_get_dpi_multiplier(), -1); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, TRUE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(window_deleted), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(window), vbox); + + group = gtk_action_group_new("rpop3"); + gtk_action_group_set_translation_domain(group, GETTEXT_PACKAGE); + gtk_action_group_add_actions(group, action_entries, + sizeof(action_entries) / + sizeof(action_entries[0]), NULL); + + ui = gtk_ui_manager_new(); + gtk_ui_manager_insert_action_group(ui, group, 0); + gtk_ui_manager_add_ui_from_string(ui, ui_def, -1, NULL); + menubar = gtk_ui_manager_get_widget(ui, "/RPop3Menu"); + gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); + gtk_window_add_accel_group(GTK_WINDOW(window), + gtk_ui_manager_get_accel_group(ui)); + + vbox2 = gtk_vbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0); + gtk_container_set_border_width(GTK_CONTAINER(vbox2), 8); + + scrwin = gtk_scrolled_window_new(NULL, NULL); + gtk_box_pack_start(GTK_BOX(vbox2), scrwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwin), + GTK_SHADOW_IN); + gtk_widget_set_size_request(scrwin, -1, 320 * gtkut_get_dpi_multiplier()); + + store = gtk_list_store_new(N_COLS, G_TYPE_INT, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, G_TYPE_BOOLEAN); + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(store); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_SUBJECT, + cmp_by_subject, NULL, NULL); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_DATE, + cmp_by_date, NULL, NULL); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_SIZE, + cmp_by_size, NULL, NULL); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + + gtk_container_add(GTK_CONTAINER(scrwin), treeview); + +#define APPEND_COLUMN(label, col, width) \ +{ \ + renderer = gtk_cell_renderer_text_new(); \ + column = gtk_tree_view_column_new_with_attributes \ + (label, renderer, "text", col, \ + "strikethrough", COL_DELETED, NULL); \ + gtk_tree_view_column_set_resizable(column, TRUE); \ + if (width) { \ + gtk_tree_view_column_set_sizing \ + (column, GTK_TREE_VIEW_COLUMN_FIXED); \ + gtk_tree_view_column_set_fixed_width(column, width); \ + } \ + gtk_tree_view_column_set_sort_column_id(column, col); \ + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); \ +} + + APPEND_COLUMN(_("No."), COL_NUMBER, 0); + APPEND_COLUMN(_("Subject"), COL_SUBJECT, 200); +#if GTK_CHECK_VERSION(2, 14, 0) + gtk_tree_view_column_set_expand(column, TRUE); +#endif + APPEND_COLUMN(_("From"), COL_FROM, 160); + APPEND_COLUMN(_("Date"), COL_DATE, 0); + APPEND_COLUMN(_("Size"), COL_SIZE, 0); + + gtk_widget_show_all(scrwin); + + g_signal_connect(G_OBJECT(treeview), "row-activated", + G_CALLBACK(rpop3_row_activated), NULL); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0); + + status_label = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(hbox), status_label, FALSE, FALSE, 0); + + hbbox = gtk_hbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(hbbox), 6); + gtk_box_pack_end(GTK_BOX(vbox2), hbbox, FALSE, FALSE, 0); + + recv_btn = gtk_button_new_with_mnemonic(_("_Get")); + gtk_box_pack_start(GTK_BOX(hbbox), recv_btn, FALSE, FALSE, 0); + gtk_widget_set_sensitive(recv_btn, FALSE); + action = gtk_ui_manager_get_action(ui, "/RPop3Menu/File/Receive"); + g_object_set(action, "sensitive", FALSE, NULL); + rpop3_window.recv_action = action; + + open_btn = gtk_button_new_from_stock(GTK_STOCK_OPEN); + gtk_box_pack_start(GTK_BOX(hbbox), open_btn, FALSE, FALSE, 0); + gtk_widget_set_sensitive(open_btn, FALSE); + action = gtk_ui_manager_get_action(ui, "/RPop3Menu/File/Open"); + g_object_set(action, "sensitive", FALSE, NULL); + rpop3_window.open_action = action; + + delete_btn = gtk_button_new_from_stock(GTK_STOCK_DELETE); + gtk_box_pack_start(GTK_BOX(hbbox), delete_btn, FALSE, FALSE, 0); + gtk_widget_set_sensitive(delete_btn, FALSE); + action = gtk_ui_manager_get_action(ui, "/RPop3Menu/File/Delete"); + g_object_set(action, "sensitive", FALSE, NULL); + rpop3_window.delete_action = action; + + action = gtk_ui_manager_get_action(ui, "/RPop3Menu/File/Resume"); + g_object_set(action, "sensitive", FALSE, NULL); + rpop3_window.update_action = action; + + stop_btn = gtk_button_new_from_stock(GTK_STOCK_STOP); + gtk_box_pack_start(GTK_BOX(hbbox), stop_btn, FALSE, FALSE, 0); + gtk_widget_set_sensitive(stop_btn, FALSE); + action = gtk_ui_manager_get_action(ui, "/RPop3Menu/File/Stop"); + g_object_set(action, "sensitive", FALSE, NULL); + rpop3_window.stop_action = action; + + close_btn = gtk_button_new_from_stock(GTK_STOCK_CLOSE); + gtk_box_pack_start(GTK_BOX(hbbox), close_btn, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(recv_btn), "clicked", + G_CALLBACK(rpop3_recv), NULL); + g_signal_connect(G_OBJECT(open_btn), "clicked", + G_CALLBACK(rpop3_open), NULL); + g_signal_connect(G_OBJECT(delete_btn), "clicked", + G_CALLBACK(rpop3_delete), NULL); + g_signal_connect(G_OBJECT(stop_btn), "clicked", + G_CALLBACK(rpop3_stop), NULL); + g_signal_connect(G_OBJECT(close_btn), "clicked", + G_CALLBACK(rpop3_close), NULL); + + gtk_widget_show_all(window); + + rpop3_window.window = window; + rpop3_window.treeview = treeview; + rpop3_window.store = store; + rpop3_window.status_label = status_label; + rpop3_window.recv_btn = recv_btn; + rpop3_window.open_btn = open_btn; + rpop3_window.delete_btn = delete_btn; + rpop3_window.stop_btn = stop_btn; + rpop3_window.close_btn = close_btn; +} + +static gint rpop3_start(Session *session) +{ + SessionState state; + PrefsAccount *ac; + SocksInfo *socks_info = NULL; + + g_return_val_if_fail(session != NULL, -1); + + rpop3_status_label_set(_("Connecting to %s:%d ..."), + session->server, session->port); + + ac = POP3_SESSION(session)->ac_prefs; + if (ac->use_socks && ac->use_socks_for_recv) { + socks_info = socks_info_new(ac->socks_type, ac->proxy_host, ac->proxy_port, ac->use_proxy_auth ? ac->proxy_name : NULL, ac->use_proxy_auth ? ac->proxy_pass : NULL); + } + + if (session_connect_full(session, session->server, session->port, + socks_info) < 0) { + manage_window_focus_in(rpop3_window.window, NULL, NULL); + alertpanel_error(_("Can't connect to POP3 server: %s:%d"), + session->server, session->port); + return -1; + } + + while (session_is_connected(session)) { + if (session->state == SESSION_READY && rpop3_window.cancelled) + break; + gtk_main_iteration(); + } + + state = session->state; + session_disconnect(session); + + switch (POP3_SESSION(session)->error_val) { + case PS_AUTHFAIL: + manage_window_focus_in(rpop3_window.window, NULL, NULL); + rpop3_status_label_set(_("Authentication failed.")); + if (POP3_SESSION(session)->error_msg) + alertpanel_error(_("Authentication failed:\n%s"), + POP3_SESSION(session)->error_msg); + else + alertpanel_error(_("Authentication failed.")); + return -1; + case PS_SUCCESS: + break; + default: + manage_window_focus_in(rpop3_window.window, NULL, NULL); + rpop3_status_label_set(_("Error occurred during POP3 session.")); + if (POP3_SESSION(session)->error_msg) + alertpanel_error + (_("Error occurred during POP3 session:\n%s"), + POP3_SESSION(session)->error_msg); + else + alertpanel_error(_("Error occurred during POP3 session.")); + return -1; + } + + switch (state) { + case SESSION_EOF: + manage_window_focus_in(rpop3_window.window, NULL, NULL); + rpop3_status_label_set + (_("Connection closed by the remote host.")); + alertpanel_error(_("Connection closed by the remote host.")); + return -1; + case SESSION_TIMEOUT: + manage_window_focus_in(rpop3_window.window, NULL, NULL); + rpop3_status_label_set(_("Session timed out.")); + alertpanel_error(_("Session timed out.")); + return -1; + case SESSION_ERROR: + manage_window_focus_in(rpop3_window.window, NULL, NULL); + if (POP3_SESSION(session)->state == POP3_READY) { + rpop3_status_label_set + (_("Can't connect to POP3 server: %s:%d"), + session->server, session->port); + alertpanel_error + (_("Can't connect to POP3 server: %s:%d"), + session->server, session->port); + } else { + rpop3_status_label_set + (_("Error occurred during POP3 session.")); + alertpanel_error + (_("Error occurred during POP3 session.")); + } + return -1; + case SESSION_READY: + debug_print("Session cancelled.\n"); + return -1; + default: + break; + } + + return 0; +} + +static void rpop3_status_label_set(const gchar *fmt, ...) +{ + va_list args; + gchar buf[1024]; + + va_start(args, fmt); + g_vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + gtk_label_set_text(GTK_LABEL(rpop3_window.status_label), buf); +} + +static gboolean clear_func(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + MsgInfo *msginfo; + + gtk_tree_model_get(model, iter, COL_MSGINFO, &msginfo, -1); + procmsg_msginfo_free(msginfo); + + return FALSE; +} + +static void rpop3_clear_list(void) +{ + + gtk_tree_model_foreach(GTK_TREE_MODEL(rpop3_window.store), clear_func, + NULL); + gtk_list_store_clear(rpop3_window.store); +} + +static gboolean rpop3_ping_cb(gpointer data) +{ + gdk_threads_enter(); + + if (rpop3_window.ping_tag > 0) { + g_source_remove(rpop3_window.ping_tag); + rpop3_window.ping_tag = 0; + } + if (rpop3_window.session->state == POP3_IDLE) + rpop3_noop_send(rpop3_window.session); + + gdk_threads_leave(); + + return FALSE; +} + +static void rpop3_idle(gboolean is_idle) +{ + if (rpop3_window.ping_tag > 0) + g_source_remove(rpop3_window.ping_tag); + rpop3_window.ping_tag = 0; + + if (is_idle) { + debug_print("Entered idle state\n"); + rpop3_window.session->state = POP3_IDLE; + if (POP3_PING_ITV < prefs_common.io_timeout_secs) + rpop3_window.ping_tag = + g_timeout_add_full(G_PRIORITY_LOW, + POP3_PING_ITV * 1000, + rpop3_ping_cb, NULL, NULL); + } +} + +static gint rpop3_noop_send(Pop3Session *session) +{ + session->state = POP3_NOOP; + pop3_gen_send(session, "NOOP"); + return PS_SUCCESS; +} + +static gint rpop3_top_send(Pop3Session *session) +{ + session->state = POP3_TOP; + pop3_gen_send(session, "TOP %d 0", session->cur_msg); + return PS_SUCCESS; +} + +static gint rpop3_top_recv(Pop3Session *session, FILE *fp, guint len) +{ + MsgInfo *msginfo; + MsgFlags flags = {0, 0}; + GtkTreeIter iter; + const gchar *subject, *from, *date; + gchar buf[1024]; + + session->msg[session->cur_msg].received = TRUE; + + msginfo = procheader_parse_stream(fp, flags, FALSE); + + msginfo->size = session->msg[session->cur_msg].size; + + subject = msginfo->subject ? msginfo->subject : _("(No Subject)"); + from = msginfo->from ? msginfo->from : _("(No From)"); + if (msginfo->date_t) { + procheader_date_get_localtime(buf, sizeof(buf), + msginfo->date_t); + date = buf; + } else if (msginfo->date) + date = msginfo->date; + else + date = _("(No Date)"); + + gtk_list_store_append(rpop3_window.store, &iter); + gtk_list_store_set(rpop3_window.store, &iter, + COL_NUMBER, session->cur_msg, + COL_SUBJECT, subject, + COL_FROM, from, + COL_DATE, date, + COL_SIZE, to_human_readable + (session->msg[session->cur_msg].size), + COL_MSGINFO, msginfo, + COL_DELETED, FALSE, + -1); + + rpop3_status_label_set(_("Retrieving message headers (%d / %d) ..."), + session->count - session->cur_msg + 1, + session->count); + + return PS_SUCCESS; +} + +static gint rpop3_retr_send(Pop3Session *session) +{ + if (rpop3_window.recv_array) { + g_return_val_if_fail(rpop3_window.recv_cur < rpop3_window.recv_array->len, -1); + session->cur_msg = g_array_index(rpop3_window.recv_array, gint, + rpop3_window.recv_cur); + } + + rpop3_idle(FALSE); + session->state = POP3_RETR; + pop3_gen_send(session, "RETR %d", session->cur_msg); + return PS_SUCCESS; +} + +static void msgview_destroy_cb(GtkWidget *widget, gpointer data) +{ + gchar *file = (gchar *)data; + + if (file) { + debug_print("msgview_destroy_cb: removing temporary file: %s\n", file); + g_unlink(file); + g_free(file); + } +} + +static gint rpop3_retr_recv(Pop3Session *session, FILE *fp, guint len) +{ + gchar *file; + MsgInfo *msginfo; + MsgFlags flags = {MSG_NEW|MSG_UNREAD, MSG_RECEIVED}; + MessageView *msgview; + + file = get_tmp_file(); + if (pop3_write_msg_to_file(file, fp, len) < 0) { + g_free(file); + session->error_val = PS_IOERR; + return -1; + } + + if (rpop3_window.recv_array) { + FolderItem *inbox; + + if (session->ac_prefs->inbox) { + inbox = folder_find_item_from_identifier + (session->ac_prefs->inbox); + if (!inbox) + inbox = folder_get_default_inbox(); + } else + inbox = folder_get_default_inbox(); + if (!inbox) { + session->error_val = PS_IOERR; + return -1; + } + + if (folder_item_add_msg(inbox, file, &flags, FALSE) < 0) { + session->error_val = PS_IOERR; + return -1; + } + if (rpop3_window.recv_cur + 1 == rpop3_window.recv_array->len) + folderview_update_item(inbox, TRUE); + else + folderview_update_item(inbox, FALSE); + + return PS_SUCCESS; + } + + msginfo = procheader_parse_file(file, flags, FALSE); + if (!msginfo) { + session->error_val = PS_IOERR; + return -1; + } + + msginfo->file_path = g_strdup(file); + + msgview = messageview_create_with_new_window(); + messageview_show(msgview, msginfo, FALSE); + + g_signal_connect(G_OBJECT(msgview->window), "destroy", + G_CALLBACK(msgview_destroy_cb), file); + + return PS_SUCCESS; +} + +static gint rpop3_delete_send(Pop3Session *session) +{ + g_return_val_if_fail(rpop3_window.delete_array != NULL, -1); + g_return_val_if_fail + (rpop3_window.delete_cur < rpop3_window.delete_array->len, -1); + + rpop3_idle(FALSE); + session->state = POP3_DELETE; + session->cur_msg = g_array_index(rpop3_window.delete_array, gint, + rpop3_window.delete_cur); + pop3_gen_send(session, "DELE %d", session->cur_msg); + return PS_SUCCESS; +} + +static gint rpop3_delete_recv(Pop3Session *session) +{ + session->msg[session->cur_msg].recv_time = RECV_TIME_DELETE; + session->msg[session->cur_msg].deleted = TRUE; + return PS_SUCCESS; +} + +static gint rpop3_session_recv_msg(Session *session, const gchar *msg) +{ + Pop3Session *pop3_session = POP3_SESSION(session); + gint val = PS_SUCCESS; + const gchar *body; + + gdk_threads_enter(); + + body = msg; + if (pop3_session->state != POP3_GETRANGE_UIDL_RECV && + pop3_session->state != POP3_GETSIZE_LIST_RECV) { + val = pop3_ok(pop3_session, msg); + if (val != PS_SUCCESS) { + if (val == PS_SOCKET) { + pop3_session->state = POP3_ERROR; + gdk_threads_leave(); + return -1; + } + if (val != PS_NOTSUPPORTED) { + if (pop3_session->state != POP3_LOGOUT) { + if (pop3_logout_send(pop3_session) == PS_SUCCESS) { + gdk_threads_leave(); + return 0; + } else { + gdk_threads_leave(); + return -1; + } + } + } + } + + if (*body == '+' || *body == '-') + body++; + while (g_ascii_isalpha(*body)) + body++; + while (g_ascii_isspace(*body)) + body++; + } + + switch (pop3_session->state) { + case POP3_READY: + case POP3_GREETING: + val = pop3_greeting_recv(pop3_session, body); + rpop3_status_label_set(_("Authenticating...")); +#if USE_SSL + if (pop3_session->ac_prefs->ssl_pop == SSL_STARTTLS) + val = pop3_stls_send(pop3_session); + else +#endif + if (pop3_session->ac_prefs->use_apop_auth) + val = pop3_getauth_apop_send(pop3_session); + else + val = pop3_getauth_user_send(pop3_session); + break; +#if USE_SSL + case POP3_STLS: + if ((val = pop3_stls_recv(pop3_session)) != PS_SUCCESS) + break; + if (pop3_session->ac_prefs->use_apop_auth) + val = pop3_getauth_apop_send(pop3_session); + else + val = pop3_getauth_user_send(pop3_session); + break; +#endif + case POP3_GETAUTH_USER: + val = pop3_getauth_pass_send(pop3_session); + break; + case POP3_GETAUTH_PASS: + case POP3_GETAUTH_APOP: + rpop3_status_label_set(_("Getting the number of messages...")); + val = pop3_getrange_stat_send(pop3_session); + break; + case POP3_GETRANGE_STAT: + if ((val = pop3_getrange_stat_recv(pop3_session, body)) != PS_SUCCESS) + break; + if (pop3_session->count > 0) + val = pop3_getrange_uidl_send(pop3_session); + else { + rpop3_status_label_set(_("No message")); + val = pop3_logout_send(pop3_session); + } + break; + case POP3_GETRANGE_LAST: + if (val == PS_NOTSUPPORTED) + pop3_session->error_val = PS_SUCCESS; + else if ((val = pop3_getrange_last_recv + (pop3_session, body)) != PS_SUCCESS) + break; + if (pop3_session->cur_msg > 0) + val = pop3_getsize_list_send(pop3_session); + else { + rpop3_status_label_set(_("No message")); + val = pop3_logout_send(pop3_session); + } + break; + case POP3_GETRANGE_UIDL: + if (val == PS_NOTSUPPORTED) { + pop3_session->error_val = PS_SUCCESS; + val = pop3_getrange_last_send(pop3_session); + } else { + pop3_session->state = POP3_GETRANGE_UIDL_RECV; + val = session_recv_data(session, 0, ".\r\n"); + } + break; + case POP3_GETSIZE_LIST: + pop3_session->state = POP3_GETSIZE_LIST_RECV; + val = session_recv_data(session, 0, ".\r\n"); + break; + case POP3_TOP: + pop3_session->state = POP3_TOP_RECV; + val = session_recv_data_as_file(session, 0, ".\r\n"); + break; + case POP3_RETR: + pop3_session->state = POP3_RETR_RECV; + val = session_recv_data_as_file(session, 0, ".\r\n"); + break; + case POP3_DELETE: + val = rpop3_delete_recv(pop3_session); + if (val != PS_SUCCESS) + break; + if (rpop3_window.delete_cur + 1 < rpop3_window.delete_array->len) { + rpop3_window.delete_cur++; + val = rpop3_delete_send(pop3_session); + } else { + rpop3_status_label_set(_("Deleted %d messages"), + rpop3_window.delete_cur + 1); + g_array_free(rpop3_window.delete_array, TRUE); + rpop3_window.delete_array = NULL; + rpop3_window.delete_cur = 0; + rpop3_idle(TRUE); + } + break; + case POP3_NOOP: + rpop3_idle(TRUE); + break; + case POP3_LOGOUT: + if (val == PS_SUCCESS) + pop3_session->state = POP3_DONE; + else + pop3_session->state = POP3_ERROR; + session_disconnect(session); + break; + case POP3_ERROR: + default: + val = -1; + break; + } + + gdk_threads_leave(); + + if (val == PS_SUCCESS) + return 0; + else + return -1; +} + +static gint rpop3_session_recv_data_finished(Session *session, guchar *data, + guint len) +{ + Pop3Session *pop3_session = POP3_SESSION(session); + Pop3ErrorValue val = PS_SUCCESS; + gint ret = 0; + + gdk_threads_enter(); + + switch (pop3_session->state) { + case POP3_GETRANGE_UIDL_RECV: + val = pop3_getrange_uidl_recv(pop3_session, (gchar *)data, len); + if (val == PS_SUCCESS) { + if (rpop3_window.cancelled) { + rpop3_status_label_set(_("Quitting...")); + pop3_logout_send(pop3_session); + } else + pop3_getsize_list_send(pop3_session); + } else + ret = -1; + break; + case POP3_GETSIZE_LIST_RECV: + val = pop3_getsize_list_recv(pop3_session, (gchar *)data, len); + if (val == PS_SUCCESS) { + pop3_session->cur_msg = 1; + if (rpop3_window.cancelled || pop3_session->count == 0) { + rpop3_status_label_set(_("Quitting...")); + pop3_logout_send(pop3_session); + } else { + pop3_session->cur_msg = pop3_session->count; + gtk_widget_set_sensitive(rpop3_window.stop_btn, + TRUE); + g_object_set(rpop3_window.update_action, + "sensitive", FALSE, NULL); + g_object_set(rpop3_window.stop_action, + "sensitive", TRUE, NULL); + rpop3_top_send(pop3_session); + } + } else + ret = -1; + break; + case POP3_ERROR: + default: + ret = -1; + break; + } + + gdk_threads_leave(); + + return ret; +} + +static gint rpop3_session_recv_data_as_file_finished(Session *session, + FILE *fp, guint len) +{ + Pop3Session *pop3_session = POP3_SESSION(session); + gint ret = 0; + + gdk_threads_enter(); + + switch (pop3_session->state) { + case POP3_RETR_RECV: + if (rpop3_retr_recv(pop3_session, fp, len) != PS_SUCCESS) { + ret = -1; + break; + } + if (rpop3_window.recv_array) { + if (rpop3_window.recv_cur + 1 < rpop3_window.recv_array->len) { + rpop3_window.recv_cur++; + if (rpop3_retr_send(pop3_session) != PS_SUCCESS) + ret = -1; + } else { + rpop3_status_label_set(_("Retrieved %d messages"), rpop3_window.recv_cur + 1); + g_array_free(rpop3_window.recv_array, TRUE); + rpop3_window.recv_array = NULL; + rpop3_window.recv_cur = 0; + rpop3_idle(TRUE); + } + } else { + rpop3_status_label_set(_("Opened message %d"), + pop3_session->cur_msg); + rpop3_idle(TRUE); + } + break; + case POP3_TOP_RECV: + if (rpop3_top_recv(pop3_session, fp, len) == PS_SUCCESS) { + if (rpop3_window.cancelled) { + rpop3_status_label_set(_("Quitting...")); + pop3_logout_send(pop3_session); + } else if (!rpop3_window.stop_load && + (pop3_session->cur_msg > 1)) { + pop3_session->cur_msg--; + rpop3_top_send(pop3_session); + } else { + if (pop3_session->cur_msg > 1) + rpop3_status_label_set + (_("Retrieved %d (of %d) message headers"), + pop3_session->count - pop3_session->cur_msg + 1, pop3_session->count); + else + rpop3_status_label_set + (_("Retrieved %d message headers"), + pop3_session->count - pop3_session->cur_msg + 1); + gtk_widget_set_sensitive + (rpop3_window.recv_btn, TRUE); + gtk_widget_set_sensitive + (rpop3_window.open_btn, TRUE); + gtk_widget_set_sensitive + (rpop3_window.delete_btn, TRUE); + if (pop3_session->cur_msg == 1) + gtk_widget_set_sensitive(rpop3_window.stop_btn, FALSE); + else + gtk_button_set_label(GTK_BUTTON(rpop3_window.stop_btn), _("_Resume")); + g_object_set(rpop3_window.recv_action, + "sensitive", TRUE, NULL); + g_object_set(rpop3_window.open_action, + "sensitive", TRUE, NULL); + g_object_set(rpop3_window.delete_action, + "sensitive", TRUE, NULL); + g_object_set(rpop3_window.stop_action, + "sensitive", FALSE, NULL); + if (pop3_session->cur_msg == 1) + g_object_set(rpop3_window.update_action, "sensitive", FALSE, NULL); + else + g_object_set(rpop3_window.update_action, "sensitive", TRUE, NULL); + rpop3_idle(TRUE); + } + } else + ret = -1; + break; + default: + ret = -1; + break; + } + + gdk_threads_leave(); + + return ret; +} + +static gint window_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + gtk_button_clicked(GTK_BUTTON(rpop3_window.close_btn)); + return TRUE; +} + +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + return FALSE; +} + +static void rpop3_row_activated(GtkTreeView *treeview, GtkTreePath *path, + GtkTreeViewColumn *column, gpointer data) +{ + gtk_button_clicked(GTK_BUTTON(rpop3_window.open_btn)); +} + +static void rpop3_recv(GtkButton *button, gpointer data) +{ + GtkTreeModel *model = GTK_TREE_MODEL(rpop3_window.store); + GtkTreeSelection *selection; + GtkTreeIter iter; + GList *rows, *cur; + gint num; + gboolean deleted; + GArray *array; + + if (rpop3_window.session->state != POP3_IDLE) + return; + + selection = gtk_tree_view_get_selection + (GTK_TREE_VIEW(rpop3_window.treeview)); + + rows = gtk_tree_selection_get_selected_rows(selection, NULL); + array = g_array_sized_new(FALSE, FALSE, sizeof(gint), + g_list_length(rows)); + + for (cur = rows; cur != NULL; cur = cur->next) { + gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)cur->data); + gtk_tree_model_get(model, &iter, COL_NUMBER, &num, + COL_DELETED, &deleted, -1); + if (!deleted) { + debug_print("rpop3_recv: receiving message %d\n", num); + g_array_append_val(array, num); + } + } + + g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL); + g_list_free(rows); + + if (array->len > 0) { + rpop3_window.recv_array = array; + rpop3_window.recv_cur = 0; + rpop3_retr_send(rpop3_window.session); + } else + g_array_free(array, TRUE); +} + +static void rpop3_open(GtkButton *button, gpointer data) +{ + GtkTreeModel *model = GTK_TREE_MODEL(rpop3_window.store); + GtkTreeSelection *selection; + GtkTreeIter iter; + GList *rows, *cur; + gint num; + gboolean deleted; + + if (rpop3_window.session->state != POP3_IDLE) + return; + + selection = gtk_tree_view_get_selection + (GTK_TREE_VIEW(rpop3_window.treeview)); + + rows = gtk_tree_selection_get_selected_rows(selection, NULL); + + for (cur = rows; cur != NULL; cur = cur->next) { + gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)cur->data); + gtk_tree_model_get(model, &iter, COL_NUMBER, &num, + COL_DELETED, &deleted, -1); + if (!deleted) { + debug_print("rpop3_open: opening message %d\n", num); + rpop3_status_label_set(_("Retrieving message %d ..."), + num); + rpop3_window.session->cur_msg = num; + rpop3_retr_send(rpop3_window.session); + break; + } + } + + g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL); + g_list_free(rows); +} + +static void rpop3_delete(GtkButton *button, gpointer data) +{ + GtkTreeModel *model = GTK_TREE_MODEL(rpop3_window.store); + GtkTreeSelection *selection; + GtkTreeIter iter; + GList *rows, *cur; + gint num; + gboolean deleted; + GArray *array; + AlertValue val; + + if (rpop3_window.session->state != POP3_IDLE) + return; + + val = alertpanel(_("Delete messages"), + _("Really delete selected messages from server?\n" + "This operation cannot be reverted."), + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if (val != G_ALERTDEFAULT) + return; + + selection = gtk_tree_view_get_selection + (GTK_TREE_VIEW(rpop3_window.treeview)); + + rows = gtk_tree_selection_get_selected_rows(selection, NULL); + + array = g_array_sized_new(FALSE, FALSE, sizeof(gint), + g_list_length(rows)); + + for (cur = rows; cur != NULL; cur = cur->next) { + gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)cur->data); + gtk_tree_model_get(model, &iter, COL_NUMBER, &num, + COL_DELETED, &deleted, -1); + if (!deleted) { + debug_print("rpop3_delete: marked %d to delete\n", num); + g_array_append_val(array, num); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, + COL_DELETED, TRUE, -1); + } + } + + g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL); + g_list_free(rows); + + if (array->len > 0) { + rpop3_window.delete_array = array; + rpop3_window.delete_cur = 0; + rpop3_delete_send(rpop3_window.session); + } else + g_array_free(array, TRUE); +} + +static void rpop3_read_next(GtkButton *button, gpointer data) +{ + gint i; + + if (rpop3_window.session->state != POP3_IDLE) + return; + + for (i = rpop3_window.session->count; i > 0; i--) { + if (!rpop3_window.session->msg[i].received) + break; + } + + if (i == 0) + return; + + debug_print("rpop3_read_next: next: %d\n", i); + rpop3_window.session->cur_msg = i; + + rpop3_window.stop_load = FALSE; + + gtk_widget_set_sensitive(rpop3_window.recv_btn, FALSE); + gtk_widget_set_sensitive(rpop3_window.open_btn, FALSE); + gtk_widget_set_sensitive(rpop3_window.delete_btn, FALSE); + gtk_button_set_label(GTK_BUTTON(rpop3_window.stop_btn), GTK_STOCK_STOP); + gtk_widget_set_sensitive(rpop3_window.stop_btn, TRUE); + g_object_set(rpop3_window.update_action, "sensitive", FALSE, NULL); + g_object_set(rpop3_window.stop_action, "sensitive", TRUE, NULL); + rpop3_idle(FALSE); + rpop3_top_send(rpop3_window.session); +} + +static void rpop3_stop(GtkButton *button, gpointer data) +{ + if (rpop3_window.session->state == POP3_IDLE) + rpop3_read_next(NULL, NULL); + else + rpop3_window.stop_load = TRUE; +} + +static void rpop3_close(GtkButton *button, gpointer data) +{ + rpop3_window.finished = TRUE; + + if (rpop3_window.session->state == POP3_IDLE) { + rpop3_status_label_set(_("Quitting...")); + rpop3_idle(FALSE); + pop3_logout_send(rpop3_window.session); + } else if (rpop3_window.session->state != POP3_DONE && + rpop3_window.session->state != POP3_ERROR) + rpop3_window.cancelled = TRUE; +} + +static void rpop3_recv_cb(void) +{ + rpop3_recv(NULL, NULL); +} + +static void rpop3_open_cb(void) +{ + rpop3_open(NULL, NULL); +} + +static void rpop3_delete_cb(void) +{ + rpop3_delete(NULL, NULL); +} + +static void rpop3_resume_cb(void) +{ + rpop3_stop(NULL, NULL); +} + +static void rpop3_stop_cb(void) +{ + rpop3_stop(NULL, NULL); +} + +static void rpop3_close_cb(void) +{ + rpop3_close(NULL, NULL); +} + +static void rpop3_about_cb(void) +{ + about_show(); +} + +static gint cmp_by_subject(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, + gpointer data) +{ + MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; + gint ret; + + gtk_tree_model_get(model, a, COL_MSGINFO, &msginfo_a, -1); + gtk_tree_model_get(model, b, COL_MSGINFO, &msginfo_b, -1); + + if (!msginfo_a || !msginfo_b) + return 0; + + if (!msginfo_a->subject) + return -(msginfo_b->subject != NULL); + if (!msginfo_b->subject) + return (msginfo_a->subject != NULL); + + ret = subject_compare_for_sort(msginfo_a->subject, msginfo_b->subject); + return (ret != 0) ? ret : (msginfo_a->msgnum - msginfo_b->msgnum); +} + +static gint cmp_by_date(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, + gpointer data) +{ + MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; + gint ret; + + gtk_tree_model_get(model, a, COL_MSGINFO, &msginfo_a, -1); + gtk_tree_model_get(model, b, COL_MSGINFO, &msginfo_b, -1); + + if (!msginfo_a || !msginfo_b) + return 0; + + ret = msginfo_a->date_t - msginfo_b->date_t; + return (ret != 0) ? ret : (msginfo_a->msgnum - msginfo_b->msgnum); +} + +static gint cmp_by_size(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, + gpointer data) +{ + MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; + gint ret; + + gtk_tree_model_get(model, a, COL_MSGINFO, &msginfo_a, -1); + gtk_tree_model_get(model, b, COL_MSGINFO, &msginfo_b, -1); + + if (!msginfo_a || !msginfo_b) + return 0; + + ret = msginfo_a->size - msginfo_b->size; + return (ret != 0) ? ret : (msginfo_a->msgnum - msginfo_b->msgnum); +} diff --git a/src/rpop3.h b/src/rpop3.h new file mode 100644 index 0000000..7f5d89a --- /dev/null +++ b/src/rpop3.h @@ -0,0 +1,34 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2008 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __RPOP3_H__ +#define __RPOP3_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "mainwindow.h" +#include "prefs_account.h" + +gint rpop3_account (PrefsAccount *account); + +#endif /* __RPOP3_H__ */ diff --git a/src/select-keys.c b/src/select-keys.c new file mode 100644 index 0000000..36c8edc --- /dev/null +++ b/src/select-keys.c @@ -0,0 +1,567 @@ +/* select-keys.c - GTK+ based key selection + * Copyright (C) 2001 Werner Koch (dd9jn) + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef USE_GPGME +#include +#include + +#include +#include +#include +#include + +#include "select-keys.h" +#include "utils.h" +#include "gtkutils.h" +#include "inputdialog.h" +#include "manage_window.h" +#include "alertpanel.h" + +#define DIM(v) (sizeof(v)/sizeof((v)[0])) +#define DIMof(type,member) DIM(((type *)0)->member) + + +enum col_titles { + COL_ALGO, + COL_KEYID, + COL_NAME, + COL_EMAIL, + COL_VALIDITY, + + N_COL_TITLES +}; + +struct select_keys_s { + int okay; + GtkWidget *window; + GtkLabel *toplabel; + GtkCList *clist; + const char *pattern; + unsigned int num_keys; + gpgme_key_t *kset; + gpgme_ctx_t select_ctx; + + GtkSortType sort_type; + enum col_titles sort_column; + +}; + + +static void set_row (GtkCList *clist, gpgme_key_t key); +static void fill_clist (struct select_keys_s *sk, const char *pattern); +static void create_dialog (struct select_keys_s *sk); +static void open_dialog (struct select_keys_s *sk); +static void close_dialog (struct select_keys_s *sk); +static gint delete_event_cb (GtkWidget *widget, + GdkEventAny *event, gpointer data); +static gboolean key_pressed_cb (GtkWidget *widget, + GdkEventKey *event, gpointer data); +static void select_btn_cb (GtkWidget *widget, gpointer data); +static void cancel_btn_cb (GtkWidget *widget, gpointer data); +static void other_btn_cb (GtkWidget *widget, gpointer data); +static void sort_keys (struct select_keys_s *sk, enum col_titles column); +static void sort_keys_name (GtkWidget *widget, gpointer data); +static void sort_keys_email (GtkWidget *widget, gpointer data); + +static gboolean use_untrusted (gpgme_key_t); + +static void +update_progress (struct select_keys_s *sk, int running, const char *pattern) +{ + static int windmill[] = { '-', '\\', '|', '/' }; + char *buf; + + if (!pattern) + pattern = ""; + if (!running) + buf = g_strdup_printf (_("Please select key for \"%s\""), + pattern); + else + buf = g_strdup_printf (_("Collecting info for \"%s\" ... %c"), + pattern, + windmill[running%DIM(windmill)]); + gtk_label_set_text (sk->toplabel, buf); + g_free (buf); +} + + +/** + * gpgmegtk_recipient_selection: + * @recp_names: A list of email addresses + * + * Select a list of recipients from a given list of email addresses. + * This may pop up a window to present the user a choice, it will also + * check that the recipients key are all valid. + * + * Return value: NULL on error or a list of list of recipients. + **/ +gpgme_key_t * +gpgmegtk_recipient_selection (GSList *recp_names) +{ + struct select_keys_s sk; + + memset (&sk, 0, sizeof sk); + + open_dialog (&sk); + + do { + sk.pattern = recp_names ? recp_names->data : NULL; + gtk_clist_clear (sk.clist); + fill_clist (&sk, sk.pattern); + update_progress (&sk, 0, sk.pattern); + gtk_main (); + if (recp_names) + recp_names = recp_names->next; + } while (sk.okay && recp_names); + + close_dialog (&sk); + + if (!sk.okay) { + g_free(sk.kset); + sk.kset = NULL; + } else { + sk.kset = g_realloc(sk.kset, sizeof(gpgme_key_t) * (sk.num_keys + 1)); + sk.kset[sk.num_keys] = NULL; + } + return sk.kset; +} + +static void +destroy_key (gpointer data) +{ + gpgme_key_t key = data; + gpgme_key_release (key); +} + +static void +set_row (GtkCList *clist, gpgme_key_t key) +{ + const char *s; + const char *text[N_COL_TITLES]; + char *algo_buf; + int row; + + /* first check whether the key is capable of encryption which is not + * the case for revoked, expired or sign-only keys */ + if (!key->can_encrypt) + return; + algo_buf = g_strdup_printf ("%du/%s", + key->subkeys->length, + gpgme_pubkey_algo_name(key->subkeys->pubkey_algo) ); + text[COL_ALGO] = algo_buf; + + s = key->subkeys->keyid; + if (strlen (s) == 16) + s += 8; /* show only the short keyID */ + text[COL_KEYID] = s; + + s = key->uids->name; + text[COL_NAME] = s; + + s = key->uids->email; + text[COL_EMAIL] = s; + + switch (key->uids->validity) + { + case GPGME_VALIDITY_UNDEFINED: + s = "q"; + break; + case GPGME_VALIDITY_NEVER: + s = "n"; + break; + case GPGME_VALIDITY_MARGINAL: + s = "m"; + break; + case GPGME_VALIDITY_FULL: + s = "f"; + break; + case GPGME_VALIDITY_ULTIMATE: + s = "u"; + break; + case GPGME_VALIDITY_UNKNOWN: + default: + s = "?"; + break; + } + text[COL_VALIDITY] = s; + + row = gtk_clist_append (clist, (gchar**)text); + g_free (algo_buf); + + gtk_clist_set_row_data_full (clist, row, key, destroy_key); +} + +static void +fill_clist (struct select_keys_s *sk, const char *pattern) +{ + GtkCList *clist; + gpgme_ctx_t ctx; + gpgme_error_t err; + gpgme_key_t key; + int running=0; + + g_return_if_fail (sk); + clist = sk->clist; + g_return_if_fail (clist); + + debug_print ("select_keys:fill_clist: pattern '%s'\n", + pattern ? pattern : ""); + + /*gtk_clist_freeze (select_keys.clist);*/ + err = gpgme_new (&ctx); + g_assert (!err); + + sk->select_ctx = ctx; + + update_progress (sk, ++running, pattern); + while (gtk_events_pending ()) + gtk_main_iteration (); + + err = gpgme_op_keylist_start (ctx, pattern, 0); + if (err) { + debug_print ("** gpgme_op_keylist_start(%s) failed: %s", + pattern ? pattern : "", gpgme_strerror (err)); + sk->select_ctx = NULL; + gpgme_release(ctx); + return; + } + update_progress (sk, ++running, pattern); + while ( !(err = gpgme_op_keylist_next ( ctx, &key )) ) { + debug_print ("%% %s:%d: insert\n", __FILE__ ,__LINE__ ); + set_row (clist, key ); key = NULL; + update_progress (sk, ++running, pattern); + while (gtk_events_pending ()) + gtk_main_iteration (); + } + debug_print ("%% %s:%d: ready\n", __FILE__ ,__LINE__ ); + if (gpgme_err_code(err) != GPG_ERR_EOF) { + debug_print ("** gpgme_op_keylist_next failed: %s", + gpgme_strerror (err)); + gpgme_op_keylist_end(ctx); + } + sk->select_ctx = NULL; + gpgme_release (ctx); + /*gtk_clist_thaw (select_keys.clist);*/ +} + + +static void +create_dialog (struct select_keys_s *sk) +{ + GtkWidget *window; + GtkWidget *vbox, *vbox2, *hbox; + GtkWidget *bbox; + GtkWidget *scrolledwin; + GtkWidget *clist; + GtkWidget *label; + GtkWidget *select_btn, *cancel_btn, *other_btn; + const char *titles[N_COL_TITLES]; + + g_assert (!sk->window); + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request (window, 520, 280); + gtk_container_set_border_width (GTK_CONTAINER (window), 8); + gtk_window_set_title (GTK_WINDOW (window), _("Select encryption keys")); + gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER); + gtk_window_set_modal (GTK_WINDOW (window), TRUE); + g_signal_connect (G_OBJECT (window), "delete_event", + G_CALLBACK (delete_event_cb), sk); + g_signal_connect (G_OBJECT (window), "key_press_event", + G_CALLBACK (key_pressed_cb), sk); + MANAGE_WINDOW_SIGNALS_CONNECT (window); + + vbox = gtk_vbox_new (FALSE, 8); + gtk_container_add (GTK_CONTAINER (window), vbox); + + hbox = gtk_hbox_new(FALSE, 4); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + label = gtk_label_new ( "" ); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 2); + + scrolledwin = gtk_scrolled_window_new (NULL, NULL); + gtk_box_pack_start (GTK_BOX (hbox), scrolledwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + titles[COL_ALGO] = _("Size"); + titles[COL_KEYID] = _("Key ID"); + titles[COL_NAME] = _("Name"); + titles[COL_EMAIL] = _("Address"); + titles[COL_VALIDITY] = _("Val"); + + clist = gtk_clist_new_with_titles (N_COL_TITLES, (char**)titles); + gtk_container_add (GTK_CONTAINER (scrolledwin), clist); + gtk_clist_set_column_width (GTK_CLIST(clist), COL_ALGO, 72); + gtk_clist_set_column_width (GTK_CLIST(clist), COL_KEYID, 76); + gtk_clist_set_column_width (GTK_CLIST(clist), COL_NAME, 130); + gtk_clist_set_column_width (GTK_CLIST(clist), COL_EMAIL, 130); + gtk_clist_set_column_width (GTK_CLIST(clist), COL_VALIDITY, 20); + gtk_clist_set_selection_mode (GTK_CLIST(clist), GTK_SELECTION_BROWSE); + gtkut_clist_set_redraw (GTK_CLIST(clist)); + g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_NAME].button), + "clicked", + G_CALLBACK(sort_keys_name), sk); + g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_EMAIL].button), + "clicked", + G_CALLBACK(sort_keys_email), sk); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + gtkut_stock_button_set_create (&bbox, + &select_btn, _("Select"), + &cancel_btn, GTK_STOCK_CANCEL, + &other_btn, _("Other")); + gtk_box_pack_end (GTK_BOX (hbox), bbox, FALSE, FALSE, 0); + gtk_widget_grab_default (select_btn); + gtk_widget_grab_focus (select_btn); + + g_signal_connect (G_OBJECT (select_btn), "clicked", + G_CALLBACK (select_btn_cb), sk); + g_signal_connect (G_OBJECT(cancel_btn), "clicked", + G_CALLBACK (cancel_btn_cb), sk); + g_signal_connect (G_OBJECT (other_btn), "clicked", + G_CALLBACK (other_btn_cb), sk); + + vbox2 = gtk_vbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0); + + gtk_widget_show_all (window); + + sk->window = window; + sk->toplabel = GTK_LABEL (label); + sk->clist = GTK_CLIST (clist); +} + + +static void +open_dialog (struct select_keys_s *sk) +{ + if (!sk->window) + create_dialog (sk); + manage_window_set_transient (GTK_WINDOW (sk->window)); + sk->okay = 0; + sk->sort_column = N_COL_TITLES; /* use an invalid value */ + sk->sort_type = GTK_SORT_ASCENDING; + gtk_widget_show (sk->window); +} + + +static void +close_dialog (struct select_keys_s *sk) +{ + g_return_if_fail (sk); + gtk_widget_destroy (sk->window); + sk->window = NULL; +} + + +static gint +delete_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer data) +{ + struct select_keys_s *sk = data; + + sk->okay = 0; + gtk_main_quit (); + + return TRUE; +} + + +static gboolean +key_pressed_cb (GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + struct select_keys_s *sk = data; + + g_return_val_if_fail (sk, FALSE); + if (event && event->keyval == GDK_Escape) { + sk->okay = 0; + gtk_main_quit (); + } + return FALSE; +} + + +static void +select_btn_cb (GtkWidget *widget, gpointer data) +{ + struct select_keys_s *sk = data; + int row; + gboolean use_key; + gpgme_key_t key; + + g_return_if_fail (sk); + if (!sk->clist->selection) { + debug_print ("** nothing selected"); + return; + } + row = GPOINTER_TO_INT(sk->clist->selection->data); + key = gtk_clist_get_row_data(sk->clist, row); + if (key) { + if ( key->uids->validity < GPGME_VALIDITY_FULL ) { + use_key = use_untrusted(key); + if (!use_key) { + debug_print ("** Key untrusted, will not encrypt"); + return; + } + } + sk->kset = g_realloc(sk->kset, + sizeof(gpgme_key_t) * (sk->num_keys + 1)); + gpgme_key_ref(key); + sk->kset[sk->num_keys] = key; + sk->num_keys++; + sk->okay = 1; + gtk_main_quit (); + } +} + + +static void +cancel_btn_cb (GtkWidget *widget, gpointer data) +{ + struct select_keys_s *sk = data; + + g_return_if_fail (sk); + sk->okay = 0; + if (sk->select_ctx) + gpgme_cancel (sk->select_ctx); + gtk_main_quit (); +} + + +static void +other_btn_cb (GtkWidget *widget, gpointer data) +{ + struct select_keys_s *sk = data; + char *uid; + + g_return_if_fail (sk); + uid = input_dialog ( _("Add key"), + _("Enter another user or key ID:"), + NULL ); + if (!uid) + return; + fill_clist (sk, uid); + update_progress (sk, 0, sk->pattern); + g_free (uid); +} + + +static gboolean +use_untrusted (gpgme_key_t key) +{ + AlertValue aval; + + aval = alertpanel + (_("Trust key"), + _("The selected key is not fully trusted.\n" + "If you choose to encrypt the message with this key you don't\n" + "know for sure that it will go to the person you mean it to.\n" + "Do you trust it enough to use it anyway?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if (aval == G_ALERTDEFAULT) + return TRUE; + else + return FALSE; +} + + +static gint +cmp_name (GtkCList *clist, gconstpointer pa, gconstpointer pb) +{ + gpgme_key_t a = ((GtkCListRow *)pa)->data; + gpgme_key_t b = ((GtkCListRow *)pb)->data; + const char *sa, *sb; + + sa = a? a->uids->name : NULL; + sb = b? b->uids->name : NULL; + if (!sa) + return !!sb; + if (!sb) + return -1; + return g_ascii_strcasecmp(sa, sb); +} + +static gint +cmp_email (GtkCList *clist, gconstpointer pa, gconstpointer pb) +{ + gpgme_key_t a = ((GtkCListRow *)pa)->data; + gpgme_key_t b = ((GtkCListRow *)pb)->data; + const char *sa, *sb; + + sa = a? a->uids->email : NULL; + sb = b? b->uids->email : NULL; + if (!sa) + return !!sb; + if (!sb) + return -1; + return g_ascii_strcasecmp(sa, sb); +} + +static void +sort_keys ( struct select_keys_s *sk, enum col_titles column) +{ + GtkCList *clist = sk->clist; + + switch (column) { + case COL_NAME: + gtk_clist_set_compare_func (clist, cmp_name); + break; + case COL_EMAIL: + gtk_clist_set_compare_func (clist, cmp_email); + break; + default: + return; + } + + /* column clicked again: toggle as-/decending */ + if ( sk->sort_column == column) { + sk->sort_type = sk->sort_type == GTK_SORT_ASCENDING ? + GTK_SORT_DESCENDING : GTK_SORT_ASCENDING; + } + else + sk->sort_type = GTK_SORT_ASCENDING; + + sk->sort_column = column; + gtk_clist_set_sort_type (clist, sk->sort_type); + gtk_clist_sort (clist); +} + +static void +sort_keys_name (GtkWidget *widget, gpointer data) +{ + sort_keys ((struct select_keys_s*)data, COL_NAME); +} + +static void +sort_keys_email (GtkWidget *widget, gpointer data) +{ + sort_keys ((struct select_keys_s*)data, COL_EMAIL); +} + +#endif /*USE_GPGME*/ diff --git a/src/select-keys.h b/src/select-keys.h new file mode 100644 index 0000000..4ca96e7 --- /dev/null +++ b/src/select-keys.h @@ -0,0 +1,29 @@ +/* select-keys.h - GTK+ based key selection + * Copyright (C) 2001 Werner Koch (dd9jn) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef GPGMEGTK_SELECT_KEYS_H +#define GPGMEGTK_SELECT_KEYS_H + +#include +#include + + +gpgme_key_t *gpgmegtk_recipient_selection (GSList *recp_names); + + +#endif /* GPGMEGTK_SELECT_KEYS_H */ diff --git a/src/send_message.c b/src/send_message.c new file mode 100644 index 0000000..a8c2c7a --- /dev/null +++ b/src/send_message.c @@ -0,0 +1,1034 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2012 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_SYS_WAIT_H +# include +#endif + +#include "send_message.h" +#include "session.h" +#include "ssl.h" +#include "smtp.h" +#include "news.h" +#include "imap.h" +#include "prefs_common.h" +#include "prefs_account.h" +#include "procheader.h" +#include "account.h" +#include "folder.h" +#include "procmsg.h" +#include "filter.h" +#include "progressdialog.h" +#include "statusbar.h" +#include "alertpanel.h" +#include "manage_window.h" +#include "socket.h" +#include "socks.h" +#include "utils.h" +#include "inc.h" +#include "mainwindow.h" +#include "summaryview.h" + +#define SMTP_PORT 25 +#if USE_SSL +#define SSMTP_PORT 465 +#endif + +typedef struct _SendProgressDialog SendProgressDialog; + +struct _SendProgressDialog +{ + ProgressDialog *dialog; + Session *session; + gboolean show_dialog; + gboolean cancelled; +}; + +static gint send_message_local (const gchar *command, + FILE *fp); +static gint send_message_smtp (PrefsAccount *ac_prefs, + GSList *to_list, + FILE *fp); + +static gint send_recv_message (Session *session, + const gchar *msg, + gpointer data); +static gint send_send_data_progressive (Session *session, + guint cur_len, + guint total_len, + gpointer data); +static gint send_send_data_finished (Session *session, + guint len, + gpointer data); + +static SendProgressDialog *send_progress_dialog_create(void); +static void send_progress_dialog_destroy(SendProgressDialog *dialog); + +static void send_cancel_button_cb (GtkWidget *widget, + gpointer data); + +static void send_put_error (Session *session); + + +gint send_message(const gchar *file, PrefsAccount *ac_prefs, GSList *to_list) +{ + FILE *fp; + gint val; + + g_return_val_if_fail(file != NULL, -1); + g_return_val_if_fail(ac_prefs != NULL, -1); + g_return_val_if_fail(to_list != NULL, -1); + + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + return -1; + } + + if (prefs_common.use_extsend && prefs_common.extsend_cmd) { + val = send_message_local(prefs_common.extsend_cmd, fp); + fclose(fp); + return val; + } + + val = send_message_smtp(ac_prefs, to_list, fp); + + fclose(fp); + return val; +} + +enum +{ + Q_SENDER = 0, + Q_SMTPSERVER = 1, + Q_RECIPIENTS = 2, + Q_ACCOUNT_ID = 3, + Q_REPLY_TARGET = 4, + Q_FORWARD_TARGETS = 5 +}; + +QueueInfo *send_get_queue_info(const gchar *file) +{ + static HeaderEntry qentry[] = {{"S:", NULL, FALSE}, + {"SSV:", NULL, FALSE}, + {"R:", NULL, FALSE}, + {"AID:", NULL, FALSE}, + {"REP:", NULL, FALSE}, + {"FWD:", NULL, FALSE}, + {NULL, NULL, FALSE}}; + FILE *fp; + gchar buf[BUFFSIZE]; + gint hnum; + QueueInfo *qinfo; + + g_return_val_if_fail(file != NULL, NULL); + + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + return NULL; + } + + qinfo = g_new0(QueueInfo, 1); + + while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry)) + != -1) { + gchar *p; + + p = buf + strlen(qentry[hnum].name); + + switch (hnum) { + case Q_SENDER: + if (!qinfo->from) + qinfo->from = g_strdup(p); + break; + case Q_SMTPSERVER: + if (!qinfo->server) + qinfo->server = g_strdup(p); + break; + case Q_RECIPIENTS: + qinfo->to_list = + address_list_append(qinfo->to_list, p); + break; + case Q_ACCOUNT_ID: + qinfo->ac = account_find_from_id(atoi(p)); + break; + case Q_REPLY_TARGET: + qinfo->reply_target = g_strdup(p); + break; + case Q_FORWARD_TARGETS: + qinfo->forward_targets = g_strdup(p); + break; + default: + break; + } + } + + qinfo->fp = fp; + + if (((!qinfo->ac || (qinfo->ac && qinfo->ac->protocol != A_NNTP)) && + !qinfo->to_list) || !qinfo->from) { + g_warning(_("Queued message header is broken.\n")); + send_queue_info_free(qinfo); + return NULL; + } + + if (!qinfo->ac) { + qinfo->ac = account_find_from_smtp_server(qinfo->from, + qinfo->server); + if (!qinfo->ac) { + g_warning("Account not found. " + "Using current account...\n"); + qinfo->ac = cur_account; + } + } + + return qinfo; +} + +static gint send_get_queue_contents(QueueInfo *qinfo, const gchar *dest) +{ + FILE *fp; + glong pos; + gchar buf[BUFFSIZE]; + + g_return_val_if_fail(qinfo != NULL, -1); + g_return_val_if_fail(qinfo->fp != NULL, -1); + g_return_val_if_fail(dest != NULL, -1); + + if ((fp = g_fopen(dest, "wb")) == NULL) { + FILE_OP_ERROR(dest, "fopen"); + return -1; + } + + pos = ftell(qinfo->fp); + + while (fgets(buf, sizeof(buf), qinfo->fp) != NULL) + fputs(buf, fp); + + if (fclose(fp) < 0) { + FILE_OP_ERROR(dest, "fclose"); + g_unlink(dest); + return -1; + } + + fseek(qinfo->fp, pos, SEEK_SET); + + return 0; +} + +static gint send_save_queued_message(QueueInfo *qinfo, gboolean filter_msgs) +{ + FolderItem *outbox; + gboolean drop_done = FALSE; + gchar tmp[MAXPATHLEN + 1]; + + g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x", + get_rc_dir(), G_DIR_SEPARATOR, g_random_int()); + + if (send_get_queue_contents(qinfo, tmp) < 0) + return -1; + + if (filter_msgs) { + FilterInfo *fltinfo; + + fltinfo = filter_info_new(); + fltinfo->account = qinfo->ac; + fltinfo->flags.perm_flags = 0; + fltinfo->flags.tmp_flags = MSG_RECEIVED; + + filter_apply(prefs_common.fltlist, tmp, fltinfo); + + drop_done = fltinfo->drop_done; + filter_info_free(fltinfo); + } + + if (!drop_done) { + outbox = account_get_special_folder(qinfo->ac, F_OUTBOX); + procmsg_save_to_outbox(outbox, tmp); + } + + g_unlink(tmp); + + return 0; +} + +void send_queue_info_free(QueueInfo *qinfo) +{ + if (qinfo == NULL) return; + + slist_free_strings(qinfo->to_list); + g_slist_free(qinfo->to_list); + g_free(qinfo->from); + g_free(qinfo->server); + g_free(qinfo->reply_target); + g_free(qinfo->forward_targets); + if (qinfo->fp) + fclose(qinfo->fp); + g_free(qinfo); +} + +gint send_message_queue(QueueInfo *qinfo) +{ + gint val = 0; + glong fpos; + PrefsAccount *mailac = NULL, *newsac = NULL; + + g_return_val_if_fail(qinfo != NULL, -1); + + fpos = ftell(qinfo->fp); + + if (prefs_common.use_extsend && prefs_common.extsend_cmd) { + val = send_message_local(prefs_common.extsend_cmd, qinfo->fp); + } else { + if (qinfo->ac && qinfo->ac->protocol == A_NNTP) { + newsac = qinfo->ac; + + /* search mail account */ + mailac = account_find_from_address(qinfo->from); + if (!mailac) { + if (cur_account && + cur_account->protocol != A_NNTP) + mailac = cur_account; + else { + mailac = account_get_default(); + if (mailac->protocol == A_NNTP) + mailac = NULL; + } + } + } else + mailac = qinfo->ac; + + if (qinfo->to_list) { + if (mailac) + val = send_message_smtp(mailac, qinfo->to_list, + qinfo->fp); + else { + PrefsAccount tmp_ac; + + g_warning("Account not found.\n"); + + memset(&tmp_ac, 0, sizeof(PrefsAccount)); + tmp_ac.address = qinfo->from; + tmp_ac.smtp_server = qinfo->server; + tmp_ac.smtpport = SMTP_PORT; + val = send_message_smtp(&tmp_ac, qinfo->to_list, + qinfo->fp); + } + } + + if (val == 0 && newsac) { + fseek(qinfo->fp, fpos, SEEK_SET); + val = news_post_stream(FOLDER(newsac->folder), + qinfo->fp); + if (val < 0) + alertpanel_error(_("Error occurred while posting the message to %s ."), + newsac->nntp_server); + } + } + + fseek(qinfo->fp, fpos, SEEK_SET); + + return val; +} + +gint send_message_queue_all(FolderItem *queue, gboolean save_msgs, + gboolean filter_msgs) +{ + gint ret = 0; + GSList *mlist = NULL; + GSList *cur; + + if (!queue) + queue = folder_get_default_queue(); + g_return_val_if_fail(queue != NULL, -1); + + mlist = folder_item_get_msg_list(queue, FALSE); + mlist = procmsg_sort_msg_list(mlist, SORT_BY_NUMBER, SORT_ASCENDING); + + for (cur = mlist; cur != NULL; cur = cur->next) { + gchar *file; + MsgInfo *msginfo = (MsgInfo *)cur->data; + QueueInfo *qinfo; + + file = procmsg_get_message_file(msginfo); + if (!file) + continue; + + qinfo = send_get_queue_info(file); + if (!qinfo || send_message_queue(qinfo) < 0) { + g_warning("Sending queued message %d failed.\n", + msginfo->msgnum); + send_queue_info_free(qinfo); + g_free(file); + continue; + } + + if (qinfo->reply_target) + send_message_set_reply_flag(qinfo->reply_target, + msginfo->inreplyto); + else if (qinfo->forward_targets) + send_message_set_forward_flags(qinfo->forward_targets); + + if (save_msgs) + send_save_queued_message(qinfo, filter_msgs); + + send_queue_info_free(qinfo); + g_free(file); + + folder_item_remove_msg(queue, msginfo); + ret++; + } + + procmsg_msg_list_free(mlist); + + procmsg_clear_cache(queue); + queue->cache_dirty = FALSE; + queue->mtime = 0; + + return ret; +} + +gint send_message_set_reply_flag(const gchar *target, const gchar *msgid) +{ + FolderItem *item; + gint num; + MsgInfo *msginfo; + SummaryView *summaryview; + + g_return_val_if_fail(target != NULL, -1); + + debug_print("send_message_set_reply_flag(): " + "setting reply flag to: %s\n", target); + + summaryview = main_window_get()->summaryview; + + item = folder_find_item_and_num_from_id(target, &num); + if (!item || num <= 0) + return -1; + + if (summaryview->folder_item == item) { + msginfo = summary_get_msginfo_by_msgnum(summaryview, num); + if (msginfo) { + if (msgid && strcmp2(msgid, msginfo->msgid) != 0) { + debug_print("send_message_set_reply_flag(): " + "message-id mismatch\n"); + return -1; + } + MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_FORWARDED); + MSG_SET_PERM_FLAGS(msginfo->flags, MSG_REPLIED); + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED); + if (MSG_IS_IMAP(msginfo->flags)) + imap_msg_set_perm_flags(msginfo, MSG_REPLIED); + if (msginfo->folder) + msginfo->folder->mark_dirty = TRUE; + summary_update_by_msgnum(summaryview, msginfo->msgnum); + } + } else { + msginfo = procmsg_get_msginfo(item, num); + if (msginfo) { + if (msgid && strcmp2(msgid, msginfo->msgid) != 0) { + debug_print("send_message_set_reply_flag(): " + "message-id mismatch\n"); + procmsg_msginfo_free(msginfo); + return -1; + } + MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_FORWARDED); + MSG_SET_PERM_FLAGS(msginfo->flags, MSG_REPLIED); + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED); + if (MSG_IS_IMAP(msginfo->flags)) + imap_msg_set_perm_flags(msginfo, MSG_REPLIED); + if (msginfo->folder) + msginfo->folder->mark_dirty = TRUE; + procmsg_add_flags(msginfo->folder, msginfo->msgnum, + msginfo->flags); + procmsg_msginfo_free(msginfo); + } + } + + return 0; +} + +gint send_message_set_forward_flags(const gchar *targets) +{ + FolderItem *item; + gint num; + MsgInfo *msginfo; + SummaryView *summaryview; + gchar **paths; + gint i; + GSList *mlist = NULL; + + g_return_val_if_fail(targets != NULL, -1); + + debug_print("send_message_set_forward_flags(): " + "setting forward flags to: %s\n", targets); + + summaryview = main_window_get()->summaryview; + + paths = g_strsplit(targets, "\n", 0); + + for (i = 0; paths[i] != NULL; i++) { + g_strstrip(paths[i]); + + item = folder_find_item_and_num_from_id(paths[i], &num); + if (!item || num <= 0) + continue; + + if (summaryview->folder_item == item) { + msginfo = summary_get_msginfo_by_msgnum(summaryview, + num); + if (msginfo) { + MSG_UNSET_PERM_FLAGS(msginfo->flags, + MSG_REPLIED); + MSG_SET_PERM_FLAGS(msginfo->flags, + MSG_FORWARDED); + MSG_SET_TMP_FLAGS(msginfo->flags, + MSG_FLAG_CHANGED); + if (msginfo->folder) + msginfo->folder->mark_dirty = TRUE; + summary_update_by_msgnum + (summaryview, msginfo->msgnum); + msginfo = procmsg_msginfo_copy(msginfo); + } + } else { + msginfo = procmsg_get_msginfo(item, num); + if (msginfo) { + MSG_UNSET_PERM_FLAGS(msginfo->flags, + MSG_REPLIED); + MSG_SET_PERM_FLAGS(msginfo->flags, + MSG_FORWARDED); + MSG_SET_TMP_FLAGS(msginfo->flags, + MSG_FLAG_CHANGED); + if (msginfo->folder) + msginfo->folder->mark_dirty = TRUE; + procmsg_add_flags(msginfo->folder, + msginfo->msgnum, + msginfo->flags); + } + } + + if (msginfo) + mlist = g_slist_append(mlist, msginfo); + } + + if (mlist) { + msginfo = (MsgInfo *)mlist->data; + if (MSG_IS_IMAP(msginfo->flags)) + imap_msg_list_unset_perm_flags(mlist, MSG_REPLIED); + } + + procmsg_msg_list_free(mlist); + g_strfreev(paths); + + return 0; +} + +static gint send_message_local(const gchar *command, FILE *fp) +{ + gchar **argv; + GPid pid; + gint child_stdin; + gchar buf[BUFFSIZE]; + gboolean err = FALSE; + gint status; + + g_return_val_if_fail(command != NULL, -1); + g_return_val_if_fail(fp != NULL, -1); + + log_message(_("Sending message using command: %s\n"), command); + + argv = strsplit_with_quote(command, " ", 0); + + if (g_spawn_async_with_pipes(NULL, argv, NULL, + G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, + &pid, &child_stdin, NULL, NULL, + NULL) == FALSE) { + g_snprintf(buf, sizeof(buf), + _("Can't execute command: %s"), command); + log_warning("%s\n", buf); + alertpanel_error("%s", buf); + g_strfreev(argv); + return -1; + } + g_strfreev(argv); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + strretchomp(buf); + if (buf[0] == '.' && buf[1] == '\0') { + if (fd_write_all(child_stdin, ".", 1) < 0) { + err = TRUE; + break; + } + } + if (fd_write_all(child_stdin, buf, strlen(buf)) < 0 || + fd_write_all(child_stdin, "\n", 1) < 0) { + err = TRUE; + break; + } + } + + fd_close(child_stdin); + +#ifdef G_OS_UNIX + waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + err = TRUE; +#endif + + g_spawn_close_pid(pid); + + if (err) { + g_snprintf(buf, sizeof(buf), + _("Error occurred while executing command: %s"), + command); + log_warning("%s\n", buf); + alertpanel_error("%s", buf); + return -1; + } + + return 0; +} + +static gint send_message_smtp(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp) +{ + Session *session; + SMTPSession *smtp_session; + SocksInfo *socks_info = NULL; + FILE *out_fp; + gushort port; + SendProgressDialog *dialog; + gchar buf[BUFFSIZE]; + gint ret = 0; + + g_return_val_if_fail(ac_prefs != NULL, -1); + g_return_val_if_fail(ac_prefs->address != NULL, -1); + g_return_val_if_fail(ac_prefs->smtp_server != NULL, -1); + g_return_val_if_fail(to_list != NULL, -1); + g_return_val_if_fail(fp != NULL, -1); + + session = smtp_session_new(); + smtp_session = SMTP_SESSION(session); + + smtp_session->hostname = + ac_prefs->set_domain ? g_strdup(ac_prefs->domain) : NULL; + + if (ac_prefs->use_smtp_auth) { + inc_lock(); + + smtp_session->forced_auth_type = ac_prefs->smtp_auth_type; + + if (ac_prefs->smtp_userid) { + smtp_session->user = g_strdup(ac_prefs->smtp_userid); + if (ac_prefs->smtp_passwd) + smtp_session->pass = + g_strdup(ac_prefs->smtp_passwd); + else if (ac_prefs->tmp_smtp_pass) + smtp_session->pass = + g_strdup(ac_prefs->tmp_smtp_pass); + else { + smtp_session->pass = + input_query_password + (ac_prefs->smtp_server, + smtp_session->user); + if (!smtp_session->pass) + smtp_session->pass = g_strdup(""); + ac_prefs->tmp_smtp_pass = + g_strdup(smtp_session->pass); + } + } else { + smtp_session->user = g_strdup(ac_prefs->userid); + if (ac_prefs->passwd) + smtp_session->pass = g_strdup(ac_prefs->passwd); + else if (ac_prefs->tmp_pass) + smtp_session->pass = + g_strdup(ac_prefs->tmp_pass); + else { + smtp_session->pass = + input_query_password + (ac_prefs->smtp_server, + smtp_session->user); + if (!smtp_session->pass) + smtp_session->pass = g_strdup(""); + ac_prefs->tmp_pass = + g_strdup(smtp_session->pass); + } + } + + inc_unlock(); + } else { + smtp_session->user = NULL; + smtp_session->pass = NULL; + } + + smtp_session->from = g_strdup(ac_prefs->address); + smtp_session->to_list = to_list; + smtp_session->cur_to = to_list; + + out_fp = get_outgoing_rfc2822_file(fp); + if (!out_fp) { + session_destroy(session); + return -1; + } + smtp_session->send_data_fp = out_fp; + smtp_session->send_data_len = get_left_file_size(out_fp); + if (smtp_session->send_data_len < 0) { + session_destroy(session); + return -1; + } + +#if USE_SSL + port = ac_prefs->set_smtpport ? ac_prefs->smtpport : + ac_prefs->ssl_smtp == SSL_TUNNEL ? SSMTP_PORT : SMTP_PORT; + session->ssl_type = ac_prefs->ssl_smtp; + if (ac_prefs->ssl_smtp != SSL_NONE) + session->nonblocking = ac_prefs->use_nonblocking_ssl; +#else + port = ac_prefs->set_smtpport ? ac_prefs->smtpport : SMTP_PORT; +#endif + + if (ac_prefs->pop_before_smtp && ac_prefs->protocol == A_POP3) { + if (inc_pop_before_smtp(ac_prefs) < 0) { + session_destroy(session); + return -1; + } + } + + dialog = send_progress_dialog_create(); + dialog->session = session; + + progress_dialog_append(dialog->dialog, NULL, ac_prefs->smtp_server, + _("Connecting"), "", NULL); + + g_snprintf(buf, sizeof(buf), _("Connecting to SMTP server: %s ..."), + ac_prefs->smtp_server); + progress_dialog_set_label(dialog->dialog, buf); + log_message("%s\n", buf); + + session_set_recv_message_notify(session, send_recv_message, dialog); + session_set_send_data_progressive_notify + (session, send_send_data_progressive, dialog); + session_set_send_data_notify(session, send_send_data_finished, dialog); + + session_set_timeout(session, prefs_common.io_timeout_secs * 1000); + + if (ac_prefs->use_socks && ac_prefs->use_socks_for_send) { + socks_info = socks_info_new(ac_prefs->socks_type, + ac_prefs->proxy_host, + ac_prefs->proxy_port, + ac_prefs->use_proxy_auth + ? ac_prefs->proxy_name : NULL, + ac_prefs->use_proxy_auth + ? ac_prefs->proxy_pass : NULL); + } + + inc_lock(); + + if (session_connect_full(session, ac_prefs->smtp_server, port, + socks_info) < 0) { + if (dialog->show_dialog) + manage_window_focus_in(dialog->dialog->window, NULL, NULL); + send_put_error(session); + if (dialog->show_dialog) + manage_window_focus_out(dialog->dialog->window, NULL, NULL); + session_destroy(session); + send_progress_dialog_destroy(dialog); + inc_unlock(); + return -1; + } + + debug_print("send_message_smtp(): begin event loop\n"); + + while (session_is_connected(session) && dialog->cancelled == FALSE) + gtk_main_iteration(); + log_window_flush(); + + if (SMTP_SESSION(session)->error_val == SM_AUTHFAIL) { + if (ac_prefs->smtp_userid && ac_prefs->tmp_smtp_pass) { + g_free(ac_prefs->tmp_smtp_pass); + ac_prefs->tmp_smtp_pass = NULL; + } else if (!ac_prefs->smtp_userid && ac_prefs->tmp_pass) { + g_free(ac_prefs->tmp_pass); + ac_prefs->tmp_pass = NULL; + } + ret = -1; + } else if (session->state == SESSION_EOF && + SMTP_SESSION(session)->state == SMTP_QUIT) { + /* consider EOF right after QUIT successful */ + log_warning("%s\n", _("Connection closed by the remote host.")); + ret = 0; + } else if (session->state == SESSION_ERROR && + SMTP_SESSION(session)->state == SMTP_QUIT) { + /* ignore errors right after QUIT */ + log_warning("%s\n", _("Error occurred after QUIT command (ignored)")); + ret = 0; + } else if (session->state == SESSION_ERROR || + session->state == SESSION_EOF || + session->state == SESSION_TIMEOUT || + SMTP_SESSION(session)->state == SMTP_ERROR || + SMTP_SESSION(session)->error_val != SM_OK) + ret = -1; + else if (dialog->cancelled == TRUE) + ret = -1; + + if (ret == -1) { + if (dialog->show_dialog) + manage_window_focus_in(dialog->dialog->window, NULL, NULL); + send_put_error(session); + if (dialog->show_dialog) + manage_window_focus_out(dialog->dialog->window, NULL, NULL); + } + + session_destroy(session); + send_progress_dialog_destroy(dialog); + inc_unlock(); + + return ret; +} + +static gint send_recv_message(Session *session, const gchar *msg, gpointer data) +{ + gchar buf[BUFFSIZE]; + SMTPSession *smtp_session = SMTP_SESSION(session); + SendProgressDialog *dialog = (SendProgressDialog *)data; + gchar *state_str = NULL; + + g_return_val_if_fail(dialog != NULL, -1); + + gdk_threads_enter(); + + switch (smtp_session->state) { + case SMTP_READY: + case SMTP_CONNECTED: + gdk_threads_leave(); + return 0; + case SMTP_HELO: + g_snprintf(buf, sizeof(buf), _("Sending HELO...")); + state_str = _("Authenticating"); + statusbar_print_all(_("Sending message via %s:%d..."), + session->server, session->port); + break; + case SMTP_EHLO: + g_snprintf(buf, sizeof(buf), _("Sending EHLO...")); + state_str = _("Authenticating"); + statusbar_print_all(_("Sending message via %s:%d..."), + session->server, session->port); + break; + case SMTP_AUTH: + g_snprintf(buf, sizeof(buf), _("Authenticating...")); + state_str = _("Authenticating"); + break; + case SMTP_FROM: + g_snprintf(buf, sizeof(buf), _("Sending MAIL FROM...")); + state_str = _("Sending"); + break; + case SMTP_RCPT: + g_snprintf(buf, sizeof(buf), _("Sending RCPT TO...")); + state_str = _("Sending"); + break; + case SMTP_DATA: + case SMTP_EOM: + g_snprintf(buf, sizeof(buf), _("Sending DATA...")); + state_str = _("Sending"); + break; + case SMTP_QUIT: + g_snprintf(buf, sizeof(buf), _("Quitting...")); + state_str = _("Quitting"); + break; + case SMTP_ERROR: + g_warning("send: error: %s\n", msg); + gdk_threads_leave(); + return 0; + default: + gdk_threads_leave(); + return 0; + } + + progress_dialog_set_label(dialog->dialog, buf); + progress_dialog_set_row_status(dialog->dialog, 0, state_str); + + gdk_threads_leave(); + + return 0; +} + +static gint send_send_data_progressive(Session *session, guint cur_len, + guint total_len, gpointer data) +{ + gchar buf[BUFFSIZE]; + SendProgressDialog *dialog = (SendProgressDialog *)data; + + g_return_val_if_fail(dialog != NULL, -1); + + if (SMTP_SESSION(session)->state != SMTP_SEND_DATA && + SMTP_SESSION(session)->state != SMTP_EOM) + return 0; + + gdk_threads_enter(); + + g_snprintf(buf, sizeof(buf), _("Sending message (%d / %d bytes)"), + cur_len, total_len); + progress_dialog_set_label(dialog->dialog, buf); + progress_dialog_set_percentage + (dialog->dialog, (gfloat)cur_len / (gfloat)total_len); + g_snprintf(buf, sizeof(buf), _("%d / %d bytes"), + cur_len, total_len); + progress_dialog_set_row_progress(dialog->dialog, 0, buf); +#ifdef G_OS_WIN32 + GTK_EVENTS_FLUSH(); +#endif + gdk_threads_leave(); + + return 0; +} + +static gint send_send_data_finished(Session *session, guint len, gpointer data) +{ + SendProgressDialog *dialog = (SendProgressDialog *)data; + + g_return_val_if_fail(dialog != NULL, -1); + + send_send_data_progressive(session, len, len, dialog); + + return 0; +} + +static SendProgressDialog *send_progress_dialog_create(void) +{ + SendProgressDialog *dialog; + ProgressDialog *progress; + + dialog = g_new0(SendProgressDialog, 1); + + progress = progress_dialog_create(); + gtk_window_set_title(GTK_WINDOW(progress->window), + _("Sending message")); + g_signal_connect(G_OBJECT(progress->cancel_btn), "clicked", + G_CALLBACK(send_cancel_button_cb), dialog); + g_signal_connect(G_OBJECT(progress->window), "delete_event", + G_CALLBACK(gtk_true), NULL); + /* gtk_window_set_modal(GTK_WINDOW(progress->window), TRUE); */ + manage_window_set_transient(GTK_WINDOW(progress->window)); + + progress_dialog_set_value(progress, 0.0); + + if (prefs_common.show_send_dialog) { + dialog->show_dialog = TRUE; + gtk_widget_show_now(progress->window); + } + + dialog->dialog = progress; + + return dialog; +} + +static void send_progress_dialog_destroy(SendProgressDialog *dialog) +{ + g_return_if_fail(dialog != NULL); + + manage_window_destroy(dialog->dialog->window, NULL); + progress_dialog_destroy(dialog->dialog); + g_free(dialog); +} + +static void send_cancel_button_cb(GtkWidget *widget, gpointer data) +{ + SendProgressDialog *dialog = (SendProgressDialog *)data; + + dialog->cancelled = TRUE; + session_disconnect(dialog->session); +} + +static void send_put_error(Session *session) +{ + gchar *msg; + gchar *log_msg = NULL; + gchar *err_msg = NULL; + + msg = SMTP_SESSION(session)->error_msg; + + switch (SMTP_SESSION(session)->error_val) { + case SM_ERROR: + case SM_UNRECOVERABLE: + log_msg = _("Error occurred while sending the message."); + if (msg) + err_msg = g_strdup_printf + (_("Error occurred while sending the message:\n%s"), + msg); + else + err_msg = g_strdup(log_msg); + break; + case SM_AUTHFAIL: + log_msg = _("Authentication failed."); + if (msg) + err_msg = g_strdup_printf + (_("Authentication failed:\n%s"), msg); + else + err_msg = g_strdup(log_msg); + break; + default: + switch (session->state) { + case SESSION_ERROR: + if (session_get_error(session) == SESSION_ERROR_LOOKUP) { + log_msg = _("Server not found."); + err_msg = g_strdup_printf + (_("SMTP server not found: %s:%d"), session->server, session->port); + } else if (SMTP_SESSION(session)->state == SMTP_READY) { + log_msg = _("Can't connect to SMTP server."); + err_msg = g_strdup_printf + (_("Can't connect to SMTP server: %s:%d"), session->server, session->port); + } else { + log_msg = _("Error occurred while sending the message."); + err_msg = g_strdup(log_msg); + } + break; + case SESSION_EOF: + log_msg = _("Connection closed by the remote host."); + err_msg = g_strdup(log_msg); + break; + case SESSION_TIMEOUT: + log_msg = _("Session timed out."); + err_msg = g_strdup(log_msg); + break; + default: + break; + } + break; + } + + if (log_msg) + log_warning("%s\n", log_msg); + if (err_msg) { + alertpanel_error("%s", err_msg); + g_free(err_msg); + } +} + diff --git a/src/send_message.h b/src/send_message.h new file mode 100644 index 0000000..814c81c --- /dev/null +++ b/src/send_message.h @@ -0,0 +1,58 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2007 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __SEND_MESSAGE_H__ +#define __SEND_MESSAGE_H__ + +#include +#include + +typedef struct _QueueInfo QueueInfo; + +#include "prefs_account.h" +#include "folder.h" +#include "procmsg.h" + +struct _QueueInfo +{ + gchar *from; + gchar *server; + GSList *to_list; + PrefsAccount *ac; + gchar *reply_target; + gchar *forward_targets; + FILE *fp; +}; + +gint send_message (const gchar *file, + PrefsAccount *ac_prefs, + GSList *to_list); + +QueueInfo *send_get_queue_info (const gchar *file); +void send_queue_info_free (QueueInfo *qinfo); +gint send_message_queue (QueueInfo *qinfo); +gint send_message_queue_all (FolderItem *queue, + gboolean save_msgs, + gboolean filter_msgs); + +gint send_message_set_reply_flag (const gchar *reply_target, + const gchar *msgid); +gint send_message_set_forward_flags (const gchar *forward_targets); + +#endif /* __SEND_H__ */ diff --git a/src/setup.c b/src/setup.c new file mode 100644 index 0000000..8d54af0 --- /dev/null +++ b/src/setup.c @@ -0,0 +1,1281 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#ifdef G_OS_WIN32 +# include +# include +# include +#endif + +#include "inputdialog.h" +#include "alertpanel.h" +#include "mainwindow.h" +#include "folderview.h" +#include "manage_window.h" +#include "account_dialog.h" +#include "gtkutils.h" +#include "filesel.h" +#include "prefs_common.h" +#include "stock_pixmap.h" +#include "account.h" +#include "addressbook.h" +#if USE_SSL +# include "ssl.h" +#endif + +static PangoFontDescription *font_desc; + +static void scan_tree_func(Folder *folder, FolderItem *item, gpointer data); + + +static void button_toggled(GtkToggleButton *button, GtkWidget *widget) +{ + gboolean is_active; + + is_active = gtk_toggle_button_get_active(button); + gtk_widget_set_sensitive(widget, is_active); +} + +static void sel_btn_clicked(GtkButton *button, GtkWidget *entry) +{ + gchar *folder; + gchar *utf8_folder; + gchar *base; + + folder = filesel_select_dir(NULL); + if (folder) { + utf8_folder = conv_filename_to_utf8(folder); + base = g_path_get_basename(utf8_folder); + if (!g_ascii_strcasecmp(base, "Mail")) { + gtk_entry_set_text(GTK_ENTRY(entry), utf8_folder); + } else { + gchar *text; + + text = g_strconcat(utf8_folder, G_DIR_SEPARATOR_S, "Mail", NULL); + gtk_entry_set_text(GTK_ENTRY(entry), text); + g_free(text); + } + g_free(base); + g_free(utf8_folder); + g_free(folder); + } +} + +#ifdef G_OS_WIN32 +#define MODIFY_LABEL_STYLE() \ + { \ + GtkStyle *style; \ + style = gtk_widget_get_style(dialog); \ + gtk_widget_modify_base(label, GTK_STATE_ACTIVE, \ + &style->base[GTK_STATE_SELECTED]); \ + gtk_widget_modify_text(label, GTK_STATE_ACTIVE, \ + &style->text[GTK_STATE_SELECTED]); \ + } +#else +#define MODIFY_LABEL_STYLE() +#endif + +void setup_mailbox(void) +{ + MainWindow *mainwin; + GtkWidget *dialog; + GtkWidget *hbox; + GtkWidget *image; + GtkWidget *vbox; + GtkWidget *label; + GtkWidget *radio; + GtkWidget *entry; + GtkWidget *sel_btn; + GtkWidget *ok_btn; + gchar *path = NULL; + gchar *fullpath; + Folder *folder; + gint result; + + mainwin = main_window_get(); + manage_window_focus_in(mainwin->window, NULL, NULL); + + dialog = gtk_dialog_new(); + gtk_window_set_title(GTK_WINDOW(dialog), _("Mailbox setting")); + gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, TRUE, FALSE); + gtk_widget_set_size_request(dialog, 540 * gtkut_get_dpi_multiplier(), -1); + gtk_window_set_position(GTK_WINDOW(dialog), + GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + manage_window_set_transient(GTK_WINDOW(dialog)); + gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE); + MANAGE_WINDOW_SIGNALS_CONNECT(dialog); + gtk_widget_realize(dialog); + + hbox = gtk_hbox_new(FALSE, 12); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 12); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), + hbox, FALSE, FALSE, 0); + + image = stock_pixbuf_widget(dialog, STOCK_PIXMAP_SYLPHEED); + + gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.0); + gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); + + vbox = gtk_vbox_new(FALSE, 12); + gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0); + + label = gtk_label_new(_("Mailbox setting")); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + + if (!font_desc) { + gint size; + + size = pango_font_description_get_size + (label->style->font_desc); + font_desc = pango_font_description_new(); + pango_font_description_set_weight + (font_desc, PANGO_WEIGHT_BOLD); + pango_font_description_set_size + (font_desc, size * PANGO_SCALE_LARGE); + } + if (font_desc) + gtk_widget_modify_font(label, font_desc); + + label = gtk_label_new(_("This dialog will make initial setup of mailbox.")); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + GTK_WIDGET_UNSET_FLAGS(label, GTK_CAN_FOCUS); + MODIFY_LABEL_STYLE(); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 12); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox, + TRUE, TRUE, 0); + + radio = gtk_radio_button_new_with_label + (NULL, _("Create mailbox at the following default location:")); + gtk_box_pack_start(GTK_BOX(vbox), radio, FALSE, FALSE, 0); + + fullpath = g_strdup_printf("%s%cMail", get_mail_base_dir(), + G_DIR_SEPARATOR); + + label = gtk_label_new(fullpath); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + gtk_label_set_line_wrap(GTK_LABEL(label), FALSE); +#if GTK_CHECK_VERSION(2, 6, 0) + gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE); +#endif + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + GTK_WIDGET_UNSET_FLAGS(label, GTK_CAN_FOCUS); + MODIFY_LABEL_STYLE(); + + g_free(fullpath); + + radio = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON(radio), _("Create mailbox at the following location:\n(enter folder name or full folder path)")); + gtk_box_pack_start(GTK_BOX(vbox), radio, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE, 4); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); + + sel_btn = gtk_button_new_with_label("..."); + gtk_box_pack_start(GTK_BOX(hbox), sel_btn, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(sel_btn), "clicked", + G_CALLBACK(sel_btn_clicked), entry); + + gtk_widget_set_sensitive(hbox, FALSE); + g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(button_toggled), + hbox); + + label = gtk_label_new(_("If you want to add a mailbox at another location afterward, please select 'File - Mailbox - Add mailbox...' in the menu.")); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + GTK_WIDGET_UNSET_FLAGS(label, GTK_CAN_FOCUS); + MODIFY_LABEL_STYLE(); + + if (prefs_common.comply_gnome_hig) { + gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + ok_btn = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OK, GTK_RESPONSE_OK); + } else { + ok_btn = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OK, GTK_RESPONSE_OK); + gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + } + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); + gtk_widget_grab_focus(ok_btn); + + gtk_widget_show_all(dialog); + + do { + result = gtk_dialog_run(GTK_DIALOG(dialog)); + if (result != GTK_RESPONSE_OK) { + if (alertpanel(_("Cancel"), _("Continue without creating mailbox?"), GTK_STOCK_YES, GTK_STOCK_NO, NULL) == G_ALERTDEFAULT) + break; + else + continue; + } + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio))) { + path = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1); + g_strstrip(path); + if (*path == '\0') { + alertpanel_error(_("Please input folder name or full folder path.")); + g_free(path); + path = NULL; + } + } else + path = g_strdup("Mail"); + + if (path) { + if (folder_find_from_path(path)) { + alertpanel_error(_("The mailbox '%s' already exists."), path); + g_warning("The mailbox '%s' already exists.", path); + g_free(path); + path = NULL; + } else if (is_path_parent(path, get_rc_dir()) || + is_path_parent(path, get_mail_base_dir())) { + alertpanel_error(_("The location '%s' includes settings folder. Please specify another location."), path); + g_warning("The location '%s' includes settings folder.", path); + g_free(path); + path = NULL; + } + } + } while (path == NULL); + + gtk_widget_destroy(dialog); + if (path == NULL) + return; + + if (!strcmp(g_basename(path), "Mail")) + folder = folder_new(F_MH, _("Mailbox"), path); + else + folder = folder_new(F_MH, g_basename(path), path); + g_free(path); + + if (folder->klass->create_tree(folder) < 0) { + alertpanel_error(_("Creation of the mailbox failed.\n" + "Maybe some files already exist, or you don't have the permission to write there.")); + folder_destroy(folder); + return; + } + + folder_add(folder); + folder_set_ui_func(folder, scan_tree_func, mainwin); + folder->klass->scan_tree(folder); + folder_set_ui_func(folder, NULL, NULL); + + folderview_set(mainwin->folderview); +} + +static void scan_tree_func(Folder *folder, FolderItem *item, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + gchar *str; + + if (item->path) + str = g_strdup_printf(_("Scanning folder %s%c%s ..."), + LOCAL_FOLDER(folder)->rootpath, + G_DIR_SEPARATOR, + item->path); + else + str = g_strdup_printf(_("Scanning folder %s ..."), + LOCAL_FOLDER(folder)->rootpath); + + gtk_statusbar_push(GTK_STATUSBAR(mainwin->statusbar), + mainwin->mainwin_cid, str); + gtkut_widget_draw_now(mainwin->statusbar); + gtk_statusbar_pop(GTK_STATUSBAR(mainwin->statusbar), + mainwin->mainwin_cid); + g_free(str); +} + +static struct +{ + GtkWidget *dialog; + GtkWidget *notebook; + GtkWidget *prev_btn; + GtkWidget *next_btn; + GtkWidget *cancel_btn; + GtkWidget *pop3_radio; + GtkWidget *imap_radio; +#if USE_SSL + GtkWidget *pop3g_radio; + GtkWidget *imapg_radio; +#endif + GtkWidget *name_entry; + GtkWidget *addr_entry; + GtkWidget *id_entry; + GtkWidget *serv_entry; + GtkWidget *smtp_entry; + GtkWidget *smtpauth_chkbtn; +#if USE_SSL + GtkWidget *servssl_chkbtn; + GtkWidget *smtpssl_chkbtn; +#endif + GtkWidget *serv_label_name1; + GtkWidget *serv_label_name2; + GtkWidget *name_label; + GtkWidget *addr_label; + GtkWidget *id_label; + GtkWidget *serv_label; + GtkWidget *smtp_label; + gboolean finished; + gboolean cancelled; + + gint type; + gchar *name; + gchar *addr; + gchar *userid; + gchar *serv; + gchar *smtpserv; + gushort serv_port; + gushort smtp_port; +#if USE_SSL + gboolean serv_ssl; + gboolean smtp_ssl; +#endif + gboolean smtp_auth; +} setupac; + +enum +{ + SETUP_PAGE_START, + SETUP_PAGE_ADDRESS, + SETUP_PAGE_ACCOUNT, + SETUP_PAGE_FINISH +}; + +enum +{ + SETUP_TYPE_POP3, + SETUP_TYPE_IMAP, +#if USE_SSL + SETUP_TYPE_POP3G, + SETUP_TYPE_IMAPG +#endif +}; + +#define GMAIL_POP3_SERVER "pop.gmail.com" +#define GMAIL_IMAP_SERVER "imap.gmail.com" +#define GMAIL_SMTP_SERVER "smtp.gmail.com" +#define POP3_PORT 110 +#define IMAP_PORT 143 +#define SMTP_PORT 25 +#define POP3S_PORT 995 +#define IMAPS_PORT 993 +#define SMTPS_PORT 465 + +static void entry_changed(GtkEditable *editable, gpointer data) +{ + const gchar *name, *addr, *userid, *serv, *smtp; + gint page; + gboolean next_enable = FALSE; + + page = gtk_notebook_get_current_page(GTK_NOTEBOOK(setupac.notebook)); + if (page != SETUP_PAGE_ADDRESS && page != SETUP_PAGE_ACCOUNT) + return; + + name = gtk_entry_get_text(GTK_ENTRY(setupac.name_entry)); + addr = gtk_entry_get_text(GTK_ENTRY(setupac.addr_entry)); + userid = gtk_entry_get_text(GTK_ENTRY(setupac.id_entry)); + serv = gtk_entry_get_text(GTK_ENTRY(setupac.serv_entry)); + smtp = gtk_entry_get_text(GTK_ENTRY(setupac.smtp_entry)); + +#if USE_SSL + if (setupac.type == SETUP_TYPE_POP3G || + setupac.type == SETUP_TYPE_IMAPG) { + if (GTK_WIDGET(editable) == setupac.addr_entry) + gtk_entry_set_text(GTK_ENTRY(setupac.id_entry), addr); + } +#endif + + if (page == SETUP_PAGE_ADDRESS && name && *name && addr && *addr) + next_enable = TRUE; + else if (page == SETUP_PAGE_ACCOUNT && + userid && *userid && serv && *serv && smtp && *smtp) + next_enable = TRUE; + + gtk_dialog_set_response_sensitive(GTK_DIALOG(setupac.dialog), + GTK_RESPONSE_ACCEPT, next_enable); +} + +static gboolean entry_is_valid(GtkWidget *entry) +{ + const gchar *str, *p; + guchar c; + + p = str = gtk_entry_get_text(GTK_ENTRY(entry)); + if (!str || *p == '\0') + return FALSE; + if (!strcmp(str, "(username)@gmail.com")) + return FALSE; + + while (*p) { + c = *p; + if (g_ascii_isspace(c) || c < 32 || c > 127) + return FALSE; + p++; + } + + return TRUE; +} + +#define GET_STR(s, m) \ +{ \ + setupac.s = gtk_editable_get_chars(GTK_EDITABLE(setupac.m), 0, -1); \ + g_strstrip(setupac.s); \ +} + +static void setup_account_response_cb(GtkDialog *dialog, gint response_id, + gpointer data) +{ + gint page, prev_page; + gboolean next_enable = TRUE; + gboolean prev_enable = TRUE; + gchar buf[1024]; + + prev_page = page = + gtk_notebook_get_current_page(GTK_NOTEBOOK(setupac.notebook)); + + if (response_id == GTK_RESPONSE_CANCEL || + response_id == GTK_RESPONSE_DELETE_EVENT) { + if (page == SETUP_PAGE_FINISH) { + setupac.finished = TRUE; + } else { + if (alertpanel(_("Cancel"), _("Cancel mail account setup?"), GTK_STOCK_YES, GTK_STOCK_NO, NULL) == G_ALERTDEFAULT) { + setupac.finished = TRUE; + setupac.cancelled = TRUE; + } + return; + } + } else if (response_id == GTK_RESPONSE_ACCEPT) { + if (prev_page == SETUP_PAGE_ADDRESS) { + if (entry_is_valid(setupac.addr_entry)) { +#if USE_SSL + if (setupac.type == SETUP_TYPE_POP3G || + setupac.type == SETUP_TYPE_IMAPG) + gtk_notebook_set_current_page + (GTK_NOTEBOOK(setupac.notebook), + SETUP_PAGE_FINISH); + else +#endif + gtk_notebook_set_current_page + (GTK_NOTEBOOK(setupac.notebook), page + 1); + } else + alertpanel_error(_("Input value is not valid.")); + } else if (prev_page == SETUP_PAGE_ACCOUNT) { + if (entry_is_valid(setupac.id_entry) && + entry_is_valid(setupac.serv_entry) && + entry_is_valid(setupac.smtp_entry)) + gtk_notebook_set_current_page + (GTK_NOTEBOOK(setupac.notebook), page + 1); + else + alertpanel_error(_("Input value is not valid.")); + } else { + gtk_notebook_set_current_page + (GTK_NOTEBOOK(setupac.notebook), page + 1); + } + + if (prev_page == SETUP_PAGE_START) { + setupac.type = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(setupac.pop3_radio)) ? SETUP_TYPE_POP3 + : gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(setupac.imap_radio)) ? SETUP_TYPE_IMAP +#if USE_SSL + : gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(setupac.pop3g_radio)) ? SETUP_TYPE_POP3G + : gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(setupac.imapg_radio)) ? SETUP_TYPE_IMAPG +#endif + : SETUP_TYPE_POP3; + } + } else if (response_id == GTK_RESPONSE_REJECT) { + gtk_notebook_set_current_page(GTK_NOTEBOOK(setupac.notebook), + page - 1); + } else { + g_warning("setup_account_response_cb: invalid response_id: %d\n", response_id); + } + + page = gtk_notebook_get_current_page(GTK_NOTEBOOK(setupac.notebook)); + + if (page == SETUP_PAGE_START) + prev_enable = FALSE; + else if (page == SETUP_PAGE_ADDRESS || page == SETUP_PAGE_ACCOUNT) { + switch (setupac.type) { + case SETUP_TYPE_POP3: + gtk_widget_set_sensitive(setupac.id_entry, TRUE); + gtk_label_set_text(GTK_LABEL(setupac.serv_label_name1), _("POP3 server:")); + gtk_widget_set_sensitive(setupac.serv_entry, TRUE); + gtk_widget_set_sensitive(setupac.smtp_entry, TRUE); + break; + case SETUP_TYPE_IMAP: + gtk_widget_set_sensitive(setupac.id_entry, TRUE); + gtk_label_set_text(GTK_LABEL(setupac.serv_label_name1), _("IMAP4 server:")); + gtk_widget_set_sensitive(setupac.serv_entry, TRUE); + gtk_widget_set_sensitive(setupac.smtp_entry, TRUE); + break; +#if USE_SSL + case SETUP_TYPE_POP3G: + if (prev_page == SETUP_PAGE_START) + gtk_entry_set_text(GTK_ENTRY(setupac.addr_entry), "(username)@gmail.com"); + gtk_widget_set_sensitive(setupac.id_entry, FALSE); + gtk_label_set_text(GTK_LABEL(setupac.serv_label_name1), _("POP3 server:")); + gtk_entry_set_text(GTK_ENTRY(setupac.serv_entry), GMAIL_POP3_SERVER); + gtk_widget_set_sensitive(setupac.serv_entry, FALSE); + gtk_entry_set_text(GTK_ENTRY(setupac.smtp_entry), GMAIL_SMTP_SERVER); + gtk_widget_set_sensitive(setupac.smtp_entry, FALSE); + break; + case SETUP_TYPE_IMAPG: + if (prev_page == SETUP_PAGE_START) + gtk_entry_set_text(GTK_ENTRY(setupac.addr_entry), "(username)@gmail.com"); + gtk_widget_set_sensitive(setupac.id_entry, FALSE); + gtk_label_set_text(GTK_LABEL(setupac.serv_label_name1), _("IMAP4 server:")); + gtk_entry_set_text(GTK_ENTRY(setupac.serv_entry), GMAIL_IMAP_SERVER); + gtk_widget_set_sensitive(setupac.serv_entry, FALSE); + gtk_entry_set_text(GTK_ENTRY(setupac.smtp_entry), GMAIL_SMTP_SERVER); + gtk_widget_set_sensitive(setupac.smtp_entry, FALSE); + break; +#endif /* USE_SSL */ + } + } else if (page == SETUP_PAGE_FINISH) { + prev_enable = FALSE; + next_enable = FALSE; + gtk_button_set_label(GTK_BUTTON(setupac.cancel_btn), + GTK_STOCK_CLOSE); + + switch (setupac.type) { + case SETUP_TYPE_POP3: +#if USE_SSL + setupac.serv_ssl = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(setupac.servssl_chkbtn)); + setupac.smtp_ssl = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(setupac.smtpssl_chkbtn)); + setupac.serv_port = setupac.serv_ssl ? POP3S_PORT : POP3_PORT; + setupac.smtp_port = setupac.smtp_ssl ? SMTPS_PORT : SMTP_PORT; +#else /* !USE_SSL */ + setupac.serv_port = POP3_PORT; + setupac.smtp_port = SMTP_PORT; +#endif /* USE_SSL */ + setupac.smtp_auth = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(setupac.smtpauth_chkbtn)); + gtk_label_set_text(GTK_LABEL(setupac.serv_label_name2), _("POP3 server:")); + break; + case SETUP_TYPE_IMAP: +#if USE_SSL + setupac.serv_ssl = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(setupac.servssl_chkbtn)); + setupac.smtp_ssl = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(setupac.smtpssl_chkbtn)); + setupac.serv_port = setupac.serv_ssl ? IMAPS_PORT : IMAP_PORT; + setupac.smtp_port = setupac.smtp_ssl ? SMTPS_PORT : SMTP_PORT; +#else /* !USE_SSL */ + setupac.serv_port = IMAP_PORT; + setupac.smtp_port = SMTP_PORT; +#endif /* USE_SSL */ + setupac.smtp_auth = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(setupac.smtpauth_chkbtn)); + gtk_label_set_text(GTK_LABEL(setupac.serv_label_name2), _("IMAP4 server:")); + break; +#if USE_SSL + case SETUP_TYPE_POP3G: + setupac.serv_ssl = TRUE; + setupac.smtp_ssl = TRUE; + setupac.smtp_auth = TRUE; + setupac.serv_port = POP3S_PORT; + setupac.smtp_port = SMTPS_PORT; + gtk_label_set_text(GTK_LABEL(setupac.serv_label_name2), _("POP3 server:")); + break; + case SETUP_TYPE_IMAPG: + setupac.serv_ssl = TRUE; + setupac.smtp_ssl = TRUE; + setupac.smtp_auth = TRUE; + setupac.serv_port = IMAPS_PORT; + setupac.smtp_port = SMTPS_PORT; + gtk_label_set_text(GTK_LABEL(setupac.serv_label_name2), _("IMAP4 server:")); + break; +#endif /* USE_SSL */ + } + + GET_STR(name, name_entry); + GET_STR(addr, addr_entry); + GET_STR(userid, id_entry); + GET_STR(serv, serv_entry); + GET_STR(smtpserv, smtp_entry); + gtk_label_set_text(GTK_LABEL(setupac.name_label), setupac.name); + gtk_label_set_text(GTK_LABEL(setupac.addr_label), setupac.addr); + gtk_label_set_text(GTK_LABEL(setupac.id_label), setupac.userid); +#if USE_SSL + if (setupac.serv_ssl) + g_snprintf(buf, sizeof(buf), "%s:%u (SSL)", + setupac.serv, setupac.serv_port); + else +#endif + g_snprintf(buf, sizeof(buf), "%s:%u", + setupac.serv, setupac.serv_port); + gtk_label_set_text(GTK_LABEL(setupac.serv_label), buf); +#if USE_SSL + if (setupac.smtp_ssl) + g_snprintf(buf, sizeof(buf), "%s:%u (SSL)", + setupac.smtpserv, setupac.smtp_port); + else +#endif + g_snprintf(buf, sizeof(buf), "%s:%u", + setupac.smtpserv, setupac.smtp_port); + gtk_label_set_text(GTK_LABEL(setupac.smtp_label), buf); + } + + gtk_dialog_set_response_sensitive(GTK_DIALOG(setupac.dialog), + GTK_RESPONSE_REJECT, prev_enable); + gtk_dialog_set_response_sensitive(GTK_DIALOG(setupac.dialog), + GTK_RESPONSE_ACCEPT, next_enable); + + if (page == SETUP_PAGE_ADDRESS || page == SETUP_PAGE_ACCOUNT) + entry_changed(GTK_EDITABLE(setupac.addr_entry), NULL); +} + +PrefsAccount *setup_account(void) +{ + MainWindow *mainwin; + GtkWidget *dialog; + GtkWidget *hbox; + GtkWidget *image; + GtkWidget *vbox; + GtkWidget *vbox2; + GtkWidget *label; + GtkWidget *table; + GtkWidget *chkbtn; + gint result; + PrefsAccount *ac; + + mainwin = main_window_get(); + manage_window_focus_in(mainwin->window, NULL, NULL); + + dialog = gtk_dialog_new_with_buttons(_("New account setup"), NULL, GTK_DIALOG_MODAL, NULL); + setupac.dialog = dialog; + + setupac.prev_btn = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_GO_BACK, GTK_RESPONSE_REJECT); + setupac.next_btn = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_GO_FORWARD, GTK_RESPONSE_ACCEPT); + setupac.cancel_btn = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + + gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, TRUE, FALSE); + gtk_widget_set_size_request(dialog, 540 * gtkut_get_dpi_multiplier(), -1); + gtk_window_set_position(GTK_WINDOW(dialog), + GTK_WIN_POS_CENTER_ON_PARENT); + //gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + manage_window_set_transient(GTK_WINDOW(dialog)); + gtk_dialog_set_has_separator(GTK_DIALOG(dialog), TRUE); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); + gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), + GTK_RESPONSE_REJECT, FALSE); + MANAGE_WINDOW_SIGNALS_CONNECT(dialog); + gtk_widget_realize(dialog); + + g_signal_connect(dialog, "response", + G_CALLBACK(setup_account_response_cb), NULL); + + hbox = gtk_hbox_new(FALSE, 12); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 12); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), + hbox, FALSE, FALSE, 0); + + image = stock_pixbuf_widget(dialog, STOCK_PIXMAP_SYLPHEED); + + gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.0); + gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); + + vbox = gtk_vbox_new(FALSE, 12); + gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0); + + label = gtk_label_new(_("New account setup")); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + + if (!font_desc) { + gint size; + + size = pango_font_description_get_size + (label->style->font_desc); + font_desc = pango_font_description_new(); + pango_font_description_set_weight + (font_desc, PANGO_WEIGHT_BOLD); + pango_font_description_set_size + (font_desc, size * PANGO_SCALE_LARGE); + } + if (font_desc) + gtk_widget_modify_font(label, font_desc); + + label = gtk_label_new(_("This dialog will make initial setup of new mail account.")); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + GTK_WIDGET_UNSET_FLAGS(label, GTK_CAN_FOCUS); + MODIFY_LABEL_STYLE(); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 8); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox, + TRUE, TRUE, 0); + + setupac.notebook = gtk_notebook_new(); + gtk_notebook_set_show_border(GTK_NOTEBOOK(setupac.notebook), FALSE); + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(setupac.notebook), FALSE); + gtk_box_pack_start(GTK_BOX(vbox), setupac.notebook, TRUE, TRUE, 0); + + /* Page 1 */ + vbox = gtk_vbox_new(FALSE, 12); + gtk_notebook_append_page(GTK_NOTEBOOK(setupac.notebook), vbox, NULL); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 12); + + label = gtk_label_new(_("Select account type:")); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 8); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + + setupac.pop3_radio = gtk_radio_button_new_with_label(NULL, "POP3"); + gtk_box_pack_start(GTK_BOX(vbox), setupac.pop3_radio, FALSE, FALSE, 0); + setupac.imap_radio = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON(setupac.pop3_radio), "IMAP4"); + gtk_box_pack_start(GTK_BOX(vbox), setupac.imap_radio, FALSE, FALSE, 0); +#if USE_SSL + setupac.pop3g_radio = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON(setupac.pop3_radio), "POP3 (Gmail)"); + gtk_box_pack_start(GTK_BOX(vbox), setupac.pop3g_radio, FALSE, FALSE, 0); + setupac.imapg_radio = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON(setupac.pop3_radio), "IMAP4 (Gmail)"); + gtk_box_pack_start(GTK_BOX(vbox), setupac.imapg_radio, FALSE, FALSE, 0); +#endif + + /* Page 2 */ + vbox = gtk_vbox_new(FALSE, 12); + gtk_notebook_append_page(GTK_NOTEBOOK(setupac.notebook), vbox, NULL); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 12); + + label = gtk_label_new(_("Input your name and mail address:")); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 8); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + + table = gtk_table_new(4, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + + label = gtk_label_new(_("Display name:")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + label = gtk_label_new(_("E-mail address:")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + setupac.name_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), setupac.name_entry, 1, 2, 0, 1, + GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0); + g_signal_connect(setupac.name_entry, "changed", + G_CALLBACK(entry_changed), NULL); + setupac.addr_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), setupac.addr_entry, 1, 2, 3, 4, + GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0); + g_signal_connect(setupac.addr_entry, "changed", + G_CALLBACK(entry_changed), NULL); + + label = gtk_label_new(_("This name will be seen at the side of recipients (e.g. John Doe)")); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, 1, 2, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + /* Page 3 */ + vbox = gtk_vbox_new(FALSE, 12); + gtk_notebook_append_page(GTK_NOTEBOOK(setupac.notebook), vbox, NULL); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 12); + gtk_widget_show_all(dialog); + + label = gtk_label_new(_("Input user ID and mail server:")); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 8); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + + table = gtk_table_new(6, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + + label = gtk_label_new(_("User ID:")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + label = gtk_label_new(_("POP3 server:")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + setupac.serv_label_name1 = label; + label = gtk_label_new(_("SMTP server:")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 4, 5, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + setupac.id_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), setupac.id_entry, 1, 2, 0, 1, + GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0); + g_signal_connect(setupac.id_entry, "changed", + G_CALLBACK(entry_changed), NULL); + setupac.serv_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), setupac.serv_entry, 1, 2, 1, 2, + GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0); + g_signal_connect(setupac.serv_entry, "changed", + G_CALLBACK(entry_changed), NULL); + setupac.smtp_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), setupac.smtp_entry, 1, 2, 4, 5, + GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0); + g_signal_connect(setupac.smtp_entry, "changed", + G_CALLBACK(entry_changed), NULL); + +#if USE_SSL + hbox = gtk_hbox_new(FALSE, 12); + gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 2, 3, + GTK_FILL, GTK_FILL, 0, 0); + chkbtn = gtk_check_button_new_with_mnemonic(_("Use SSL")); + gtk_box_pack_start(GTK_BOX(hbox), chkbtn, FALSE, FALSE, 0); + setupac.servssl_chkbtn = chkbtn; +#endif + + hbox = gtk_hbox_new(FALSE, 12); + gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 5, 6, + GTK_FILL, GTK_FILL, 0, 0); + chkbtn = gtk_check_button_new_with_mnemonic(_("Use SMTP authentication")); + gtk_box_pack_start(GTK_BOX(hbox), chkbtn, FALSE, FALSE, 0); + setupac.smtpauth_chkbtn = chkbtn; +#if USE_SSL + chkbtn = gtk_check_button_new_with_mnemonic(_("Use SSL")); + gtk_box_pack_start(GTK_BOX(hbox), chkbtn, FALSE, FALSE, 0); + setupac.smtpssl_chkbtn = chkbtn; +#endif + + /* Page 4 */ + vbox = gtk_vbox_new(FALSE, 12); + gtk_notebook_append_page(GTK_NOTEBOOK(setupac.notebook), vbox, NULL); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 12); + + vbox2 = gtk_vbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 8); + + label = gtk_label_new(_("Your new mail account has been set up with the following settings.")); + gtk_box_pack_start(GTK_BOX(vbox2), label, FALSE, FALSE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + label = gtk_label_new(_("If you want to modify the settings, select\n" + "'Configuration - Preferences for current account' or\n" + "'Configuration - Edit accounts' in the main menu.")); + gtk_box_pack_start(GTK_BOX(vbox2), label, FALSE, FALSE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + //gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + + table = gtk_table_new(5, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + gtk_table_set_row_spacings(GTK_TABLE(table), 8); + gtk_table_set_col_spacings(GTK_TABLE(table), 8); + + label = gtk_label_new(_("Display name:")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + label = gtk_label_new(_("E-mail address:")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + label = gtk_label_new(_("User ID:")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + setupac.serv_label_name2 = gtk_label_new(_("POP3 server:")); + gtk_table_attach(GTK_TABLE(table), setupac.serv_label_name2, 0, 1, 3, 4, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(setupac.serv_label_name2), 1, 0.5); + label = gtk_label_new(_("SMTP server:")); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 4, 5, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + setupac.name_label = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), setupac.name_label, 1, 2, 0, 1, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(setupac.name_label), 0, 0.5); + setupac.addr_label = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), setupac.addr_label, 1, 2, 1, 2, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(setupac.addr_label), 0, 0.5); + setupac.id_label = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), setupac.id_label, 1, 2, 2, 3, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(setupac.id_label), 0, 0.5); + setupac.serv_label = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), setupac.serv_label, 1, 2, 3, 4, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(setupac.serv_label), 0, 0.5); + setupac.smtp_label = gtk_label_new(""); + gtk_table_attach(GTK_TABLE(table), setupac.smtp_label, 1, 2, 4, 5, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment(GTK_MISC(setupac.smtp_label), 0, 0.5); + + + gtk_widget_show_all(dialog); + + while (!setupac.finished) + result = gtk_dialog_run(GTK_DIALOG(dialog)); + + gtk_widget_destroy(dialog); + + if (setupac.cancelled) { + memset(&setupac, 0, sizeof(setupac)); + return NULL; + } + + /* Create account */ + ac = prefs_account_new(); + + if (!cur_account) { + account_set_as_default(ac); + cur_account = ac; + } + g_free(ac->account_name); + ac->account_name = g_strdup(setupac.addr); + g_free(ac->name); + ac->name = g_strdup(setupac.name); + g_free(ac->address); + ac->address = g_strdup(setupac.addr); + g_free(ac->recv_server); + ac->recv_server = g_strdup(setupac.serv); + g_free(ac->smtp_server); + ac->smtp_server = g_strdup(setupac.smtpserv); + g_free(ac->userid); + ac->userid = g_strdup(setupac.userid); +#if USE_SSL + if (setupac.smtp_ssl) + ac->ssl_smtp = SSL_TUNNEL; +#endif + ac->smtpport = setupac.smtp_port; + ac->use_smtp_auth = setupac.smtp_auth; + + switch (setupac.type) { + case SETUP_TYPE_POP3: + ac->protocol = A_POP3; +#if USE_SSL + if (setupac.serv_ssl) + ac->ssl_pop = SSL_TUNNEL; +#endif + ac->popport = setupac.serv_port; + break; + case SETUP_TYPE_IMAP: + ac->protocol = A_IMAP4; +#if USE_SSL + if (setupac.serv_ssl) + ac->ssl_imap = SSL_TUNNEL; +#endif + ac->imapport = setupac.serv_port; + break; +#if USE_SSL + case SETUP_TYPE_POP3G: + ac->protocol = A_POP3; + ac->ssl_pop = SSL_TUNNEL; + ac->popport = setupac.serv_port; + break; + case SETUP_TYPE_IMAPG: + ac->protocol = A_IMAP4; + ac->ssl_imap = SSL_TUNNEL; + ac->imapport = setupac.serv_port; + break; +#endif /* USE_SSL */ + } + + g_free(ac->sig_text); + ac->sig_text = g_strdup_printf("%s <%s>\\n", ac->name, ac->address); + + account_update_lock(); + account_append(ac); + account_write_config_all(); + account_update_unlock(); + account_updated(); + + g_free(setupac.name); + g_free(setupac.addr); + g_free(setupac.serv); + g_free(setupac.smtpserv); + g_free(setupac.userid); + memset(&setupac, 0, sizeof(setupac)); + + return ac; +} + +#ifdef G_OS_WIN32 +struct Identity +{ + gchar *name; + gchar *path; +}; + +static GSList *get_dbx_source(void) +{ + HKEY reg_key, hkey, hkey2; + wchar_t name[1024]; + DWORD size, type, i; + LPWSTR username, store, path; + GSList *src_list = NULL; + + if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Identities", 0, KEY_READ, ®_key) != ERROR_SUCCESS) + return NULL; + + for (i = 0; ; i++) { + size = sizeof(name); + if (RegEnumKeyExW(reg_key, i, name, &size, 0, 0, 0, 0) != ERROR_SUCCESS) + break; + if (RegOpenKeyExW(reg_key, name, 0, KEY_READ, &hkey) != ERROR_SUCCESS) + continue; + + do { + if (RegQueryValueExW(hkey, L"UserName", 0, &type, 0, &size) != ERROR_SUCCESS) + break; + if (type != REG_SZ) + break; + ++size; + username = g_malloc(size); + if (RegQueryValueExW(hkey, L"UserName", 0, &type, (LPBYTE)username, &size) != ERROR_SUCCESS) { + g_free(username); + break; + } + + if (RegOpenKeyExW(hkey, L"Software\\Microsoft\\Outlook Express\\5.0", 0, KEY_READ, &hkey2) != ERROR_SUCCESS) { + g_free(username); + break; + } + + do { + if (RegQueryValueExW(hkey2, L"Store Root", 0, &type, 0, &size) != ERROR_SUCCESS) + break; + if (type != REG_SZ && type != REG_EXPAND_SZ) + break; + + ++size; + store = g_malloc(size); + if (RegQueryValueExW(hkey2, L"Store Root", 0, &type, (LPBYTE)store, &size) != ERROR_SUCCESS) { + g_free(store); + break; + } + + if (type == REG_EXPAND_SZ) { + size = MAX_PATH * 2; + path = g_malloc(size); + if (!ExpandEnvironmentStringsW(store, path, size)) { + g_free(path); + path = NULL; + } + } else { + path = store; + store = NULL; + } + + if (path) { + struct Identity *ident; + + ident = g_new(struct Identity, 1); + ident->name = g_utf16_to_utf8(username, -1, NULL, NULL, NULL); + ident->path = g_utf16_to_utf8(path, -1, NULL, NULL, NULL); + src_list = g_slist_append(src_list, ident); + debug_print("get_dbx_source: username = %s , path = %s\n", ident->name, ident->path); + g_free(path); + } + g_free(store); + } while (0); + g_free(username); + RegCloseKey(hkey2); + } while (0); + RegCloseKey(hkey); + } + + RegCloseKey(reg_key); + + return src_list; +} +#endif /* G_OS_WIN32 */ + +gint setup_import_data(void) +{ +#ifdef G_OS_WIN32 + AlertValue val; + GSList *src_list, *cur; + struct Identity *ident; + gchar *src; + Folder *folder; + FolderItem *parent, *dest; + gint ok = 0; + + debug_print("setup_import_data\n"); + + src_list = get_dbx_source(); + if (!src_list) + return 0; + + val = alertpanel(_("Importing mail data"), _("The mail store of Outlook Express was found. Do you want to import the mail data of Outlook Express?\n\n(The folder structure will not be reproduced)"), GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if (val != G_ALERTDEFAULT) { + goto finish; + } + + folder = folder_get_default_folder(); + if (!folder) { + g_warning("Cannot get default folder"); + ok = -1; + goto finish; + } + parent = FOLDER_ITEM(folder->node->data); + if (!parent) { + g_warning("Cannot get root folder"); + ok = -1; + goto finish; + } + dest = folder_find_child_item_by_name(parent, _("Imported")); + if (!dest) { + dest = folder->klass->create_folder(folder, parent, _("Imported")); + } + if (!dest) { + g_warning("Cannot create a folder"); + ok = -1; + goto finish; + } + parent = dest; + folderview_append_item(folderview_get(), NULL, parent, TRUE); + folder_write_list(); + + for (cur = src_list; cur != NULL; cur = cur->next) { + ident = (struct Identity *)cur->data; + dest = folder_find_child_item_by_name(parent, ident->name); + if (!dest) + dest = folder->klass->create_folder(folder, parent, ident->name); + if (!dest) + continue; + folderview_append_item(folderview_get(), NULL, dest, TRUE); + folder_write_list(); + ok = import_dbx_folders(dest, ident->path); + if (ok < 0) + break; + } + +finish: + for (cur = src_list; cur != NULL; cur = cur->next) { + ident = (struct Identity *)cur->data; + g_free(ident->name); + g_free(ident->path); + g_free(ident); + } + g_slist_free(src_list); + + if (ok == -1) + alertpanel_error(_("Failed to import the mail data.")); + + return ok; +#else /* G_OS_WIN32 */ + return 0; +#endif /* G_OS_WIN32 */ +} + +#ifdef G_OS_WIN32 +static gchar *get_wab_file(void) +{ + HKEY reg_key; + DWORD type, nbytes; + wchar_t *tmp; + gchar *result = NULL; + + if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\WAB\\WAB4\\Wab File Name", 0, KEY_QUERY_VALUE, ®_key) != ERROR_SUCCESS) + return NULL; + if (RegQueryValueExW(reg_key, L"", 0, &type, NULL, &nbytes) == ERROR_SUCCESS && type == REG_SZ) { + tmp = g_new(wchar_t, (nbytes + 1) / 2 + 1); + RegQueryValueExW(reg_key, L"", 0, &type, (LPBYTE)tmp, &nbytes); + tmp[nbytes / 2] = '\0'; + result = g_utf16_to_utf8(tmp, -1, NULL, NULL, NULL); + g_free(tmp); + } + RegCloseKey(reg_key); + + return result; +} +#endif /* G_OS_WIN32 */ + +gint setup_import_addressbook(void) +{ +#ifdef G_OS_WIN32 + AlertValue val; + gchar *appdata; + gchar *wabfile, *ldiffile; + gchar cmdline[MAX_PATH + 1]; + gchar *cpcmdline; + gint ret = 0; + + debug_print("setup_import_addressbook\n"); + + wabfile = get_wab_file(); + if (!wabfile || !is_file_exist(wabfile)) { + g_free(wabfile); + return 0; + } + + val = alertpanel(_("Importing address book"), _("The Windows address book was found. Do you want to import the address book?"), GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if (val != G_ALERTDEFAULT) { + g_free(wabfile); + return 0; + } + + ldiffile = g_strconcat(get_tmp_dir(), G_DIR_SEPARATOR_S, "impwab.ldif", + NULL); + g_snprintf(cmdline, sizeof(cmdline), "wabread \"%s\" > \"%s\"", + wabfile, ldiffile); + debug_print("setup_import_addressbook: cmdline: %s\n", cmdline); + cpcmdline = g_win32_locale_filename_from_utf8(cmdline); + if (!cpcmdline) { + g_warning("g_win32_locale_filename_from_utf8() failed"); + g_free(ldiffile); + g_free(wabfile); + return -1; + } + ret = system(cpcmdline); + g_free(cpcmdline); + g_free(wabfile); + if (ret != 0 || !is_file_exist(ldiffile)) { + g_warning("system() failed"); + ret = -1; + goto finish; + } + + if (!addressbook_import_ldif_file(ldiffile, _("Imported"))) { + g_warning("setup_import_addressbook: import failed"); + ret = -1; + goto finish; + } + +finish: + g_unlink(ldiffile); + g_free(ldiffile); + if (ret < 0) + alertpanel_error(_("Failed to import the address book.")); + + return ret; +#else /* G_OS_WIN32 */ + return 0; +#endif /* G_OS_WIN32 */ +} diff --git a/src/setup.h b/src/setup.h new file mode 100644 index 0000000..48c1cec --- /dev/null +++ b/src/setup.h @@ -0,0 +1,32 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2010 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __SETUP_H__ +#define __SETUP_H__ + +#include + +#include "prefs_account.h" + +void setup_mailbox(void); +PrefsAccount *setup_account(void); +gint setup_import_data(void); +gint setup_import_addressbook(void); + +#endif /* __SETUP_H__ */ diff --git a/src/sigstatus.c b/src/sigstatus.c new file mode 100644 index 0000000..4f777e7 --- /dev/null +++ b/src/sigstatus.c @@ -0,0 +1,280 @@ +/* sigstatus.h - GTK+ based signature status display + * Copyright (C) 2001 Werner Koch (dd9jn) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if USE_GPGME + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtkutils.h" +#include "utils.h" +#include "sigstatus.h" + +/* remove the window after 30 seconds to avoid cluttering the deskop + * with too many of them */ +#define MY_TIMEOUT (30*1000) + +struct gpgmegtk_sig_status_s { + GtkWidget *mainwindow; + GtkWidget *label; + gint running; + gint destroy_pending; + guint timeout_id; + gint timeout_id_valid; +}; + + +static void do_destroy(GpgmegtkSigStatus hd) +{ + if (!hd->running) { + if (hd->timeout_id_valid) { + g_source_remove(hd->timeout_id); + hd->timeout_id_valid = 0; + } + if (hd->mainwindow) { + gtk_widget_destroy ( hd->mainwindow ); + hd->mainwindow = NULL; + } + if (hd->destroy_pending) + g_free(hd); + } +} + +static void okay_cb(GtkWidget *widget, gpointer data) +{ + GpgmegtkSigStatus hd = data; + + hd->running = 0; + do_destroy(hd); +} + +static gboolean delete_event(GtkWidget *widget, GdkEventAny *event, gpointer data) +{ + GpgmegtkSigStatus hd = data; + + hd->running = 0; + do_destroy(hd); + + return TRUE; +} + +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + GpgmegtkSigStatus hd = data; + + if (event && event->keyval == GDK_Escape) { + hd->running = 0; + do_destroy(hd); + return TRUE; + } + return FALSE; +} + +GpgmegtkSigStatus gpgmegtk_sig_status_create(void) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *okay_btn; + GtkWidget *okay_area; + GpgmegtkSigStatus hd; + + hd = g_malloc0(sizeof *hd); + hd->running = 1; + + window = gtk_dialog_new(); + hd->mainwindow = window; + gtk_widget_set_size_request(window, 400, -1); + gtk_window_set_title(GTK_WINDOW(window), _("Signature check result")); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_dialog_set_has_separator(GTK_DIALOG(window), FALSE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(delete_event), hd); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(key_pressed), hd); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 12); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), + vbox, TRUE, TRUE, 0); + gtk_widget_show(vbox); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); + gtk_widget_show(hbox); + + label = gtk_label_new(_("Checking signature")); + hd->label = label; + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 8); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_widget_show(label); + + gtkut_stock_button_set_create(&okay_area, &okay_btn, GTK_STOCK_OK, + NULL, NULL, NULL, NULL); + gtk_box_pack_end(GTK_BOX(GTK_DIALOG(window)->action_area), + okay_area, FALSE, FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(okay_area), 5); + gtk_widget_grab_default(okay_btn); + gtk_widget_grab_focus(okay_btn); + g_signal_connect(G_OBJECT(okay_btn), "clicked", + G_CALLBACK(okay_cb), hd); + + gtk_widget_show_all(window); + + while (gtk_events_pending()) + gtk_main_iteration(); + + return hd; +} + +static gint timeout_cb(gpointer data) +{ + GpgmegtkSigStatus hd = data; + + gdk_threads_enter(); + + hd->running = 0; + hd->timeout_id_valid = 0; + do_destroy(hd); + + gdk_threads_leave(); + + return FALSE; +} + +void gpgmegtk_sig_status_destroy(GpgmegtkSigStatus hd) +{ + if (hd) { + hd->destroy_pending = 1; + if (hd->running && !hd->timeout_id_valid) { + hd->timeout_id = g_timeout_add(MY_TIMEOUT, + timeout_cb, hd); + hd->timeout_id_valid = 1; + } + do_destroy(hd); + } +} + +void gpgmegtk_sig_status_update(GpgmegtkSigStatus hd, gpgme_ctx_t ctx) +{ + gpgme_verify_result_t result; + gpgme_signature_t sig; + gchar *text = NULL; + + if (!hd || !hd->running || !ctx) + return; + result = gpgme_op_verify_result(ctx); + if (!result) + return; + + sig = result->signatures; + while (sig) { + gchar *tmp; + const gchar *userid; + gpgme_key_t key = NULL; + + if (!gpgme_get_key(ctx, sig->fpr, &key, 0)) { + userid = key->uids->uid; + } else { + userid = "[?]"; + } + tmp = g_strdup_printf + (_("%s%s%s from \"%s\""), + text ? text : "", text ? "\n" : "", + gpgmegtk_sig_status_to_string(sig, FALSE), userid); + g_free (text); + text = tmp; + gpgme_key_unref (key); + sig = sig->next; + } + gtk_label_set_text(GTK_LABEL(hd->label), text); + g_free(text); + + while (gtk_events_pending()) + gtk_main_iteration(); +} + +const gchar *gpgmegtk_sig_status_to_string(gpgme_signature_t signature, + gboolean use_name) +{ + const gchar *result = "?"; + + g_return_val_if_fail(signature != NULL, result); + + switch (gpg_err_code(signature->status)) { + case GPG_ERR_NO_DATA: + result = _("No signature found"); + break; + case GPG_ERR_NO_ERROR: + switch (signature->validity) { + case GPGME_VALIDITY_ULTIMATE: + case GPGME_VALIDITY_FULL: + case GPGME_VALIDITY_MARGINAL: + result = use_name ? _("Good signature from \"%s\"") : + _("Good signature"); + break; + default: + result = use_name ? + _("Valid signature but the key for \"%s\" is not trusted") : + _("Valid signature (untrusted key)"); + break; + } + break; + case GPG_ERR_SIG_EXPIRED: + result = use_name ? _("Signature valid but expired for \"%s\"") : + _("Signature valid but expired"); + break; + case GPG_ERR_KEY_EXPIRED: + result = use_name ? _("Signature valid but the signing key for \"%s\" has expired") : + _("Signature valid but the signing key has expired"); + break; + case GPG_ERR_CERT_REVOKED: + result = use_name ? _("Signature valid but the signing key for \"%s\" has been revoked") : + _("Signature valid but the signing key has been revoked"); + break; + case GPG_ERR_BAD_SIGNATURE: + result = use_name ? _("BAD signature from \"%s\"") : + _("BAD signature"); + break; + case GPG_ERR_NO_PUBKEY: + result = _("No public key to verify the signature"); + break; + default: + result = _("Error verifying the signature"); + break; + } + + return result; +} + +#endif /* USE_GPGME */ diff --git a/src/sigstatus.h b/src/sigstatus.h new file mode 100644 index 0000000..36f52e2 --- /dev/null +++ b/src/sigstatus.h @@ -0,0 +1,34 @@ +/* sigstatus.h - GTK+ based signature status display + * Copyright (C) 2001 Werner Koch (dd9jn) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef GPGMEGTK_SIGSTATUS_H +#define GPGMEGTK_SIGSTATUS_H + +#include + +struct gpgmegtk_sig_status_s; +typedef struct gpgmegtk_sig_status_s *GpgmegtkSigStatus; + +GpgmegtkSigStatus gpgmegtk_sig_status_create(void); +void gpgmegtk_sig_status_destroy(GpgmegtkSigStatus hd); +void gpgmegtk_sig_status_update(GpgmegtkSigStatus hd, gpgme_ctx_t ctx); + +const gchar *gpgmegtk_sig_status_to_string(gpgme_signature_t signature, + gboolean use_name); + +#endif /* GPGMEGTK_SIGSTATUS_H */ diff --git a/src/simple-gettext.c b/src/simple-gettext.c new file mode 100644 index 0000000..be6292b --- /dev/null +++ b/src/simple-gettext.c @@ -0,0 +1,386 @@ +/* simple-gettext.c - a simplified version of gettext. + * Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +/* This is a simplified version of gettext written by Ulrich Drepper. + * It is used for the Win32 version of GnuPG becuase all the overhead + * of gettext is not needed and we have to do some special Win32 stuff. + * I decided that this is far easier than to tweak gettext for the special + * cases (I tried it but it is a lot of code). wk 15.09.99 + */ + +#include +#ifdef USE_SIMPLE_GETTEXT +#ifndef __MINGW32__ + #error This file can only be used with MinGW32 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include "w32reg.h" + +typedef unsigned int u32; /* That is fine with MingW32 */ + +typedef unsigned long ulong; + +/* The magic number of the GNU message catalog format. */ +#define MAGIC 0x950412de +#define MAGIC_SWAPPED 0xde120495 + +/* Revision number of the currently used .mo (binary) file format. */ +#define MO_REVISION_NUMBER 0 + + +/* Header for binary .mo file format. */ +struct mo_file_header +{ + /* The magic number. */ + u32 magic; + /* The revision number of the file format. */ + u32 revision; + /* The number of strings pairs. */ + u32 nstrings; + /* Offset of table with start offsets of original strings. */ + u32 orig_tab_offset; + /* Offset of table with start offsets of translation strings. */ + u32 trans_tab_offset; + /* Size of hashing table. */ + u32 hash_tab_size; + /* Offset of first hashing entry. */ + u32 hash_tab_offset; +}; + +struct string_desc +{ + /* Length of addressed string. */ + u32 length; + /* Offset of string in file. */ + u32 offset; +}; + + + +struct loaded_domain +{ + char *data; + int must_swap; + u32 nstrings; + char *mapped; + struct string_desc *orig_tab; + struct string_desc *trans_tab; + u32 hash_size; + u32 *hash_tab; +}; + + +static struct loaded_domain *the_domain; + +static __inline__ u32 +do_swap_u32( u32 i ) +{ + return (i << 24) | ((i & 0xff00) << 8) | ((i >> 8) & 0xff00) | (i >> 24); +} + +#define SWAPIT(flag, data) ((flag) ? do_swap_u32(data) : (data) ) + + +/* We assume to have `unsigned long int' value with at least 32 bits. */ +#define HASHWORDBITS 32 + +/* The so called `hashpjw' function by P.J. Weinberger + [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools, + 1986, 1987 Bell Telephone Laboratories, Inc.] */ + +static __inline__ ulong +hash_string( const char *str_param ) +{ + unsigned long int hval, g; + const char *str = str_param; + + hval = 0; + while (*str != '\0') + { + hval <<= 4; + hval += (unsigned long int) *str++; + g = hval & ((unsigned long int) 0xf << (HASHWORDBITS - 4)); + if (g != 0) + { + hval ^= g >> (HASHWORDBITS - 8); + hval ^= g; + } + } + return hval; +} + + +static struct loaded_domain * +load_domain( const char *filename ) +{ + FILE *fp; + size_t size; + struct stat st; + struct mo_file_header *data = NULL; + struct loaded_domain *domain = NULL; + size_t to_read; + char *read_ptr; + + fp = fopen( filename, "rb" ); + if( !fp ) + return NULL; /* can't open the file */ + /* we must know about the size of the file */ + if( fstat( fileno(fp ), &st ) + || (size = (size_t)st.st_size) != st.st_size + || size < sizeof (struct mo_file_header) ) { + fclose( fp ); + return NULL; + } + + data = malloc( size ); + if( !data ) { + fclose( fp ); + return NULL; /* out of memory */ + } + + to_read = size; + read_ptr = (char *) data; + do { + long int nb = fread( read_ptr, 1, to_read, fp ); + if( nb < to_read ) { + fclose (fp); + free(data); + return NULL; /* read error */ + } + read_ptr += nb; + to_read -= nb; + } while( to_read > 0 ); + fclose (fp); + + /* Using the magic number we can test whether it really is a message + * catalog file. */ + if( data->magic != MAGIC && data->magic != MAGIC_SWAPPED ) { + /* The magic number is wrong: not a message catalog file. */ + free( data ); + return NULL; + } + + domain = calloc( 1, sizeof *domain ); + if( !domain ) { + free( data ); + return NULL; + } + domain->data = (char *) data; + domain->must_swap = data->magic != MAGIC; + + /* Fill in the information about the available tables. */ + switch( SWAPIT(domain->must_swap, data->revision) ) { + case 0: + domain->nstrings = SWAPIT(domain->must_swap, data->nstrings); + domain->orig_tab = (struct string_desc *) + ((char *) data + SWAPIT(domain->must_swap, data->orig_tab_offset)); + domain->trans_tab = (struct string_desc *) + ((char *) data + SWAPIT(domain->must_swap, data->trans_tab_offset)); + domain->hash_size = SWAPIT(domain->must_swap, data->hash_tab_size); + domain->hash_tab = (u32 *) + ((char *) data + SWAPIT(domain->must_swap, data->hash_tab_offset)); + break; + + default: /* This is an invalid revision. */ + free( data ); + free( domain ); + return NULL; + } + + /* allocate an array to keep track of code page mappings */ + domain->mapped = calloc( 1, domain->nstrings ); + if( !domain->mapped ) { + free( data ); + free( domain ); + return NULL; + } + + return domain; +} + + +/**************** + * Set the file used for translations. Pass a NULL to disable + * translation. A new filename may be set at anytime. + * WARNING: After changing the filename you shoudl not access any data + * retrieved by gettext(). + */ +int +set_gettext_file( const char *filename ) +{ + struct loaded_domain *domain = NULL; + + if( filename && *filename ) { + if( filename[0] == '/' + #ifdef HAVE_DRIVE_LETTERS + || ( isalpha(filename[0]) + && filename[1] == ':' + && (filename[2] == '/' || filename[2] == '\\') ) + #endif + ) { + /* absolute path - use it as is */ + domain = load_domain( filename ); + } + else { /* relative path - append ".mo" and get dir from the environment */ + char *buf = NULL; + char *dir; + + dir = read_w32_registry_string( NULL, + "Control Panel\\Mingw32\\NLS", + "MODir" ); + if( dir && (buf=malloc(strlen(dir)+strlen(filename)+1+3+1)) ) { + strcpy(stpcpy(stpcpy(stpcpy( buf, dir),"\\"), filename),".mo"); + domain = load_domain( buf ); + free(buf); + } + free(dir); + } + if( !domain ) { + return -1; } + } + if( the_domain ) { + free( the_domain->data ); + free( the_domain->mapped ); + free( the_domain ); + the_domain = NULL; + } + the_domain = domain; + return NULL; +} + + +static const char* +get_string( struct loaded_domain *domain, u32 idx ) +{ + char *p = domain->data + SWAPIT(domain->must_swap, + domain->trans_tab[idx].offset); + + /* status of domain->mapped[idx] is ignored. + * not sure about the consequences. + * perhaps mapped can entirely be removed? + */ + + /* we assume, strings are already correctly + * encoded. + */ + + return (const char*)p; +} + + + +const char * +gettext( const char *msgid ) +{ + struct loaded_domain *domain; + size_t act = 0; + size_t top, bottom; + + if( !(domain = the_domain) ) { + goto not_found; + } + + /* Locate the MSGID and its translation. */ + if( domain->hash_size > 2 && domain->hash_tab ) { + /* Use the hashing table. */ + u32 len = strlen (msgid); + u32 hash_val = hash_string (msgid); + u32 idx = hash_val % domain->hash_size; + u32 incr = 1 + (hash_val % (domain->hash_size - 2)); + u32 nstr = SWAPIT (domain->must_swap, domain->hash_tab[idx]); + + if ( !nstr ) /* Hash table entry is empty. */ + goto not_found; + + if( SWAPIT(domain->must_swap, + domain->orig_tab[nstr - 1].length) == len + && !strcmp( msgid, + domain->data + SWAPIT(domain->must_swap, + domain->orig_tab[nstr - 1].offset)) ) + return get_string( domain, nstr - 1 ); + + for(;;) { + if (idx >= domain->hash_size - incr) + idx -= domain->hash_size - incr; + else + idx += incr; + + nstr = SWAPIT(domain->must_swap, domain->hash_tab[idx]); + if( !nstr ) + goto not_found; /* Hash table entry is empty. */ + + if ( SWAPIT(domain->must_swap, + domain->orig_tab[nstr - 1].length) == len + && !strcmp (msgid, + domain->data + SWAPIT(domain->must_swap, + domain->orig_tab[nstr - 1].offset))) + return get_string( domain, nstr-1 ); + } + /* NOTREACHED */ + } + + /* Now we try the default method: binary search in the sorted + array of messages. */ + bottom = 0; + top = domain->nstrings; + while( bottom < top ) { + int cmp_val; + + act = (bottom + top) / 2; + cmp_val = strcmp(msgid, domain->data + + SWAPIT(domain->must_swap, + domain->orig_tab[act].offset)); + if (cmp_val < 0) + top = act; + else if (cmp_val > 0) + bottom = act + 1; + else + return get_string( domain, act ); + } + + not_found: + return msgid; +} + +#if 0 + unsigned int cp1, cp2; + + cp1 = GetConsoleCP(); + cp2 = GetConsoleOutputCP(); + + log_info("InputCP=%u OutputCP=%u\n", cp1, cp2 ); + + if( !SetConsoleOutputCP( 1252 ) ) + log_info("SetConsoleOutputCP failed: %d\n", (int)GetLastError() ); + + cp1 = GetConsoleCP(); + cp2 = GetConsoleOutputCP(); + log_info("InputCP=%u OutputCP=%u after switch1\n", cp1, cp2 ); +#endif + +#endif /* USE_SIMPLE_GETTEXT */ diff --git a/src/sourcewindow.c b/src/sourcewindow.c new file mode 100644 index 0000000..b1926fe --- /dev/null +++ b/src/sourcewindow.c @@ -0,0 +1,212 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sourcewindow.h" +#include "procmsg.h" +#include "codeconv.h" +#include "utils.h" +#include "gtkutils.h" +#include "prefs_common.h" + +static void source_window_size_alloc_cb (GtkWidget *widget, + GtkAllocation *allocation); +static gint source_window_delete_cb (GtkWidget *widget, + GdkEventAny *event, + SourceWindow *sourcewin); +static gboolean key_pressed (GtkWidget *widget, + GdkEventKey *event, + SourceWindow *sourcewin); + +static void adj_value_changed (GtkAdjustment *adj, + SourceWindow *sourcewin); + +static void source_window_init() +{ +} + +SourceWindow *source_window_create(void) +{ + SourceWindow *sourcewin; + GtkWidget *window; + GtkWidget *scrolledwin; + GtkWidget *text; + static PangoFontDescription *font_desc = NULL; + + debug_print(_("Creating source window...\n")); + sourcewin = g_new0(SourceWindow, 1); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), _("Source of the message")); + gtk_window_set_wmclass(GTK_WINDOW(window), "source_window", "Sylpheed"); + gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE); + gtk_widget_set_size_request(window, prefs_common.sourcewin_width, + prefs_common.sourcewin_height); + g_signal_connect(G_OBJECT(window), "size_allocate", + G_CALLBACK(source_window_size_alloc_cb), sourcewin); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(source_window_delete_cb), sourcewin); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(key_pressed), sourcewin); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(window), scrolledwin); + gtk_widget_show(scrolledwin); + + text = gtk_text_view_new(); + gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6); + gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6); + if (!font_desc && prefs_common.textfont) + font_desc = pango_font_description_from_string + (prefs_common.textfont); + if (font_desc) + gtk_widget_modify_font(text, font_desc); + gtk_container_add(GTK_CONTAINER(scrolledwin), text); + gtk_widget_show(text); + + g_signal_connect(G_OBJECT(GTK_TEXT_VIEW(text)->vadjustment), + "value-changed", + G_CALLBACK(adj_value_changed), sourcewin); + + sourcewin->window = window; + sourcewin->scrolledwin = scrolledwin; + sourcewin->text = text; + + source_window_init(); + + return sourcewin; +} + +void source_window_show(SourceWindow *sourcewin) +{ + gtk_widget_show_all(sourcewin->window); +} + +void source_window_destroy(SourceWindow *sourcewin) +{ + gtk_widget_destroy(sourcewin->window); + g_free(sourcewin); +} + +void source_window_show_msg(SourceWindow *sourcewin, MsgInfo *msginfo) +{ + gchar *file; + gchar *title; + FILE *fp; + gchar buf[BUFFSIZE]; + GtkTextBuffer *buffer; + GtkTextIter iter; + + g_return_if_fail(msginfo != NULL); + + file = procmsg_get_message_file(msginfo); + g_return_if_fail(file != NULL); + + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + g_free(file); + return; + } + + debug_print(_("Displaying the source of %s ...\n"), file); + + title = g_strdup_printf(_("%s - Source"), file); + gtk_window_set_title(GTK_WINDOW(sourcewin->window), title); + g_free(title); + g_free(file); + + while (fgets(buf, sizeof(buf), fp) != NULL) + source_window_append(sourcewin, buf); + + fclose(fp); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(sourcewin->text)); + gtk_text_buffer_get_start_iter(buffer, &iter); + gtk_text_buffer_place_cursor(buffer, &iter); +} + +void source_window_append(SourceWindow *sourcewin, const gchar *str) +{ + GtkTextView *text = GTK_TEXT_VIEW(sourcewin->text); + GtkTextBuffer *buffer; + GtkTextIter iter; + gchar *out; + + buffer = gtk_text_view_get_buffer(text); + + out = conv_utf8todisp(str, NULL); + gtk_text_buffer_get_iter_at_offset(buffer, &iter, -1); + gtk_text_buffer_insert(buffer, &iter, out, -1); + g_free(out); +} + +static void source_window_size_alloc_cb(GtkWidget *widget, + GtkAllocation *allocation) +{ + g_return_if_fail(allocation != NULL); + + prefs_common.sourcewin_width = allocation->width; + prefs_common.sourcewin_height = allocation->height; +} + +static gint source_window_delete_cb(GtkWidget *widget, GdkEventAny *event, + SourceWindow *sourcewin) +{ + source_window_destroy(sourcewin); + return TRUE; +} + +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, + SourceWindow *sourcewin) +{ + if (event && event->keyval == GDK_Escape) { + source_window_destroy(sourcewin); + return TRUE; + } + return FALSE; +} + +static void adj_value_changed(GtkAdjustment *adj, SourceWindow *sourcewin) +{ + GtkTextBuffer *buffer; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(sourcewin->text)); + if (gtk_text_buffer_get_selection_bounds(buffer, NULL, NULL)) + return; + gtk_text_view_place_cursor_onscreen(GTK_TEXT_VIEW(sourcewin->text)); +} diff --git a/src/sourcewindow.h b/src/sourcewindow.h new file mode 100644 index 0000000..a50a132 --- /dev/null +++ b/src/sourcewindow.h @@ -0,0 +1,45 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2001 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __SOURCEWINDOW_H__ +#define __SOURCEWINDOW_H__ + +#include +#include + +#include "procmsg.h" + +typedef struct _SourceWindow SourceWindow; + +struct _SourceWindow +{ + GtkWidget *window; + GtkWidget *scrolledwin; + GtkWidget *text; +}; + +SourceWindow *source_window_create (void); +void source_window_show (SourceWindow *sourcewin); +void source_window_destroy (SourceWindow *sourcewin); +void source_window_show_msg (SourceWindow *sourcewin, + MsgInfo *msginfo); +void source_window_append (SourceWindow *sourcewin, + const gchar *str); + +#endif /* __SOURCEWINDOW_H__ */ diff --git a/src/sslmanager.c b/src/sslmanager.c new file mode 100644 index 0000000..a633710 --- /dev/null +++ b/src/sslmanager.c @@ -0,0 +1,225 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if USE_SSL + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ssl.h" +#include "sslmanager.h" +#include "manage_window.h" +#include "prefs_common.h" + +gint ssl_manager_verify_cert(SockInfo *sockinfo, const gchar *hostname, + X509 *server_cert, glong verify_result) +{ + static PangoFontDescription *font_desc; + GtkWidget *dialog; + GtkWidget *hbox; + GtkWidget *image; + GtkWidget *vbox; + GtkWidget *label; + const gchar *title; + GString *message; + gchar *subject, *issuer; + guchar keyid[EVP_MAX_MD_SIZE]; + gchar keyidstr[EVP_MAX_MD_SIZE * 3 + 1] = ""; + guint keyidlen = 0; + gchar *sha1_keyidstr, *md5_keyidstr; + BIO *bio; + gchar not_before[64] = "", not_after[64] = ""; + gint i; + gint result; + gboolean disable_always = FALSE; + + if (verify_result == X509_V_OK) + return 0; + + gdk_threads_enter(); + + title = _("SSL certificate verify failed"); + + subject = X509_NAME_oneline(X509_get_subject_name(server_cert), + NULL, 0); + issuer = X509_NAME_oneline(X509_get_issuer_name(server_cert), NULL, 0); + + bio = BIO_new(BIO_s_mem()); + ASN1_TIME_print(bio, X509_get_notBefore(server_cert)); + BIO_gets(bio, not_before, sizeof(not_before)); + BIO_reset(bio); + ASN1_TIME_print(bio, X509_get_notAfter(server_cert)); + BIO_gets(bio, not_after, sizeof(not_after)); + BIO_free(bio); + + if (X509_digest(server_cert, EVP_sha1(), keyid, &keyidlen)) { + for (i = 0; i < keyidlen; i++) + g_snprintf(keyidstr + i * 3, 4, "%02x:", keyid[i]); + keyidstr[keyidlen * 3 - 1] = '\0'; + sha1_keyidstr = g_ascii_strup(keyidstr, -1); + } else { + sha1_keyidstr = g_strdup("(cannot calculate digest)"); + } + if (X509_digest(server_cert, EVP_md5(), keyid, &keyidlen)) { + for (i = 0; i < keyidlen; i++) + g_snprintf(keyidstr + i * 3, 4, "%02x:", keyid[i]); + keyidstr[keyidlen * 3 - 1] = '\0'; + md5_keyidstr = g_ascii_strup(keyidstr, -1); + } else { + md5_keyidstr = g_strdup("(cannot calculate digest)"); + } + + message = g_string_new(""); + g_string_append_printf(message, _("The SSL certificate of %s cannot be verified by the following reason:"), hostname); + if (verify_result == X509_V_ERR_APPLICATION_VERIFICATION) { + g_string_append_printf(message, "\n certificate hostname does not match\n\n"); + } else { + g_string_append_printf(message, "\n %s\n\n", X509_verify_cert_error_string(verify_result)); + } + g_string_append_printf(message, _("Subject: %s\n"), subject ? subject : "(unknown)"); + g_string_append_printf(message, _("Issuer: %s\n"), issuer ? issuer : "(unknown)"); + g_string_append_printf(message, _("Issued date: %s\n"), not_before); + g_string_append_printf(message, _("Expire date: %s\n"), not_after); + g_string_append(message, "\n"); + g_string_append_printf(message, _("SHA1 fingerprint: %s\n"), sha1_keyidstr); + g_string_append_printf(message, _("MD5 fingerprint: %s\n"), md5_keyidstr); + g_string_append(message, "\n"); + g_string_append(message, _("Do you accept this certificate?")); + g_free(md5_keyidstr); + g_free(sha1_keyidstr); + if (issuer) + OPENSSL_free(issuer); + if (subject) + OPENSSL_free(subject); + + dialog = gtk_dialog_new(); + gtk_window_set_title(GTK_WINDOW(dialog), title); + gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, FALSE); + gtk_window_set_position(GTK_WINDOW(dialog), + GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + manage_window_set_transient(GTK_WINDOW(dialog)); + gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE); + gtk_widget_realize(dialog); + + hbox = gtk_hbox_new(FALSE, 12); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 12); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), + hbox, FALSE, FALSE, 0); + + image = gtk_image_new_from_stock + (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG); + + gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.0); + gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); + + vbox = gtk_vbox_new(FALSE, 12); + gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0); + + label = gtk_label_new(title); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + + if (!font_desc) { + gint size; + + size = pango_font_description_get_size + (label->style->font_desc); + font_desc = pango_font_description_new(); + pango_font_description_set_weight + (font_desc, PANGO_WEIGHT_BOLD); + pango_font_description_set_size + (font_desc, size * PANGO_SCALE_LARGE); + } + if (font_desc) + gtk_widget_modify_font(label, font_desc); + + label = gtk_label_new(message->str); + g_string_free(message, TRUE); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + GTK_WIDGET_UNSET_FLAGS(label, GTK_CAN_FOCUS); +#ifdef G_OS_WIN32 + { + GtkStyle *style; + style = gtk_widget_get_style(dialog); + gtk_widget_modify_base(label, GTK_STATE_ACTIVE, + &style->base[GTK_STATE_SELECTED]); + gtk_widget_modify_text(label, GTK_STATE_ACTIVE, + &style->text[GTK_STATE_SELECTED]); + } +#endif + + /* prohibit acception of expired certificates */ + if (verify_result == X509_V_ERR_CERT_HAS_EXPIRED) + disable_always = TRUE; + + if (prefs_common.comply_gnome_hig) + gtk_dialog_add_buttons(GTK_DIALOG(dialog), + _("_Reject"), GTK_RESPONSE_REJECT, + _("_Temporarily accept"), GTK_RESPONSE_OK, + _("Always _accept"), GTK_RESPONSE_ACCEPT, + NULL); + else + gtk_dialog_add_buttons(GTK_DIALOG(dialog), + _("Always _accept"), GTK_RESPONSE_ACCEPT, + _("_Temporarily accept"), GTK_RESPONSE_OK, + _("_Reject"), GTK_RESPONSE_REJECT, + NULL); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); + if (disable_always) + gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), + GTK_RESPONSE_ACCEPT, FALSE); + + gtk_widget_show_all(dialog); + + result = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + + gdk_threads_leave(); + + switch (result) { + case GTK_RESPONSE_ACCEPT: + return 0; + case GTK_RESPONSE_OK: + return 1; + case GTK_RESPONSE_REJECT: + default: + break; + } + + return -1; +} + +#endif /* USE_SSL */ diff --git a/src/sslmanager.h b/src/sslmanager.h new file mode 100644 index 0000000..92b9b0d --- /dev/null +++ b/src/sslmanager.h @@ -0,0 +1,40 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2006 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __SSLMANAGER_H__ +#define __SSLMANAGER_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if USE_SSL + +#include + +#include "ssl.h" + +gint ssl_manager_verify_cert (SockInfo *sockinfo, + const gchar *hostname, + X509 *server_cert, + glong verify_result); + +#endif /* USE_SSL */ + +#endif /* __MAIN_H__ */ diff --git a/src/statusbar.c b/src/statusbar.c new file mode 100644 index 0000000..2bd7ff8 --- /dev/null +++ b/src/statusbar.c @@ -0,0 +1,112 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2006 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include "statusbar.h" +#include "gtkutils.h" +#include "utils.h" + +#define BUFFSIZE 1024 + +static GList *statusbar_list = NULL; + +GtkWidget *statusbar_create(void) +{ + GtkWidget *statusbar; + + statusbar = gtk_statusbar_new(); + gtk_widget_set_size_request(statusbar, 1, -1); + statusbar_list = g_list_append(statusbar_list, statusbar); + + set_log_show_status_func(statusbar_puts_all); + + return statusbar; +} + +void statusbar_puts(GtkStatusbar *statusbar, const gchar *str) +{ + gint cid; + gchar *buf; + + buf = g_strdup(str); + strretchomp(buf); + + cid = gtk_statusbar_get_context_id(statusbar, "Standard Output"); + gtk_statusbar_pop(statusbar, cid); + gtk_statusbar_push(statusbar, cid, buf); + gtkut_widget_draw_now(GTK_WIDGET(statusbar)); + + g_free(buf); +} + +void statusbar_puts_all(const gchar *str) +{ + GList *cur; + + gdk_threads_enter(); + for (cur = statusbar_list; cur != NULL; cur = cur->next) + statusbar_puts(GTK_STATUSBAR(cur->data), str); + gdk_threads_leave(); +} + +void statusbar_print(GtkStatusbar *statusbar, const gchar *format, ...) +{ + va_list args; + gchar buf[BUFFSIZE]; + + va_start(args, format); + g_vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + + statusbar_puts(statusbar, buf); +} + +void statusbar_print_all(const gchar *format, ...) +{ + va_list args; + gchar buf[BUFFSIZE]; + GList *cur; + + va_start(args, format); + g_vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + + for (cur = statusbar_list; cur != NULL; cur = cur->next) + statusbar_puts(GTK_STATUSBAR(cur->data), buf); +} + +void statusbar_pop_all(void) +{ + GList *cur; + gint cid; + + for (cur = statusbar_list; cur != NULL; cur = cur->next) { + cid = gtk_statusbar_get_context_id(GTK_STATUSBAR(cur->data), + "Standard Output"); + gtk_statusbar_pop(GTK_STATUSBAR(cur->data), cid); + } +} diff --git a/src/statusbar.h b/src/statusbar.h new file mode 100644 index 0000000..e84a46a --- /dev/null +++ b/src/statusbar.h @@ -0,0 +1,38 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __STATUSBAR_H__ +#define __STATUSBAR_H__ + +#include +#include +#include + +GtkWidget *statusbar_create (void); +void statusbar_puts (GtkStatusbar *statusbar, + const gchar *str); +void statusbar_puts_all (const gchar *str); +void statusbar_print (GtkStatusbar *statusbar, + const gchar *format, ...) + G_GNUC_PRINTF(2, 3); +void statusbar_print_all (const gchar *format, ...) + G_GNUC_PRINTF(1, 2); +void statusbar_pop_all (void); + +#endif /* __STATUSBAR_H__ */ diff --git a/src/stock_pixmap.c b/src/stock_pixmap.c new file mode 100644 index 0000000..5cb8c5e --- /dev/null +++ b/src/stock_pixmap.c @@ -0,0 +1,335 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2013 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include + +#include "stock_pixmap.h" +#include "gtkutils.h" +#include "utils.h" + +#include "icons/address.xpm" +#include "icons/category.xpm" +#include "icons/clip.xpm" +#include "icons/complete.xpm" +#include "icons/continue.xpm" +#include "icons/deleted.xpm" +#include "icons/error.xpm" +#include "icons/forwarded.xpm" +#include "icons/interface.xpm" +#include "icons/jpilot.xpm" +#include "icons/ldap.xpm" +#include "icons/linewrap.xpm" +#include "icons/mark.xpm" +#include "icons/new.xpm" +#include "icons/replied.xpm" +#include "icons/unread.xpm" +#include "icons/vcard.xpm" +#include "icons/online.xpm" +#include "icons/offline.xpm" +#include "icons/mail.xpm" +#include "icons/stock_inbox.h" +#include "icons/stock_outbox.h" +#include "icons/stock_mail.h" +#include "icons/stock_attach.h" +#include "icons/stock_mail-compose.h" +#include "icons/stock_mail-compose_16.h" +#include "icons/stock_mail-forward.h" +#include "icons/stock_mail-receive.h" +#include "icons/stock_mail_receive_all.h" +#include "icons/stock_mail-reply.h" +#include "icons/stock_mail-reply-to-all.h" +#include "icons/stock_mail-send.h" +#include "icons/stock_mail_send_queue.h" +#include "icons/stock_insert-file.h" +#include "icons/stock_addressbook.h" +#include "icons/stock_delete.h" +#include "icons/stock_delete_16.h" +#include "icons/stock_spam.h" +#include "icons/stock_spam_16.h" +#include "icons/stock_notspam.h" +#include "icons/stock_hand-signed.h" +#include "icons/stock_sylpheed.h" +#include "icons/stock_sylpheed_16.h" +#include "icons/stock_sylpheed_32.h" +#include "icons/stock_sylpheed_newmail.h" +#include "icons/stock_sylpheed_newmail_16.h" +#include "icons/sylpheed-logo.h" +#include "icons/stock_person.h" +#include "icons/stock_book.h" +#include "icons/folder-close.h" +#include "icons/folder-open.h" +#include "icons/folder-noselect.h" +#include "icons/folder-search.h" +#include "icons/group.h" +#include "icons/html.h" + +typedef struct _StockPixmapData StockPixmapData; + +struct _StockPixmapData +{ + gchar **data; + GdkPixmap *pixmap; + GdkBitmap *mask; + + GdkPixbuf *pixbuf; + const guint8 *pixbuf_data; + gint pixbuf_data_len; + + gchar *icon_name; + gint size; + + gchar *icon_file; + GdkPixbuf *icon_file_pixbuf; +}; + +static StockPixmapData pixmaps[] = +{ + {address_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "address.png"}, + {NULL, NULL, NULL, NULL, stock_book, sizeof(stock_book), "stock_book", 16, "stock_book.png"}, + {category_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "category.png"}, + {clip_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "clip.png"}, + {complete_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "complete.png"}, + {continue_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "continue.png"}, + {deleted_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "deleted.png"}, + {NULL, NULL, NULL, NULL, folder_close, sizeof(folder_close), "folder-close", 0, "folder-close.png"}, + {NULL, NULL, NULL, NULL, folder_open, sizeof(folder_open), "folder-open", 0, "folder-open.png"}, + {NULL, NULL, NULL, NULL, folder_noselect, sizeof(folder_noselect), "folder-noselect", 0, "folder-noselect.png"}, + {error_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "error.png"}, + {forwarded_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "forwarded.png"}, + {NULL, NULL, NULL, NULL, group, sizeof(group), "group", 0, "group.png"}, + {NULL, NULL, NULL, NULL, html, sizeof(html), "html", 0, "html.png"}, + {interface_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "interface.png"}, + {jpilot_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "jpilot.png"}, + {ldap_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "ldap.png"}, + {linewrap_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "linewrap.png"}, + {mark_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "mark.png"}, + {new_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "new.png"}, + {replied_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "replied.png"}, + {unread_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "unread.png"}, + {vcard_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "vcard.png"}, + {online_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "online.png"}, + {offline_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "offline.png"}, + {mail_xpm , NULL, NULL, NULL, NULL, 0, NULL, 0, "mail.png"}, + + {NULL, NULL, NULL, NULL, stock_inbox, sizeof(stock_inbox), "stock_inbox", 16, "stock_inbox.png"}, + {NULL, NULL, NULL, NULL, stock_outbox, sizeof(stock_outbox), "stock_outbox", 16, "stock_outbox.png"}, + {NULL, NULL, NULL, NULL, stock_mail_compose_16, sizeof(stock_mail_compose_16), "stock_mail-compose", 16, "stock_mail-compose_16.png"}, + {NULL, NULL, NULL, NULL, stock_delete_16, sizeof(stock_delete_16), GTK_STOCK_DELETE, 16, "stock_delete_16.png"}, + {NULL, NULL, NULL, NULL, stock_mail, sizeof(stock_mail), "stock_mail", 24, "stock_mail.png"}, + {NULL, NULL, NULL, NULL, stock_attach, sizeof(stock_attach), "stock_attach", 24, "stock_attach.png"}, + {NULL, NULL, NULL, NULL, stock_mail_compose, sizeof(stock_mail_compose), "stock_mail-compose", 24, "stock_mail-compose.png"}, + {NULL, NULL, NULL, NULL, stock_mail_forward, sizeof(stock_mail_forward), "stock_mail-forward", 24, "stock_mail-forward.png"}, + {NULL, NULL, NULL, NULL, stock_mail_receive, sizeof(stock_mail_receive), "stock_mail-receive", 24, "stock_mail-receive.png"}, + {NULL, NULL, NULL, NULL, stock_mail_receive_all, sizeof(stock_mail_receive_all), NULL, 0, "stock_mail_receive_all.png"}, + {NULL, NULL, NULL, NULL, stock_mail_reply, sizeof(stock_mail_reply), "stock_mail-reply", 24, "stock_mail-reply.png"}, + {NULL, NULL, NULL, NULL, stock_mail_reply_to_all, sizeof(stock_mail_reply_to_all), "stock_mail-reply-to-all", 24, "stock_mail-reply-to-all.png"}, + {NULL, NULL, NULL, NULL, stock_mail_send, sizeof(stock_mail_send), "stock_mail-send", 24, "stock_mail-send.png"}, + {NULL, NULL, NULL, NULL, stock_mail_send_queue, sizeof(stock_mail_send_queue), NULL, 0, "stock_mail_send_queue.png"}, + {NULL, NULL, NULL, NULL, stock_insert_file, sizeof(stock_insert_file), "stock_insert-file", 24, "stock_insert-file.png"}, + {NULL, NULL, NULL, NULL, stock_addressbook, sizeof(stock_addressbook), "stock_addressbook", 24, "stock_addressbook.png"}, + {NULL, NULL, NULL, NULL, stock_delete, sizeof(stock_delete), GTK_STOCK_DELETE, 24, "stock_delete.png"}, + {NULL, NULL, NULL, NULL, stock_spam, sizeof(stock_spam), "stock_spam", 24, "stock_spam.png"}, + {NULL, NULL, NULL, NULL, stock_notspam, sizeof(stock_notspam), "stock_notspam", 24, "stock_notspam.png"}, + {NULL, NULL, NULL, NULL, stock_hand_signed, sizeof(stock_hand_signed), "stock_hand-signed", 24, "stock_hand-signed.png"}, + {NULL, NULL, NULL, NULL, stock_sylpheed, sizeof(stock_sylpheed), NULL, 0, "stock_sylpheed.png"}, + {NULL, NULL, NULL, NULL, stock_sylpheed_16, sizeof(stock_sylpheed_16), NULL, 0, "stock_sylpheed_16.png"}, + {NULL, NULL, NULL, NULL, stock_sylpheed_32, sizeof(stock_sylpheed_32), NULL, 0, "stock_sylpheed_32.png"}, + {NULL, NULL, NULL, NULL, stock_sylpheed_newmail, sizeof(stock_sylpheed_newmail), NULL, 0, "stock_sylpheed_newmail.png"}, + {NULL, NULL, NULL, NULL, stock_sylpheed_newmail_16, sizeof(stock_sylpheed_newmail_16), NULL, 0, "stock_sylpheed_newmail_16.png"}, + {NULL, NULL, NULL, NULL, sylpheed_logo, sizeof(sylpheed_logo), NULL, 0, "sylpheed-logo.png"}, + {NULL, NULL, NULL, NULL, stock_person, sizeof(stock_person), "stock_person", 16, "stock_person.png"}, + {NULL, NULL, NULL, NULL, folder_search, sizeof(folder_search), "folder-search", 0, "folder-search.png"}, + {NULL, NULL, NULL, NULL, stock_spam_16, sizeof(stock_spam_16), "stock_spam", 16, "stock_spam_16.png"}, + + /* for toolbar */ + {NULL, NULL, NULL, NULL, NULL, 0, GTK_STOCK_NETWORK, 24, "remote-mailbox.png"}, + {NULL, NULL, NULL, NULL, NULL, 0, GTK_STOCK_GO_DOWN, 24, "next.png"}, + {NULL, NULL, NULL, NULL, NULL, 0, GTK_STOCK_GO_UP, 24, "prev.png"}, + {NULL, NULL, NULL, NULL, NULL, 0, GTK_STOCK_FIND, 24, "search.png"}, + {NULL, NULL, NULL, NULL, NULL, 0, GTK_STOCK_PRINT, 24, "print.png"}, + {NULL, NULL, NULL, NULL, NULL, 0, GTK_STOCK_STOP, 24, "stop.png"}, + {NULL, NULL, NULL, NULL, NULL, 0, GTK_STOCK_EXECUTE, 24, "execute.png"}, + {NULL, NULL, NULL, NULL, NULL, 0, GTK_STOCK_PREFERENCES, 24, "common-prefs.png"}, + {NULL, NULL, NULL, NULL, NULL, 0, GTK_STOCK_PREFERENCES, 24, "account-prefs.png"}, + {NULL, NULL, NULL, NULL, NULL, 0, GTK_STOCK_SAVE, 24, "save.png"}, + {NULL, NULL, NULL, NULL, NULL, 0, GTK_STOCK_EDIT, 24, "editor.png"}, +}; + +static gchar *theme_dir = NULL; + + +GtkWidget *stock_pixbuf_widget(GtkWidget *window, StockPixmap icon) +{ + GdkPixbuf *pixbuf; + + g_return_val_if_fail(icon >= 0 && icon < N_STOCK_PIXMAPS, NULL); + + stock_pixbuf_gdk(window, icon, &pixbuf); + return gtk_image_new_from_pixbuf(pixbuf); +} + +GtkWidget *stock_pixbuf_widget_scale(GtkWidget *window, StockPixmap icon, + gint width, gint height) +{ + GdkPixbuf *pixbuf; + GdkPixbuf *scaled_pixbuf; + GtkWidget *image; + + g_return_val_if_fail(icon >= 0 && icon < N_STOCK_PIXMAPS, NULL); + + stock_pixbuf_gdk(window, icon, &pixbuf); + scaled_pixbuf = gdk_pixbuf_scale_simple(pixbuf, width, height, + GDK_INTERP_HYPER); + image = gtk_image_new_from_pixbuf(scaled_pixbuf); + g_object_unref(scaled_pixbuf); + + return image; +} + +static gboolean stock_pixbuf_load_theme_icon_file(StockPixmap icon) +{ + StockPixmapData *pix_d; + + g_return_val_if_fail(icon >= 0 && icon < N_STOCK_PIXMAPS, FALSE); + + pix_d = &pixmaps[icon]; + + if (!pix_d->icon_file_pixbuf && pix_d->icon_file && theme_dir) { + gchar *path; + + path = g_strconcat(theme_dir, G_DIR_SEPARATOR_S, + pix_d->icon_file, NULL); + debug_print("stock_pixbuf_load_theme_icon_file: checking theme icon: %s\n", path); + if (is_file_exist(path)) { + debug_print("stock_pixbuf_load_theme_icon_file: loading theme icon: %s\n", path); + pix_d->icon_file_pixbuf = gdk_pixbuf_new_from_file(path, NULL); + if (!pix_d->icon_file_pixbuf) { + g_warning("stock_pixbuf_load_theme_icon_file: loading of theme icon failed: %s", path); + pix_d->icon_file = NULL; + } else if (!pix_d->pixbuf) + pix_d->pixbuf = pix_d->icon_file_pixbuf; + } else + pix_d->icon_file = NULL; + g_free(path); + } + + return pix_d->icon_file_pixbuf != NULL; +} + +GtkWidget *stock_pixbuf_widget_for_toolbar(StockPixmap icon) +{ + GtkWidget *image = NULL; + StockPixmapData *pix_d; + + g_return_val_if_fail(icon >= 0 && icon < N_STOCK_PIXMAPS, NULL); + + stock_pixbuf_load_theme_icon_file(icon); + + pix_d = &pixmaps[icon]; + + if (pix_d->icon_file_pixbuf) { + image = gtk_image_new_from_pixbuf(pix_d->icon_file_pixbuf); + } + + if (!image && pix_d->icon_name) { + if (gtk_style_lookup_icon_set(gtk_widget_get_default_style(), pix_d->icon_name)) + image = gtk_image_new_from_stock(pix_d->icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR); + else + pix_d->icon_name = NULL; + } + + if (!image) + image = stock_pixbuf_widget(NULL, icon); + + return image; +} + +/* create GdkPixmap if it has not created yet */ +gint stock_pixmap_gdk(GtkWidget *window, StockPixmap icon, + GdkPixmap **pixmap, GdkBitmap **mask) +{ + StockPixmapData *pix_d; + + if (pixmap) *pixmap = NULL; + if (mask) *mask = NULL; + + g_return_val_if_fail(window != NULL, -1); + g_return_val_if_fail(icon >= 0 && icon < N_STOCK_PIXMAPS, -1); + + pix_d = &pixmaps[icon]; + + g_return_val_if_fail(pix_d->data != NULL, -1); + + if (!pix_d->pixmap) { + PIXMAP_CREATE(window, pix_d->pixmap, pix_d->mask, + pix_d->data); + } + + if (pixmap) *pixmap = pix_d->pixmap; + if (mask) *mask = pix_d->mask; + + return 0; +} + +gint stock_pixbuf_gdk(GtkWidget *window, StockPixmap icon, GdkPixbuf **pixbuf) +{ + StockPixmapData *pix_d; + + if (pixbuf) + *pixbuf = NULL; + + g_return_val_if_fail(icon >= 0 && icon < N_STOCK_PIXMAPS, -1); + + stock_pixbuf_load_theme_icon_file(icon); + + pix_d = &pixmaps[icon]; + + if (!pix_d->pixbuf && pix_d->pixbuf_data) + pix_d->pixbuf = gdk_pixbuf_new_from_inline + (pix_d->pixbuf_data_len, pix_d->pixbuf_data, + FALSE, NULL); + if (!pix_d->pixbuf && pix_d->icon_name) + pix_d->pixbuf = gtk_icon_theme_load_icon + (gtk_icon_theme_get_default(), + pix_d->icon_name, pix_d->size, 0, NULL); + if (!pix_d->pixbuf && pix_d->data) + pix_d->pixbuf = gdk_pixbuf_new_from_xpm_data + ((const gchar **)pix_d->data); + if (!pix_d->pixbuf) { + g_warning("can't read image %d\n", icon); + return -1; + } + + if (pixbuf) + *pixbuf = pix_d->pixbuf; + + return 0; +} + +gint stock_pixbuf_set_theme_dir(const gchar *dir) +{ + g_free(theme_dir); + theme_dir = g_strdup(dir); + + return 0; +} diff --git a/src/stock_pixmap.h b/src/stock_pixmap.h new file mode 100644 index 0000000..b049698 --- /dev/null +++ b/src/stock_pixmap.h @@ -0,0 +1,122 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2013 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __STOCK_PIXMAP_H__ +#define __STOCK_PIXMAP_H__ + +#include +#include + +typedef enum +{ + STOCK_PIXMAP_ADDRESS, + STOCK_PIXMAP_BOOK, + STOCK_PIXMAP_CATEGORY, + STOCK_PIXMAP_CLIP, + STOCK_PIXMAP_COMPLETE, + STOCK_PIXMAP_CONTINUE, + STOCK_PIXMAP_DELETED, + STOCK_PIXMAP_FOLDER_CLOSE, + STOCK_PIXMAP_FOLDER_OPEN, + STOCK_PIXMAP_FOLDER_NOSELECT, + STOCK_PIXMAP_ERROR, + STOCK_PIXMAP_FORWARDED, + STOCK_PIXMAP_GROUP, + STOCK_PIXMAP_HTML, + STOCK_PIXMAP_INTERFACE, + STOCK_PIXMAP_JPILOT, + STOCK_PIXMAP_LDAP, + STOCK_PIXMAP_LINEWRAP, + STOCK_PIXMAP_MARK, + STOCK_PIXMAP_NEW, + STOCK_PIXMAP_REPLIED, + STOCK_PIXMAP_UNREAD, + STOCK_PIXMAP_VCARD, + STOCK_PIXMAP_ONLINE, + STOCK_PIXMAP_OFFLINE, + STOCK_PIXMAP_MAIL_SMALL, + + STOCK_PIXMAP_INBOX, + STOCK_PIXMAP_OUTBOX, + STOCK_PIXMAP_DRAFT, + STOCK_PIXMAP_TRASH, + STOCK_PIXMAP_MAIL, + STOCK_PIXMAP_MAIL_ATTACH, + STOCK_PIXMAP_MAIL_COMPOSE, + STOCK_PIXMAP_MAIL_FORWARD, + STOCK_PIXMAP_MAIL_RECEIVE, + STOCK_PIXMAP_MAIL_RECEIVE_ALL, + STOCK_PIXMAP_MAIL_REPLY, + STOCK_PIXMAP_MAIL_REPLY_TO_ALL, + STOCK_PIXMAP_MAIL_SEND, + STOCK_PIXMAP_MAIL_SEND_QUEUE, + STOCK_PIXMAP_INSERT_FILE, + STOCK_PIXMAP_ADDRESS_BOOK, + STOCK_PIXMAP_DELETE, + STOCK_PIXMAP_SPAM, + STOCK_PIXMAP_NOTSPAM, + STOCK_PIXMAP_SIGN, + STOCK_PIXMAP_SYLPHEED, + STOCK_PIXMAP_SYLPHEED_SMALL, + STOCK_PIXMAP_SYLPHEED_32, + STOCK_PIXMAP_SYLPHEED_NEWMAIL, + STOCK_PIXMAP_SYLPHEED_NEWMAIL_SMALL, + STOCK_PIXMAP_SYLPHEED_LOGO, + STOCK_PIXMAP_PERSON, + STOCK_PIXMAP_FOLDER_SEARCH, + STOCK_PIXMAP_SPAM_SMALL, + + /* for toolbar */ + + STOCK_PIXMAP_REMOTE_MAILBOX, + STOCK_PIXMAP_NEXT, + STOCK_PIXMAP_PREV, + STOCK_PIXMAP_SEARCH, + STOCK_PIXMAP_PRINT, + STOCK_PIXMAP_STOP, + STOCK_PIXMAP_EXECUTE, + STOCK_PIXMAP_COMMON_PREFS, + STOCK_PIXMAP_ACCOUNT_PREFS, + STOCK_PIXMAP_SAVE, + STOCK_PIXMAP_EDIT, + + N_STOCK_PIXMAPS +} StockPixmap; + +GtkWidget *stock_pixbuf_widget (GtkWidget *window, + StockPixmap icon); + +GtkWidget *stock_pixbuf_widget_scale (GtkWidget *window, + StockPixmap icon, + gint width, + gint height); + +GtkWidget *stock_pixbuf_widget_for_toolbar(StockPixmap icon); + +gint stock_pixmap_gdk (GtkWidget *window, + StockPixmap icon, + GdkPixmap **pixmap, + GdkBitmap **mask); +gint stock_pixbuf_gdk (GtkWidget *window, + StockPixmap icon, + GdkPixbuf **pixbuf); + +gint stock_pixbuf_set_theme_dir (const gchar *dir); + +#endif /* __STOCK_PIXMAP_H__ */ diff --git a/src/subscribedialog.c b/src/subscribedialog.c new file mode 100644 index 0000000..902fc35 --- /dev/null +++ b/src/subscribedialog.c @@ -0,0 +1,661 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2015 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "subscribedialog.h" +#include "manage_window.h" +#include "mainwindow.h" +#include "gtkutils.h" +#include "utils.h" +#include "news.h" +#include "folder.h" +#include "alertpanel.h" +#include "statusbar.h" +#include "recv.h" +#include "socket.h" + +enum { + SUBSCRIBE_TOGGLE, + SUBSCRIBE_NAME, + SUBSCRIBE_NUM, + SUBSCRIBE_TYPE, + SUBSCRIBE_INFO, + SUBSCRIBE_CAN_TOGGLE, + N_SUBSCRIBE_COLUMNS +}; + +#define SUBSCRIBE_DIALOG_WIDTH 450 +#define SUBSCRIBE_DIALOG_HEIGHT 400 +#define SUBSCRIBE_COL_NAME_WIDTH 250 + +static gboolean ack; +static gboolean locked; + +static GtkWidget *dialog; +static GtkWidget *entry; +static GtkWidget *treeview; +static GtkWidget *status_label; +static GtkWidget *ok_button; + +static GtkTreeStore *tree_store; + +static GSList *group_list; +static GSList *subscribe_list; +static Folder *news_folder; + +static void subscribe_dialog_create (void); +static void subscribe_dialog_set_list (const gchar *pattern, + gboolean refresh); +static void subscribe_search (void); +static void subscribe_clear (void); + +static gboolean subscribe_recv_func (SockInfo *sock, + gint count, + gint read_bytes, + gpointer data); + +static gint window_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); +static void ok_clicked (GtkWidget *widget, + gpointer data); +static void cancel_clicked (GtkWidget *widget, + gpointer data); +static void refresh_clicked (GtkWidget *widget, + gpointer data); +static gboolean key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); + +static gboolean subscribe_selected (GtkTreeSelection *selection, + GtkTreeModel *model, + GtkTreePath *path, + gboolean cur_selected, + gpointer data); + +static void subscribe_toggled (GtkCellRenderer *cell, + gchar *path, + gpointer data); + +static void entry_activated (GtkEditable *editable); +static void search_clicked (GtkWidget *widget, + gpointer data); + +GSList *subscribe_dialog(Folder *folder) +{ + GNode *node; + FolderItem *item; + + if (dialog && GTK_WIDGET_VISIBLE(dialog)) return NULL; + + if (!dialog) + subscribe_dialog_create(); + + news_folder = folder; + + gtk_widget_show(dialog); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + manage_window_set_transient(GTK_WINDOW(dialog)); + gtk_widget_grab_focus(ok_button); + gtk_widget_grab_focus(treeview); + GTK_EVENTS_FLUSH(); + + subscribe_list = NULL; + for (node = folder->node->children; node != NULL; node = node->next) { + item = FOLDER_ITEM(node->data); + subscribe_list = g_slist_append(subscribe_list, + g_strdup(item->path)); + } + + subscribe_dialog_set_list(NULL, TRUE); + + if (ack) gtk_main(); + + manage_window_focus_out(dialog, NULL, NULL); + gtk_widget_hide(dialog); + main_window_popup(main_window_get()); + + if (ack) { + GSList *cur; + + slist_free_strings(subscribe_list); + subscribe_list = NULL; + for (cur = group_list; cur != NULL; cur = cur->next) { + NewsGroupInfo *ginfo = (NewsGroupInfo *)cur->data; + + if (ginfo->subscribed) + subscribe_list = g_slist_append + (subscribe_list, g_strdup(ginfo->name)); + } + } + + subscribe_clear(); + + return subscribe_list; +} + +static void subscribe_dialog_create(void) +{ + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *msg_label; + GtkWidget *search_button; + GtkWidget *confirm_area; + GtkWidget *cancel_button; + GtkWidget *refresh_button; + GtkWidget *scrolledwin; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + + dialog = gtk_dialog_new(); + gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, TRUE, FALSE); + gtk_widget_set_size_request(dialog, + SUBSCRIBE_DIALOG_WIDTH * gtkut_get_dpi_multiplier(), + SUBSCRIBE_DIALOG_HEIGHT * gtkut_get_dpi_multiplier()); + gtk_container_set_border_width + (GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), 5); + gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); + gtk_window_set_title(GTK_WINDOW(dialog), _("Subscribe to newsgroup")); + g_signal_connect(G_OBJECT(dialog), "delete_event", + G_CALLBACK(window_deleted), NULL); + g_signal_connect(G_OBJECT(dialog), "key_press_event", + G_CALLBACK(key_pressed), NULL); + MANAGE_WINDOW_SIGNALS_CONNECT(dialog); + + gtk_widget_realize(dialog); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), vbox); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 8); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + msg_label = gtk_label_new(_("Select newsgroups to subscribe.")); + gtk_box_pack_start(GTK_BOX(hbox), msg_label, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + msg_label = gtk_label_new(_("Find groups:")); + gtk_box_pack_start(GTK_BOX(hbox), msg_label, FALSE, FALSE, 0); + + entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); + g_signal_connect(G_OBJECT(entry), "activate", + G_CALLBACK(entry_activated), NULL); + + search_button = gtk_button_new_with_label(_(" Search ")); + gtk_box_pack_start(GTK_BOX(hbox), search_button, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(search_button), "clicked", + G_CALLBACK(search_clicked), NULL); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_box_pack_start(GTK_BOX (vbox), scrolledwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + + tree_store = gtk_tree_store_new(N_SUBSCRIBE_COLUMNS, + G_TYPE_BOOLEAN, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_POINTER, + G_TYPE_BOOLEAN); + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(tree_store), + SUBSCRIBE_NAME, + GTK_SORT_ASCENDING); + + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tree_store)); + g_object_unref(G_OBJECT(tree_store)); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), + SUBSCRIBE_NAME); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + gtk_tree_selection_set_select_function(selection, subscribe_selected, + NULL, NULL); + + gtk_container_add(GTK_CONTAINER(scrolledwin), treeview); + + renderer = gtk_cell_renderer_toggle_new(); + column = gtk_tree_view_column_new_with_attributes + (NULL, renderer, "active", SUBSCRIBE_TOGGLE, + "activatable", SUBSCRIBE_CAN_TOGGLE, + "visible", SUBSCRIBE_CAN_TOGGLE, NULL); + gtk_tree_view_column_set_min_width(column, 20); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + g_signal_connect(renderer, "toggled", G_CALLBACK(subscribe_toggled), + NULL); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes + (_("Newsgroup name"), renderer, "text", SUBSCRIBE_NAME, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + gtk_tree_view_set_expander_column(GTK_TREE_VIEW(treeview), column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes + (_("Messages"), renderer, "text", SUBSCRIBE_NUM, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes + (_("Type"), renderer, "text", SUBSCRIBE_TYPE, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + status_label = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(hbox), status_label, FALSE, FALSE, 0); + + gtkut_stock_button_set_create(&confirm_area, + &ok_button, GTK_STOCK_OK, + &cancel_button, GTK_STOCK_CANCEL, + &refresh_button, GTK_STOCK_REFRESH); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), + confirm_area); + gtk_widget_grab_default(ok_button); + + g_signal_connect(G_OBJECT(ok_button), "clicked", + G_CALLBACK(ok_clicked), NULL); + g_signal_connect(G_OBJECT(cancel_button), "clicked", + G_CALLBACK(cancel_clicked), NULL); + g_signal_connect(G_OBJECT(refresh_button), "clicked", + G_CALLBACK(refresh_clicked), NULL); + + gtk_widget_show_all(GTK_DIALOG(dialog)->vbox); +} + +static GHashTable *branch_node_table; + +static void subscribe_hash_init(void) +{ + branch_node_table = g_hash_table_new_full + (g_str_hash, g_str_equal, NULL, + (GDestroyNotify)gtk_tree_iter_free); +} + +static void subscribe_hash_free(void) +{ + hash_free_strings(branch_node_table); + g_hash_table_destroy(branch_node_table); +} + +static gboolean subscribe_hash_get_branch_node(const gchar *name, + GtkTreeIter *iter) +{ + GtkTreeIter *iter_; + + iter_ = g_hash_table_lookup(branch_node_table, name); + if (iter_) { + *iter = *iter_; + return TRUE; + } + + return FALSE; +} + +static void subscribe_hash_set_branch_node(const gchar *name, + GtkTreeIter *iter) +{ + GtkTreeIter *iter_; + + iter_ = gtk_tree_iter_copy(iter); + g_hash_table_replace(branch_node_table, g_strdup(name), iter_); +} + +static gchar *subscribe_get_parent_name(const gchar *name) +{ + gchar *p; + + p = strrchr(name, '.'); + if (!p) + return g_strdup(""); + return g_strndup(name, p - name); +} + +static gboolean subscribe_create_parent(const gchar *name, + const gchar *pattern, + GtkTreeIter *parent) +{ + GtkTreeIter parent_; + GtkTreeIter iter; + gchar *parent_name; + gboolean has_parent; + + if (*name == '\0') return FALSE; + if (subscribe_hash_get_branch_node(name, &iter)) { + *parent = iter; + return TRUE; + } + + parent_name = subscribe_get_parent_name(name); + has_parent = subscribe_create_parent(parent_name, pattern, &parent_); + + if (has_parent) { + gtk_tree_store_append(tree_store, &iter, &parent_); + if (g_pattern_match_simple(pattern, parent_name) == FALSE) + gtkut_tree_view_expand_parent_all + (GTK_TREE_VIEW(treeview), &iter); + } else + gtk_tree_store_append(tree_store, &iter, NULL); + gtk_tree_store_set(tree_store, &iter, SUBSCRIBE_NAME, name, -1); + + subscribe_hash_set_branch_node(name, &iter); + + g_free(parent_name); + + *parent = iter; + + return TRUE; +} + +static gboolean subscribe_create_branch(NewsGroupInfo *ginfo, + const gchar *pattern, + GtkTreeIter *iter) +{ + GtkTreeIter iter_; + GtkTreeIter parent; + const gchar *name = ginfo->name; + gchar *parent_name; + gint count; + const gchar *count_str; + const gchar *type_str; + gboolean has_parent; + + count = ginfo->last - ginfo->first; + if (count < 0) + count = 0; + count_str = itos(count); + + if (ginfo->type == 'y') + type_str = ""; + else if (ginfo->type == 'm') + type_str = _("moderated"); + else if (ginfo->type == 'n') + type_str = _("readonly"); + else + type_str = _("unknown"); + + parent_name = subscribe_get_parent_name(name); + has_parent = subscribe_create_parent(parent_name, pattern, &parent); + if (!subscribe_hash_get_branch_node(name, &iter_)) { + if (has_parent) { + gtk_tree_store_append(tree_store, &iter_, &parent); + if (g_pattern_match_simple(pattern, parent_name) == FALSE) + gtkut_tree_view_expand_parent_all + (GTK_TREE_VIEW(treeview), &iter_); + } else + gtk_tree_store_append(tree_store, &iter_, NULL); + } + + gtk_tree_store_set(tree_store, &iter_, + SUBSCRIBE_NAME, name, + SUBSCRIBE_NUM, count_str, + SUBSCRIBE_TYPE, type_str, + SUBSCRIBE_INFO, ginfo, + SUBSCRIBE_CAN_TOGGLE, TRUE, + -1); + + g_free(parent_name); + + *iter = iter_; + + return TRUE; +} + +static void subscribe_dialog_set_list(const gchar *pattern, gboolean refresh) +{ + gchar *pattern_; + GSList *cur; + GPatternSpec *pspec; + + if (locked) return; + locked = TRUE; + + if (!pattern || *pattern == '\0') + pattern_ = g_strdup("*"); + else if (strchr(pattern, '*') == NULL) + pattern_ = g_strconcat("*", pattern, "*", NULL); + else + pattern_ = g_strdup(pattern); + + if (refresh) { + ack = TRUE; + subscribe_clear(); + if (pattern) + gtk_entry_set_text(GTK_ENTRY(entry), pattern); + gtk_label_set_text(GTK_LABEL(status_label), + _("Getting newsgroup list...")); + GTK_EVENTS_FLUSH(); + recv_set_ui_func(subscribe_recv_func, NULL); + group_list = news_get_group_list(news_folder); + group_list = g_slist_reverse(group_list); + recv_set_ui_func(NULL, NULL); + statusbar_pop_all(); + if (group_list == NULL && ack == TRUE) { + alertpanel_error(_("Can't retrieve newsgroup list.")); + g_free(pattern_); + locked = FALSE; + return; + } + } else { + gtk_tree_store_clear(tree_store); + } + + subscribe_hash_init(); + + pspec = g_pattern_spec_new(pattern_); + + for (cur = group_list; cur != NULL ; cur = cur->next) { + NewsGroupInfo *ginfo = (NewsGroupInfo *)cur->data; + GtkTreeIter iter; + + if (!ginfo->name || !is_ascii_str(ginfo->name)) + continue; + + if (g_slist_find_custom(subscribe_list, ginfo->name, + (GCompareFunc)g_ascii_strcasecmp) + != NULL) + ginfo->subscribed = TRUE; + + if (g_pattern_match_string(pspec, ginfo->name)) { + subscribe_create_branch(ginfo, pattern_, &iter); + if (ginfo->subscribed) + gtk_tree_store_set(tree_store, &iter, + SUBSCRIBE_TOGGLE, TRUE, -1); + } + } + + g_pattern_spec_free(pspec); + subscribe_hash_free(); + g_free(pattern_); + + gtk_label_set_text(GTK_LABEL(status_label), _("Done.")); + + locked = FALSE; +} + +static void subscribe_search(void) +{ + gchar *str; + + if (locked) return; + + str = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1); + subscribe_dialog_set_list(str, FALSE); + g_free(str); +} + +static void subscribe_clear(void) +{ + gtk_tree_store_clear(tree_store); + gtk_entry_set_text(GTK_ENTRY(entry), ""); + news_group_list_free(group_list); + group_list = NULL; +} + +static gboolean subscribe_recv_func(SockInfo *sock, gint count, gint read_bytes, + gpointer data) +{ + gchar buf[BUFFSIZE]; + + g_snprintf(buf, sizeof(buf), + _("%d newsgroups received (%s read)"), + count, to_human_readable(read_bytes)); + gtk_label_set_text(GTK_LABEL(status_label), buf); + GTK_EVENTS_FLUSH(); + if (ack == FALSE) + return FALSE; + else + return TRUE; +} + +static gint window_deleted(GtkWidget *widget, GdkEventAny *event, gpointer data) +{ + ack = FALSE; + if (gtk_main_level() > 1) + gtk_main_quit(); + + return TRUE; +} + +static void ok_clicked(GtkWidget *widget, gpointer data) +{ + ack = TRUE; + if (gtk_main_level() > 1) + gtk_main_quit(); +} + +static void cancel_clicked(GtkWidget *widget, gpointer data) +{ + ack = FALSE; + if (gtk_main_level() > 1) + gtk_main_quit(); +} + +static void refresh_clicked(GtkWidget *widget, gpointer data) +{ + gchar *str; + + if (locked) return; + + news_remove_group_list_cache(news_folder); + + str = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1); + subscribe_dialog_set_list(str, TRUE); + g_free(str); +} + +static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + if (event && event->keyval == GDK_Escape) + cancel_clicked(NULL, NULL); + return FALSE; +} + +static gboolean subscribe_selected(GtkTreeSelection *selection, + GtkTreeModel *model, GtkTreePath *path, + gboolean cur_selected, gpointer data) +{ + GtkTreeIter iter; + NewsGroupInfo *ginfo; + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(tree_store), &iter, path)) + return FALSE; + + gtk_tree_model_get(GTK_TREE_MODEL(tree_store), &iter, + SUBSCRIBE_INFO, &ginfo, -1); + if (!ginfo) + return FALSE; + + return TRUE; +} + +static void subscribe_toggled(GtkCellRenderer *cell, gchar *path_str, + gpointer data) +{ + GtkTreePath *path; + GtkTreeIter iter; + gboolean enabled; + NewsGroupInfo *ginfo; + gboolean can_toggle; + + path = gtk_tree_path_new_from_string(path_str); + gtk_tree_model_get_iter(GTK_TREE_MODEL(tree_store), &iter, path); + gtk_tree_path_free(path); + + gtk_tree_model_get(GTK_TREE_MODEL(tree_store), &iter, + SUBSCRIBE_TOGGLE, &enabled, + SUBSCRIBE_INFO, &ginfo, + SUBSCRIBE_CAN_TOGGLE, &can_toggle, + -1); + if (ginfo && can_toggle) { + ginfo->subscribed = !enabled; + gtk_tree_store_set(tree_store, &iter, + SUBSCRIBE_TOGGLE, !enabled, -1); + } +} + +static void entry_activated(GtkEditable *editable) +{ + subscribe_search(); +} + +static void search_clicked(GtkWidget *widget, gpointer data) +{ + subscribe_search(); +} diff --git a/src/subscribedialog.h b/src/subscribedialog.h new file mode 100644 index 0000000..afb3636 --- /dev/null +++ b/src/subscribedialog.h @@ -0,0 +1,29 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __SUBSCRIBEDIALOG_H__ +#define __SUBSCRIBEDIALOG_H__ + +#include + +#include "folder.h" + +GSList *subscribe_dialog (Folder *folder); + +#endif /* __SUBSCRIBEDIALOG_H__ */ diff --git a/src/summaryview.c b/src/summaryview.c new file mode 100644 index 0000000..211efed --- /dev/null +++ b/src/summaryview.c @@ -0,0 +1,6683 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2018 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "main.h" +#include "menu.h" +#include "mainwindow.h" +#include "folderview.h" +#include "summaryview.h" +#include "messageview.h" +#include "foldersel.h" +#include "procmsg.h" +#include "procheader.h" +#include "sourcewindow.h" +#include "prefs_common.h" +#include "prefs_summary_column.h" +#include "prefs_filter.h" +#include "account.h" +#include "compose.h" +#include "utils.h" +#include "gtkutils.h" +#include "stock_pixmap.h" +#include "filesel.h" +#include "alertpanel.h" +#include "inputdialog.h" +#include "statusbar.h" +#include "trayicon.h" +#include "printing.h" +#include "filter.h" +#include "folder.h" +#include "colorlabel.h" +#include "inc.h" +#include "imap.h" +#include "plugin.h" + +#define STATUSBAR_PUSH(mainwin, str) \ +{ \ + gtk_statusbar_push(GTK_STATUSBAR(mainwin->statusbar), \ + mainwin->summaryview_cid, str); \ + gtkut_widget_draw_now(mainwin->statusbar); \ +} + +#define STATUSBAR_POP(mainwin) \ +{ \ + gtk_statusbar_pop(GTK_STATUSBAR(mainwin->statusbar), \ + mainwin->summaryview_cid); \ +} + +#define GET_MSG_INFO(msginfo, iter__) \ +{ \ + gtk_tree_model_get(GTK_TREE_MODEL(summaryview->store), iter__, \ + S_COL_MSG_INFO, &msginfo, -1); \ +} + +#define SORT_BLOCK(key) \ + if (summaryview->folder_item->sort_key == key) { \ + sort_key = key; \ + sort_type = summaryview->folder_item->sort_type; \ + summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING); \ + } + +#define SORT_UNBLOCK(key) \ + if (sort_key == key) \ + summary_sort(summaryview, sort_key, sort_type); + +#define SUMMARY_DISPLAY_TOTAL_NUM(item) \ + (summaryview->on_filter ? summaryview->flt_msg_total : item->total) + +#ifdef G_OS_WIN32 +# define SUMMARY_COL_MARK_WIDTH 23 +# define SUMMARY_COL_UNREAD_WIDTH 26 +# define SUMMARY_COL_MIME_WIDTH 21 +#else +# define SUMMARY_COL_MARK_WIDTH 21 +# define SUMMARY_COL_UNREAD_WIDTH 24 +# define SUMMARY_COL_MIME_WIDTH 19 +#endif + +static GdkPixbuf *mark_pixbuf; +static GdkPixbuf *deleted_pixbuf; + +static GdkPixbuf *mail_pixbuf; +static GdkPixbuf *new_pixbuf; +static GdkPixbuf *unread_pixbuf; +static GdkPixbuf *replied_pixbuf; +static GdkPixbuf *forwarded_pixbuf; + +static GdkPixbuf *clip_pixbuf; +static GdkPixbuf *html_pixbuf; + +static void summary_clear_list_full (SummaryView *summaryview, + gboolean is_refresh); + +static GList *summary_get_selected_rows (SummaryView *summaryview); +static void summary_selection_list_free (SummaryView *summaryview); + +static GSList *summary_get_tmp_marked_msg_list + (SummaryView *summaryview); +static void summary_restore_tmp_marks (SummaryView *summaryview, + GSList *save_mark_mlist); + +static void summary_update_msg_list (SummaryView *summaryview); + +static void summary_msgid_table_create (SummaryView *summaryview); +static void summary_msgid_table_destroy (SummaryView *summaryview); + +static void summary_set_menu_sensitive (SummaryView *summaryview); + +static void summary_scroll_to_selected (SummaryView *summaryview, + gboolean align_center); + +static MsgInfo *summary_get_msginfo (SummaryView *summaryview, + GtkTreeRowReference *row); +static guint summary_get_msgnum (SummaryView *summaryview, + GtkTreeRowReference *row); + +static gboolean summary_find_prev_msg (SummaryView *summaryview, + GtkTreeIter *prev, + GtkTreeIter *iter); +static gboolean summary_find_next_msg (SummaryView *summaryview, + GtkTreeIter *next, + GtkTreeIter *iter); + +static gboolean summary_find_nearest_msg(SummaryView *summaryview, + GtkTreeIter *target, + GtkTreeIter *iter); + +static gboolean summary_find_prev_flagged_msg + (SummaryView *summaryview, + GtkTreeIter *prev, + GtkTreeIter *iter, + MsgPermFlags flags, + gboolean start_from_prev); +static gboolean summary_find_next_flagged_msg + (SummaryView *summaryview, + GtkTreeIter *next, + GtkTreeIter *iter, + MsgPermFlags flags, + gboolean start_from_next); + +static gboolean summary_find_msg_by_msgnum + (SummaryView *summaryview, + guint msgnum, + GtkTreeIter *found); + +static void summary_update_display_state(SummaryView *summaryview, + guint disp_msgnum, + guint sel_msgnum); + +static void summary_update_status (SummaryView *summaryview); + +/* display functions */ +static void summary_status_show (SummaryView *summaryview); +static void summary_set_row (SummaryView *summaryview, + GtkTreeIter *iter, + MsgInfo *msginfo); +static void summary_set_tree_model_from_list + (SummaryView *summaryview, + GSList *mlist); +static gboolean summary_row_is_displayed(SummaryView *summaryview, + GtkTreeIter *iter); +static void summary_display_msg (SummaryView *summaryview, + GtkTreeIter *iter); +static void summary_display_msg_full (SummaryView *summaryview, + GtkTreeIter *iter, + gboolean new_window, + gboolean all_headers, + gboolean redisplay); + +static void summary_activate_selected (SummaryView *summaryview); + +/* message handling */ +static void summary_mark_row (SummaryView *summaryview, + GtkTreeIter *iter); +static void summary_mark_row_as_read (SummaryView *summaryview, + GtkTreeIter *iter); +static void summary_mark_row_as_unread (SummaryView *summaryview, + GtkTreeIter *iter); +static void summary_delete_row (SummaryView *summaryview, + GtkTreeIter *iter); +static void summary_unmark_row (SummaryView *summaryview, + GtkTreeIter *iter); +static void summary_move_row_to (SummaryView *summaryview, + GtkTreeIter *iter, + FolderItem *to_folder); +static void summary_copy_row_to (SummaryView *summaryview, + GtkTreeIter *iter, + FolderItem *to_folder); + +static void summary_remove_invalid_messages + (SummaryView *summaryview); + +static gint summary_execute_move (SummaryView *summaryview); +static gint summary_execute_copy (SummaryView *summaryview); +static gint summary_execute_delete (SummaryView *summaryview); + +static void summary_modify_threads (SummaryView *summaryview); + +static void summary_colorlabel_menu_item_activate_cb + (GtkWidget *widget, + gpointer data); +static void summary_colorlabel_menu_item_activate_item_cb + (GtkMenuItem *label_menu_item, + gpointer data); +static void summary_colorlabel_menu_create + (SummaryView *summaryview); + +static GtkWidget *summary_tree_view_create + (SummaryView *summaryview); + +/* callback functions */ +static gboolean summary_toggle_pressed (GtkWidget *eventbox, + GdkEventButton *event, + SummaryView *summaryview); +static gboolean summary_button_pressed (GtkWidget *treeview, + GdkEventButton *event, + SummaryView *summaryview); +static gboolean summary_button_released (GtkWidget *treeview, + GdkEventButton *event, + SummaryView *summaryview); +static gboolean summary_key_pressed (GtkWidget *treeview, + GdkEventKey *event, + SummaryView *summaryview); + +static void summary_row_expanded (GtkTreeView *treeview, + GtkTreeIter *iter, + GtkTreePath *path, + SummaryView *summaryview); +static void summary_row_collapsed (GtkTreeView *treeview, + GtkTreeIter *iter, + GtkTreePath *path, + SummaryView *summaryview); + +static void summary_columns_changed (GtkTreeView *treeview, + SummaryView *summaryview); + +static gboolean summary_select_func (GtkTreeSelection *selection, + GtkTreeModel *model, + GtkTreePath *path, + gboolean cur_selected, + gpointer data); + +static void summary_selection_changed (GtkTreeSelection *selection, + SummaryView *summaryview); + +static void summary_col_resized (GtkWidget *widget, + GtkAllocation *allocation, + SummaryView *summaryview); + +static void summary_reply_cb (SummaryView *summaryview, + guint action, + GtkWidget *widget); +static void summary_show_all_header_cb (SummaryView *summaryview, + guint action, + GtkWidget *widget); + +static void summary_add_address_cb (SummaryView *summaryview, + guint action, + GtkWidget *widget); +static void summary_create_filter_cb (SummaryView *summaryview, + guint action, + GtkWidget *widget); + +static void summary_column_clicked (GtkWidget *button, + SummaryView *summaryview); + +static gboolean summary_column_drop_func(GtkTreeView *treeview, + GtkTreeViewColumn *column, + GtkTreeViewColumn *prev_column, + GtkTreeViewColumn *next_column, + gpointer data); + +static void summary_drag_begin (GtkWidget *widget, + GdkDragContext *drag_context, + SummaryView *summaryview); +static void summary_drag_end (GtkWidget *widget, + GdkDragContext *drag_context, + SummaryView *summaryview); +static void summary_drag_data_get (GtkWidget *widget, + GdkDragContext *drag_context, + GtkSelectionData *selection_data, + guint info, + guint time, + SummaryView *summaryview); + +static void summary_text_adj_value_changed + (GtkAdjustment *adj, + SummaryView *summaryview); + +/* custom compare functions for sorting */ +static gint summary_cmp_by_mark (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint summary_cmp_by_unread (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint summary_cmp_by_mime (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint summary_cmp_by_num (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint summary_cmp_by_size (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint summary_cmp_by_date (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint summary_cmp_by_thread_date (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint summary_cmp_by_from (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint summary_cmp_by_label (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint summary_cmp_by_to (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); +static gint summary_cmp_by_subject (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data); + + +/* must be synched with FolderSortKey */ +static SummaryColumnType sort_key_to_col[] = { + -1, + S_COL_NUMBER, + S_COL_SIZE, + S_COL_DATE, + S_COL_TDATE, + S_COL_FROM, + S_COL_SUBJECT, + -1, + S_COL_LABEL, + S_COL_MARK, + S_COL_UNREAD, + S_COL_MIME, + S_COL_TO +}; + +/* must be synched with SummaryColumnType */ +static FolderSortKey col_to_sort_key[] = { + SORT_BY_MARK, + SORT_BY_UNREAD, + SORT_BY_MIME, + SORT_BY_SUBJECT, + SORT_BY_FROM, + SORT_BY_DATE, + SORT_BY_SIZE, + SORT_BY_NUMBER, + SORT_BY_TO +}; + +enum +{ + DRAG_TYPE_TEXT, + DRAG_TYPE_RFC822, + DRAG_TYPE_URI_LIST, + + N_DRAG_TYPES +}; + +static GtkTargetEntry summary_drag_types[] = +{ + {"text/plain", GTK_TARGET_SAME_APP, DRAG_TYPE_TEXT}, + {"message/rfc822", GTK_TARGET_SAME_APP, DRAG_TYPE_RFC822}, + {"text/uri-list", 0, DRAG_TYPE_URI_LIST} +}; + +static GtkItemFactoryEntry summary_popup_entries[] = +{ + {N_("/_Reply"), NULL, summary_reply_cb, COMPOSE_REPLY, NULL}, + {N_("/Repl_y to"), NULL, NULL, 0, ""}, + {N_("/Repl_y to/_all"), NULL, summary_reply_cb, COMPOSE_REPLY_TO_ALL, NULL}, + {N_("/Repl_y to/_sender"), NULL, summary_reply_cb, COMPOSE_REPLY_TO_SENDER, NULL}, + {N_("/Repl_y to/mailing _list"), + NULL, summary_reply_cb, COMPOSE_REPLY_TO_LIST, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Forward"), NULL, summary_reply_cb, COMPOSE_FORWARD, NULL}, + {N_("/For_ward as attachment"), NULL, summary_reply_cb, COMPOSE_FORWARD_AS_ATTACH, NULL}, + {N_("/Redirec_t"), NULL, summary_reply_cb, COMPOSE_REDIRECT, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/M_ove..."), NULL, summary_move_to, 0, NULL}, + {N_("/_Copy..."), NULL, summary_copy_to, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Mark"), NULL, NULL, 0, ""}, + {N_("/_Mark/Set _flag"), NULL, summary_mark, 0, NULL}, + {N_("/_Mark/_Unset flag"), NULL, summary_unmark, 0, NULL}, + {N_("/_Mark/---"), NULL, NULL, 0, ""}, + {N_("/_Mark/Mark as unr_ead"), NULL, summary_mark_as_unread, 0, NULL}, + {N_("/_Mark/Mark as rea_d"), + NULL, summary_mark_as_read, 0, NULL}, + {N_("/_Mark/Mark _thread as read"), + NULL, summary_mark_thread_as_read, 0, NULL}, + {N_("/_Mark/Mark all _read"), NULL, summary_mark_all_read, 0, NULL}, + {N_("/Color la_bel"), NULL, NULL, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Delete"), NULL, summary_delete, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/Set as _junk mail"), NULL, summary_junk, 0, NULL}, + {N_("/Set as not j_unk mail"), NULL, summary_not_junk, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/Re-_edit"), NULL, summary_reedit, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/Add sender to address boo_k..."), + NULL, summary_add_address_cb, 0, NULL}, + {N_("/Create f_ilter rule"), NULL, NULL, 0, ""}, + {N_("/Create f_ilter rule/_Automatically"), + NULL, summary_create_filter_cb, FLT_BY_AUTO, NULL}, + {N_("/Create f_ilter rule/by _From"), + NULL, summary_create_filter_cb, FLT_BY_FROM, NULL}, + {N_("/Create f_ilter rule/by _To"), + NULL, summary_create_filter_cb, FLT_BY_TO, NULL}, + {N_("/Create f_ilter rule/by _Subject"), + NULL, summary_create_filter_cb, FLT_BY_SUBJECT, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_View"), NULL, NULL, 0, ""}, + {N_("/_View/Open in new _window"), + NULL, summary_open_msg, 0, NULL}, + {N_("/_View/Mess_age source"), NULL, summary_view_source, 0, NULL}, + {N_("/_View/All _headers"), NULL, summary_show_all_header_cb, 0, ""}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Print..."), NULL, summary_print, 0, NULL} +}; + +SummaryView *summary_create(void) +{ + SummaryView *summaryview; + GtkWidget *vbox; + GtkWidget *scrolledwin; + GtkWidget *treeview; + GtkTreeStore *store; + GtkTreeSelection *selection; + GtkWidget *hseparator; + GtkWidget *hbox; + GtkWidget *statlabel_folder; + GtkWidget *statlabel_select; + GtkWidget *statlabel_msgs; + GtkWidget *toggle_eventbox; + GtkWidget *toggle_arrow; + GtkTooltips *tip; + GtkWidget *popupmenu; + GtkItemFactory *popupfactory; + gint n_entries; + GList *child; + + debug_print(_("Creating summary view...\n")); + summaryview = g_new0(SummaryView, 1); + + vbox = gtk_vbox_new(FALSE, 1); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0); + gtk_widget_set_size_request(vbox, + prefs_common.summaryview_width, + prefs_common.summaryview_height); + + treeview = summary_tree_view_create(summaryview); + gtk_container_add(GTK_CONTAINER(scrolledwin), treeview); + + store = GTK_TREE_STORE + (gtk_tree_view_get_model(GTK_TREE_VIEW(treeview))); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + + /* create status label */ + hseparator = gtk_hseparator_new(); + gtk_box_pack_end(GTK_BOX(vbox), hseparator, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + statlabel_folder = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(hbox), statlabel_folder, FALSE, FALSE, 2); + statlabel_select = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(hbox), statlabel_select, FALSE, FALSE, 12); + + /* toggle view button */ + toggle_eventbox = gtk_event_box_new(); + gtk_box_pack_end(GTK_BOX(hbox), toggle_eventbox, FALSE, FALSE, 4); + toggle_arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT); + gtk_container_add(GTK_CONTAINER(toggle_eventbox), toggle_arrow); + g_signal_connect(G_OBJECT(toggle_eventbox), "button_press_event", + G_CALLBACK(summary_toggle_pressed), summaryview); + tip = gtk_tooltips_new(); + gtk_tooltips_set_tip(tip, toggle_eventbox, _("Toggle message view"), + NULL); + + statlabel_msgs = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(statlabel_msgs), 1, 0.5); +#if GTK_CHECK_VERSION(2, 6, 0) + gtk_label_set_ellipsize(GTK_LABEL(statlabel_msgs), + PANGO_ELLIPSIZE_START); +#endif + gtk_box_pack_start(GTK_BOX(hbox), statlabel_msgs, TRUE, TRUE, 2); + + /* create popup menu */ + n_entries = sizeof(summary_popup_entries) / + sizeof(summary_popup_entries[0]); + popupmenu = menu_create_items(summary_popup_entries, n_entries, + "", &popupfactory, + summaryview); + + summaryview->vbox = vbox; + summaryview->scrolledwin = scrolledwin; + summaryview->treeview = treeview; + summaryview->store = store; + summaryview->selection = selection; + summaryview->hseparator = hseparator; + summaryview->hbox = hbox; + summaryview->statlabel_folder = statlabel_folder; + summaryview->statlabel_select = statlabel_select; + summaryview->statlabel_msgs = statlabel_msgs; + summaryview->toggle_eventbox = toggle_eventbox; + summaryview->toggle_arrow = toggle_arrow; + summaryview->popupmenu = popupmenu; + summaryview->popupfactory = popupfactory; + summaryview->lock_count = 0; + + summaryview->reedit_menuitem = + gtk_item_factory_get_widget(popupfactory, "/Re-edit"); + child = g_list_find(GTK_MENU_SHELL(popupmenu)->children, + summaryview->reedit_menuitem); + summaryview->reedit_separator = GTK_WIDGET(child->next->data); + + summaryview->junk_menuitem = + gtk_item_factory_get_widget(popupfactory, "/Set as junk mail"); + summaryview->nojunk_menuitem = + gtk_item_factory_get_widget(popupfactory, + "/Set as not junk mail"); + child = g_list_find(GTK_MENU_SHELL(popupmenu)->children, + summaryview->nojunk_menuitem); + summaryview->junk_separator = GTK_WIDGET(child->next->data); + + gtk_widget_show_all(vbox); + + return summaryview; +} + +void summary_init(SummaryView *summaryview) +{ + GtkWidget *pixmap; + PangoFontDescription *font_desc; + gint size; + TextView *textview; + GtkAdjustment *adj; + + stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_MARK, + &mark_pixbuf); + stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_DELETED, + &deleted_pixbuf); + stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_MAIL_SMALL, + &mail_pixbuf); + stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_NEW, + &new_pixbuf); + stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_UNREAD, + &unread_pixbuf); + stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_REPLIED, + &replied_pixbuf); + stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_FORWARDED, + &forwarded_pixbuf); + stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_CLIP, + &clip_pixbuf); + stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_HTML, + &html_pixbuf); + + font_desc = pango_font_description_new(); + size = pango_font_description_get_size + (summaryview->statlabel_folder->style->font_desc); + pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL); + gtk_widget_modify_font(summaryview->statlabel_folder, font_desc); + gtk_widget_modify_font(summaryview->statlabel_select, font_desc); + gtk_widget_modify_font(summaryview->statlabel_msgs, font_desc); + pango_font_description_free(font_desc); + + pixmap = stock_pixbuf_widget(summaryview->hbox, + STOCK_PIXMAP_FOLDER_OPEN); + gtk_box_pack_start(GTK_BOX(summaryview->hbox), pixmap, FALSE, FALSE, 4); + gtk_box_reorder_child(GTK_BOX(summaryview->hbox), pixmap, 0); + gtk_widget_show(pixmap); + + summary_clear_list(summaryview); + summary_set_column_order(summaryview); + summary_colorlabel_menu_create(summaryview); + summary_set_menu_sensitive(summaryview); + + textview = summaryview->messageview->textview; + adj = GTK_TEXT_VIEW(textview->text)->vadjustment; + g_signal_connect(adj, "value-changed", + G_CALLBACK(summary_text_adj_value_changed), + summaryview); + textview = summaryview->messageview->mimeview->textview; + adj = GTK_TEXT_VIEW(textview->text)->vadjustment; + g_signal_connect(adj, "value-changed", + G_CALLBACK(summary_text_adj_value_changed), + summaryview); +} + +static void get_msg_list_func(Folder *folder, FolderItem *item, gpointer data) +{ + SummaryView *summary = (SummaryView *)folder->data; + gint count = GPOINTER_TO_INT(data); + static GTimeVal tv_prev = {0, 0}; + GTimeVal tv_cur; + + g_get_current_time(&tv_cur); + + if (tv_prev.tv_sec == 0 || + (tv_cur.tv_sec - tv_prev.tv_sec) * G_USEC_PER_SEC + + tv_cur.tv_usec - tv_prev.tv_usec > 100 * 1000) { + gchar buf[256]; + + g_snprintf(buf, sizeof(buf), _("Scanning folder (%s) (%d)..."), + item->path, count); + STATUSBAR_POP(summary->mainwin); + STATUSBAR_PUSH(summary->mainwin, buf); + tv_prev = tv_cur; + } +} + +gboolean summary_show(SummaryView *summaryview, FolderItem *item, + gboolean update_cache) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview); + GtkTreeIter iter; + GSList *mlist; + gchar *buf; + gboolean is_refresh; + guint selected_msgnum = 0; + guint displayed_msgnum = 0; + gboolean moved; + gboolean selection_done = FALSE; + gboolean do_qsearch = FALSE; + gboolean set_column_order_required = FALSE; + const gchar *key = NULL; + gpointer save_data; + GSList *save_mark_mlist = NULL; + + if (summary_is_locked(summaryview)) return FALSE; + + inc_lock(); + summary_lock(summaryview); + + STATUSBAR_POP(summaryview->mainwin); + + is_refresh = (item == summaryview->folder_item && + update_cache == FALSE) ? TRUE : FALSE; + selected_msgnum = summary_get_msgnum(summaryview, + summaryview->selected); + displayed_msgnum = summary_get_msgnum(summaryview, + summaryview->displayed); + if (summaryview->folder_item) + summaryview->folder_item->last_selected = selected_msgnum; + if (!is_refresh) { + if (item) + selected_msgnum = item->last_selected; + else + selected_msgnum = 0; + displayed_msgnum = 0; + } + + /* process the marks if any */ + if (summaryview->mainwin->lock_count == 0 && !is_refresh && + (summaryview->moved > 0 || summaryview->copied > 0)) { + AlertValue val; + + val = alertpanel(_("Process mark"), + _("Some marks are left. Process it?"), + GTK_STOCK_YES, GTK_STOCK_NO, GTK_STOCK_CANCEL); + if (G_ALERTDEFAULT == val) { + summary_unlock(summaryview); + summary_execute(summaryview); + summary_lock(summaryview); + GTK_EVENTS_FLUSH(); + } else if (G_ALERTALTERNATE == val) { + summary_write_cache(summaryview); + GTK_EVENTS_FLUSH(); + } else { + summary_unlock(summaryview); + inc_unlock(); + return FALSE; + } + } else { + /* save temporary move/copy marks */ + if (is_refresh && + (summaryview->moved > 0 || summaryview->copied > 0)) + save_mark_mlist = summary_get_tmp_marked_msg_list(summaryview); + + summary_write_cache(summaryview); + } + + if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item) != + FOLDER_ITEM_IS_SENT_FOLDER(item)) + set_column_order_required = TRUE; + + folderview_set_opened_item(summaryview->folderview, item); + + summary_clear_list_full(summaryview, is_refresh); + + buf = NULL; + if (!item || !item->path || !item->parent || item->no_select || + (FOLDER_TYPE(item->folder) == F_MH && item->stype != F_VIRTUAL && + ((buf = folder_item_get_path(item)) == NULL || + change_dir(buf) < 0))) { + g_free(buf); + debug_print("empty folder\n\n"); + summary_clear_all(summaryview); + summaryview->folder_item = item; + if (item) + item->qsearch_cond_type = QS_ALL; + if (set_column_order_required) + summary_set_column_order(summaryview); + if (save_mark_mlist) + procmsg_msg_list_free(save_mark_mlist); + summary_unlock(summaryview); + inc_unlock(); + return TRUE; + } + g_free(buf); + + if (!is_refresh) + messageview_clear(summaryview->messageview); + + summaryview->folder_item = item; + if (set_column_order_required) + summary_set_column_order(summaryview); + + g_signal_handlers_block_matched(G_OBJECT(treeview), + (GSignalMatchType)G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, summaryview); + + buf = g_strdup_printf(_("Scanning folder (%s)..."), item->path); + debug_print("%s\n", buf); + STATUSBAR_PUSH(summaryview->mainwin, buf); + g_free(buf); + + main_window_cursor_wait(summaryview->mainwin); + + save_data = item->folder->data; + item->folder->data = summaryview; + folder_set_ui_func(item->folder, get_msg_list_func, NULL); + + mlist = folder_item_get_msg_list(item, !update_cache); + + folder_set_ui_func(item->folder, NULL, NULL); + item->folder->data = save_data; + + statusbar_pop_all(); + STATUSBAR_POP(summaryview->mainwin); + + summaryview->all_mlist = mlist; + + /* restore temporary move/copy marks */ + if (save_mark_mlist) { + summary_restore_tmp_marks(summaryview, save_mark_mlist); + save_mark_mlist = NULL; + } + + /* set tree store and hash table from the msginfo list, and + create the thread */ + if (prefs_common.show_searchbar && + (prefs_common.persist_qsearch_filter || is_refresh)) { + if (item->qsearch_cond_type > QS_ALL) + do_qsearch = TRUE; + if (is_refresh && summaryview->qsearch->entry_entered) { + key = gtk_entry_get_text + (GTK_ENTRY(summaryview->qsearch->entry)); + if (key && *key != '\0') + do_qsearch = TRUE; + else + key = NULL; + } + } + + if (do_qsearch) { + gint index; + QSearchCondType type = item->qsearch_cond_type; + + index = menu_find_option_menu_index + (GTK_OPTION_MENU(summaryview->qsearch->optmenu), + GINT_TO_POINTER(type), NULL); + if (index > 0) { + gtk_option_menu_set_history + (GTK_OPTION_MENU(summaryview->qsearch->optmenu), + index); + } else { + gtk_option_menu_set_history + (GTK_OPTION_MENU(summaryview->qsearch->optmenu), + 0); + type = QS_ALL; + } + + if (type > QS_ALL || key) { + summaryview->flt_mlist = + quick_search_filter(summaryview->qsearch, + type, key); + summaryview->on_filter = TRUE; + summary_set_tree_model_from_list + (summaryview, summaryview->flt_mlist); + summary_update_status(summaryview); + } else { + item->qsearch_cond_type = QS_ALL; + summary_set_tree_model_from_list(summaryview, mlist); + } + } else { + item->qsearch_cond_type = QS_ALL; + summary_set_tree_model_from_list(summaryview, mlist); + } + + if (mlist) + gtk_widget_grab_focus(GTK_WIDGET(treeview)); + + summary_write_cache(summaryview); + + item->opened = TRUE; + + g_signal_handlers_unblock_matched(G_OBJECT(treeview), + (GSignalMatchType)G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, summaryview); + + if (is_refresh) { + summary_update_display_state(summaryview, displayed_msgnum, + selected_msgnum); + + if (!summaryview->selected) { + /* no selected message - select first unread + message, but do not display it */ + if (summary_find_next_flagged_msg + (summaryview, &iter, NULL, MSG_UNREAD, FALSE)) { + summary_select_row(summaryview, &iter, + FALSE, TRUE); + } else if (item->sort_type == SORT_ASCENDING && + SUMMARY_DISPLAY_TOTAL_NUM(item) > 1) { + g_signal_emit_by_name + (treeview, "move-cursor", + GTK_MOVEMENT_BUFFER_ENDS, 1, &moved); + GTK_EVENTS_FLUSH(); + summary_scroll_to_selected(summaryview, TRUE); + } else if (gtk_tree_model_get_iter_first + (GTK_TREE_MODEL(summaryview->store), + &iter)) { + summary_select_row(summaryview, &iter, + FALSE, TRUE); + } + } + selection_done = TRUE; + } else if (prefs_common.remember_last_selected) { + summary_unlock(summaryview); + summary_select_by_msgnum(summaryview, selected_msgnum); + summary_lock(summaryview); + + if (summaryview->selected) + selection_done = TRUE; + } + + if (!selection_done) { + /* select first unread message */ + if (summary_find_next_flagged_msg(summaryview, &iter, NULL, + MSG_UNREAD, FALSE)) { + if (prefs_common.open_unread_on_enter || + prefs_common.always_show_msg) { + summary_unlock(summaryview); + summary_select_row(summaryview, &iter, + TRUE, TRUE); + summary_lock(summaryview); + } else { + summary_select_row(summaryview, &iter, + FALSE, TRUE); + } + } else { + summary_unlock(summaryview); + if (item->sort_type == SORT_ASCENDING && + SUMMARY_DISPLAY_TOTAL_NUM(item) > 1) { + g_signal_emit_by_name + (treeview, "move-cursor", + GTK_MOVEMENT_BUFFER_ENDS, 1, &moved); + } else if (gtk_tree_model_get_iter_first + (GTK_TREE_MODEL(summaryview->store), + &iter)) { + summary_select_row(summaryview, &iter, + FALSE, TRUE); + } + summary_lock(summaryview); + GTK_EVENTS_FLUSH(); + summary_scroll_to_selected(summaryview, TRUE); + } + } + + summary_status_show(summaryview); + summary_set_menu_sensitive(summaryview); + main_window_set_toolbar_sensitive(summaryview->mainwin); + + debug_print("\n"); + STATUSBAR_PUSH(summaryview->mainwin, _("Done.")); + + main_window_cursor_normal(summaryview->mainwin); + + if (prefs_common.online_mode) { + if (FOLDER_IS_REMOTE(item->folder) && + REMOTE_FOLDER(item->folder)->session == NULL) { + alertpanel_error(_("Could not establish a connection to the server.")); + } + } + + summary_unlock(summaryview); + inc_unlock(); + + return TRUE; +} + +static void summary_unset_sort_column_id(SummaryView *summaryview) +{ + gint id; + GtkSortType order; + + if (gtk_tree_sortable_get_sort_column_id + (GTK_TREE_SORTABLE(summaryview->store), &id, &order) && + id >= 0 && id < N_SUMMARY_VISIBLE_COLS) { + GtkTreeViewColumn *column = summaryview->columns[id]; + column->sort_column_id = -1; + gtk_tree_view_column_set_sort_indicator(column, FALSE); + } + + gtkut_tree_sortable_unset_sort_column_id + (GTK_TREE_SORTABLE(summaryview->store)); +} + +void summary_clear_list(SummaryView *summaryview) +{ + summary_clear_list_full(summaryview, FALSE); +} + +static void summary_clear_list_full(SummaryView *summaryview, + gboolean is_refresh) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview); + GtkAdjustment *adj; + + if (summaryview->folder_item) { + folder_item_close(summaryview->folder_item); + summaryview->folder_item = NULL; + } + + summaryview->display_msg = FALSE; + + summaryview->selected = NULL; + summaryview->displayed = NULL; + + summary_selection_list_free(summaryview); + + summaryview->total_size = 0; + summaryview->deleted = summaryview->moved = 0; + summaryview->copied = 0; + + summary_msgid_table_destroy(summaryview); + + summaryview->tmp_mlist = NULL; + summaryview->to_folder = NULL; + if (summaryview->folder_table) { + g_hash_table_destroy(summaryview->folder_table); + summaryview->folder_table = NULL; + } + summaryview->filtered = 0; + summaryview->flt_count = 0; + summaryview->flt_total = 0; + + summaryview->on_button_press = FALSE; + summaryview->can_toggle_selection = TRUE; + summaryview->on_drag = FALSE; + if (summaryview->pressed_path) { + gtk_tree_path_free(summaryview->pressed_path); + summaryview->pressed_path = NULL; + } + if (summaryview->drag_list) { + g_free(summaryview->drag_list); + summaryview->drag_list = NULL; + } + + if (summaryview->flt_mlist) { + g_slist_free(summaryview->flt_mlist); + summaryview->flt_mlist = NULL; + } + summaryview->total_flt_msg_size = 0; + summaryview->flt_msg_total = 0; + summaryview->flt_deleted = summaryview->flt_moved = 0; + summaryview->flt_copied = 0; + summaryview->flt_new = summaryview->flt_unread = 0; + if (!is_refresh) { + quick_search_clear_entry(summaryview->qsearch); + gtk_option_menu_set_history + (GTK_OPTION_MENU(summaryview->qsearch->optmenu), 0); + } + summaryview->on_filter = FALSE; + + procmsg_msg_list_free(summaryview->all_mlist); + summaryview->all_mlist = NULL; + + gtkut_tree_view_fast_clear(treeview, summaryview->store); + + /* ensure that the "value-changed" signal is always emitted */ + adj = gtk_tree_view_get_vadjustment(treeview); + adj->value = 0.0; + + summary_unset_sort_column_id(summaryview); +} + +void summary_clear_all(SummaryView *summaryview) +{ + messageview_clear(summaryview->messageview); + summary_clear_list(summaryview); + summary_set_menu_sensitive(summaryview); + main_window_set_toolbar_sensitive(summaryview->mainwin); + summary_status_show(summaryview); +} + +void summary_show_queued_msgs(SummaryView *summaryview) +{ + FolderItem *item; + GSList *qlist, *cur; + MsgInfo *msginfo; + GtkTreeStore *store = GTK_TREE_STORE(summaryview->store); + GtkTreeIter iter; + + if (summary_is_locked(summaryview)) + return; + + item = summaryview->folder_item; + if (!item || !item->path || !item->cache_queue || + item->stype == F_VIRTUAL) + return; + + debug_print("summary_show_queued_msgs: appending queued messages to summary (%s)\n", item->path); + + qlist = g_slist_reverse(item->cache_queue); + item->cache_queue = NULL; + if (item->mark_queue) { + procmsg_flaginfo_list_free(item->mark_queue); + item->mark_queue = NULL; + } + + for (cur = qlist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + + debug_print("summary_show_queued_msgs: appending msg %u\n", + msginfo->msgnum); + msginfo->folder = item; + gtk_tree_store_append(store, &iter, NULL); + summary_set_row(summaryview, &iter, msginfo); + + if (cur == qlist) { + GtkTreePath *path; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), + &iter); + gtk_tree_view_scroll_to_cell + (GTK_TREE_VIEW(summaryview->treeview), path, + NULL, FALSE, 0.0, 0.0); + gtk_tree_path_free(path); + } + + summaryview->total_size += msginfo->size; + } + + summaryview->all_mlist = g_slist_concat(summaryview->all_mlist, qlist); + + item->cache_dirty = TRUE; + summary_selection_list_free(summaryview); + + summary_status_show(summaryview); + + debug_print("summary_show_queued_msgs: done.\n"); +} + +void summary_lock(SummaryView *summaryview) +{ + summaryview->lock_count++; + summaryview->write_lock_count++; + /* g_print("summary_lock: %d\n", summaryview->lock_count); */ +} + +void summary_unlock(SummaryView *summaryview) +{ + /* g_print("summary_unlock: %d\n", summaryview->lock_count); */ + if (summaryview->lock_count) + summaryview->lock_count--; + if (summaryview->write_lock_count) + summaryview->write_lock_count--; +} + +gboolean summary_is_locked(SummaryView *summaryview) +{ + return summaryview->lock_count > 0 || summaryview->write_lock_count > 0; +} + +gboolean summary_is_read_locked(SummaryView *summaryview) +{ + return summaryview->lock_count > 0; +} + +void summary_write_lock(SummaryView *summaryview) +{ + summaryview->write_lock_count++; +} + +void summary_write_unlock(SummaryView *summaryview) +{ + if (summaryview->write_lock_count) + summaryview->write_lock_count--; +} + +gboolean summary_is_write_locked(SummaryView *summaryview) +{ + return summaryview->write_lock_count > 0; +} + +FolderItem *summary_get_current_folder(SummaryView *summaryview) +{ + return summaryview->folder_item; +} + +SummarySelection summary_get_selection_type(SummaryView *summaryview) +{ + SummarySelection selection; + GList *rows; + + rows = summary_get_selected_rows(summaryview); + + if (!summaryview->folder_item || summaryview->folder_item->total == 0) + selection = SUMMARY_NONE; + else if (!rows) + selection = SUMMARY_SELECTED_NONE; + else if (rows && !rows->next) + selection = SUMMARY_SELECTED_SINGLE; + else + selection = SUMMARY_SELECTED_MULTIPLE; + + return selection; +} + +static GList *summary_get_selected_rows(SummaryView *summaryview) +{ + if (!summaryview->selection_list) + summaryview->selection_list = + gtk_tree_selection_get_selected_rows + (summaryview->selection, NULL); + + return summaryview->selection_list; +} + +static void summary_selection_list_free(SummaryView *summaryview) +{ + if (summaryview->selection_list) { + g_list_foreach(summaryview->selection_list, + (GFunc)gtk_tree_path_free, NULL); + g_list_free(summaryview->selection_list); + summaryview->selection_list = NULL; + } +} + +GSList *summary_get_selected_msg_list(SummaryView *summaryview) +{ + GSList *mlist = NULL; + GList *rows; + GList *cur; + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter; + MsgInfo *msginfo; + + rows = summary_get_selected_rows(summaryview); + for (cur = rows; cur != NULL; cur = cur->next) { + gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)cur->data); + gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1); + mlist = g_slist_prepend(mlist, msginfo); + } + + mlist = g_slist_reverse(mlist); + + return mlist; +} + +GSList *summary_get_changed_msg_list(SummaryView *summaryview) +{ + MsgInfo *msginfo; + GSList *mlist = NULL; + GSList *cur; + + for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + if (MSG_IS_FLAG_CHANGED(msginfo->flags)) + mlist = g_slist_prepend(mlist, msginfo); + } + + return g_slist_reverse(mlist); +} + +GSList *summary_get_msg_list(SummaryView *summaryview) +{ + if (summaryview->on_filter) + return g_slist_copy(summaryview->flt_mlist); + else + return g_slist_copy(summaryview->all_mlist); +} + +GSList *summary_get_flagged_msg_list(SummaryView *summaryview, + MsgPermFlags flags) +{ + MsgInfo *msginfo; + GSList *list, *cur; + GSList *mlist = NULL; + + if (summaryview->on_filter) + list = summaryview->flt_mlist; + else + list = summaryview->all_mlist; + + for (cur = list; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + if ((msginfo->flags.perm_flags & flags) != 0) + mlist = g_slist_prepend(mlist, msginfo); + } + + return g_slist_reverse(mlist); +} + +/* return list of copied MsgInfo */ +static GSList *summary_get_tmp_marked_msg_list(SummaryView *summaryview) +{ + MsgInfo *msginfo, *markinfo; + GSList *mlist = NULL; + GSList *cur; + + for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + if (MSG_IS_MOVE(msginfo->flags) || + MSG_IS_COPY(msginfo->flags)) { + markinfo = g_new0(MsgInfo, 1); + markinfo->msgnum = msginfo->msgnum; + markinfo->flags = msginfo->flags; + markinfo->folder = msginfo->folder; + markinfo->to_folder = msginfo->to_folder; + mlist = g_slist_prepend(mlist, markinfo); + } + } + + return g_slist_reverse(mlist); +} + +static void summary_restore_tmp_marks(SummaryView *summaryview, + GSList *save_mark_mlist) +{ + GSList *cur, *scur; + MsgInfo *msginfo, *markinfo; + + debug_print("summary_restore_tmp_marks: restoring temporary marks\n"); + + for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + for (scur = save_mark_mlist; scur != NULL; scur = scur->next) { + markinfo = (MsgInfo *)scur->data; + if (msginfo->msgnum == markinfo->msgnum && + msginfo->folder == markinfo->folder) { + msginfo->flags.tmp_flags |= (markinfo->flags.tmp_flags & (MSG_MOVE|MSG_COPY)); + msginfo->to_folder = markinfo->to_folder; + save_mark_mlist = g_slist_remove + (save_mark_mlist, markinfo); + g_free(markinfo); + if (!save_mark_mlist) + return; + break; + } + } + } + + if (save_mark_mlist) + procmsg_msg_list_free(save_mark_mlist); +} + +static void summary_update_msg_list(SummaryView *summaryview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter; + GSList *mlist = NULL; + MsgInfo *msginfo; + gboolean valid; + + if (summaryview->on_filter) + return; + + g_slist_free(summaryview->all_mlist); + summaryview->all_mlist = NULL; + + valid = gtk_tree_model_get_iter_first(model, &iter); + + while (valid) { + gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1); + mlist = g_slist_prepend(mlist, msginfo); + valid = gtkut_tree_model_next(model, &iter); + } + + summaryview->all_mlist = g_slist_reverse(mlist); + +} + +static gboolean summary_msgid_table_create_func(GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GHashTable *msgid_table = (GHashTable *)data; + MsgInfo *msginfo; + GtkTreeIter *iter_; + + gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); + + if (msginfo && !MSG_IS_INVALID(msginfo->flags) && + !MSG_IS_DELETED(msginfo->flags) && + msginfo->msgid && msginfo->msgid[0] != '\0') { + iter_ = gtk_tree_iter_copy(iter); + g_hash_table_replace(msgid_table, msginfo->msgid, iter_); + } + + return FALSE; +} + +static void summary_msgid_table_create(SummaryView *summaryview) +{ + GHashTable *msgid_table; + + g_return_if_fail(summaryview->msgid_table == NULL); + + msgid_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, + (GDestroyNotify)gtk_tree_iter_free); + + gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), + summary_msgid_table_create_func, msgid_table); + + summaryview->msgid_table = msgid_table; +} + +static void summary_msgid_table_destroy(SummaryView *summaryview) +{ + if (!summaryview->msgid_table) + return; + + g_hash_table_destroy(summaryview->msgid_table); + summaryview->msgid_table = NULL; +} + +static void summary_set_menu_sensitive(SummaryView *summaryview) +{ + GtkItemFactory *ifactory = summaryview->popupfactory; + SummarySelection selection; + GtkWidget *menuitem; + gboolean sens; + + selection = summary_get_selection_type(summaryview); + sens = (selection == SUMMARY_SELECTED_MULTIPLE) ? FALSE : TRUE; + + main_window_set_menu_sensitive(summaryview->mainwin); + + if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item)) { + gtk_widget_show(summaryview->reedit_menuitem); + gtk_widget_show(summaryview->reedit_separator); + menu_set_sensitive(ifactory, "/Re-edit", sens); + } else { + gtk_widget_hide(summaryview->reedit_menuitem); + gtk_widget_hide(summaryview->reedit_separator); + menu_set_sensitive(ifactory, "/Re-edit", FALSE); + } + + if (selection == SUMMARY_NONE) { + menu_set_insensitive_all + (GTK_MENU_SHELL(summaryview->popupmenu)); + return; + } + + if (summaryview->folder_item && + FOLDER_TYPE(summaryview->folder_item->folder) != F_NEWS) { + menu_set_sensitive(ifactory, "/Move...", TRUE); + menu_set_sensitive(ifactory, "/Delete", TRUE); + } else { + menu_set_sensitive(ifactory, "/Move...", FALSE); + menu_set_sensitive(ifactory, "/Delete", FALSE); + } + + menu_set_sensitive(ifactory, "/Copy...", TRUE); + + menu_set_sensitive(ifactory, "/Mark", TRUE); + menu_set_sensitive(ifactory, "/Mark/Set flag", TRUE); + menu_set_sensitive(ifactory, "/Mark/Unset flag", TRUE); + + menu_set_sensitive(ifactory, "/Mark/Mark as unread", TRUE); + menu_set_sensitive(ifactory, "/Mark/Mark as read", TRUE); + menu_set_sensitive(ifactory, "/Mark/Mark all read", TRUE); + + if (prefs_common.enable_junk) { + gtk_widget_show(summaryview->junk_menuitem); + gtk_widget_show(summaryview->nojunk_menuitem); + gtk_widget_show(summaryview->junk_separator); + menu_set_sensitive(ifactory, "/Set as junk mail", TRUE); + menu_set_sensitive(ifactory, "/Set as not junk mail", TRUE); + } else { + gtk_widget_hide(summaryview->junk_menuitem); + gtk_widget_hide(summaryview->nojunk_menuitem); + gtk_widget_hide(summaryview->junk_separator); + menu_set_sensitive(ifactory, "/Set as junk mail", FALSE); + menu_set_sensitive(ifactory, "/Set as not junk mail", FALSE); + } + + menu_set_sensitive(ifactory, "/Color label", TRUE); + + menu_set_sensitive(ifactory, "/Reply", sens); + menu_set_sensitive(ifactory, "/Reply to", sens); + menu_set_sensitive(ifactory, "/Reply to/all", sens); + menu_set_sensitive(ifactory, "/Reply to/sender", sens); + menu_set_sensitive(ifactory, "/Reply to/mailing list", sens); + menu_set_sensitive(ifactory, "/Forward", TRUE); + menu_set_sensitive(ifactory, "/Forward as attachment", TRUE); + menu_set_sensitive(ifactory, "/Redirect", sens); + + menu_set_sensitive(ifactory, "/Add sender to address book...", sens); + menu_set_sensitive(ifactory, "/Create filter rule", sens); + + menu_set_sensitive(ifactory, "/View", sens); + menu_set_sensitive(ifactory, "/View/Open in new window", sens); + menu_set_sensitive(ifactory, "/View/Message source", sens); + menu_set_sensitive(ifactory, "/View/All headers", sens); + + menu_set_sensitive(ifactory, "/Print...", TRUE); + + summary_lock(summaryview); + menuitem = gtk_item_factory_get_widget(ifactory, "/View/All headers"); + gtk_check_menu_item_set_active + (GTK_CHECK_MENU_ITEM(menuitem), + summaryview->messageview->textview->show_all_headers); + summary_unlock(summaryview); +} + +static void summary_select_prev_flagged(SummaryView *summaryview, + MsgPermFlags flags, + const gchar *title, + const gchar *ask_msg, + const gchar *notice) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter prev, iter; + gboolean start_from_prev = FALSE; + gboolean found; + + if (!gtkut_tree_row_reference_get_iter(model, summaryview->selected, + &iter)) + return; + + if (!messageview_is_visible(summaryview->messageview) || + summary_row_is_displayed(summaryview, &iter)) + start_from_prev = TRUE; + + found = summary_find_prev_flagged_msg + (summaryview, &prev, &iter, flags, start_from_prev); + + if (!found) { + AlertValue val; + + val = alertpanel(title, ask_msg, GTK_STOCK_YES, GTK_STOCK_NO, + NULL); + if (val != G_ALERTDEFAULT) return; + found = summary_find_prev_flagged_msg(summaryview, &prev, NULL, + flags, start_from_prev); + } + + if (!found) { + if (notice) + alertpanel_notice("%s", notice); + } else { + gboolean visible; + visible = messageview_is_visible(summaryview->messageview); + summary_select_row(summaryview, &prev, visible, FALSE); + if (visible) + summary_mark_displayed_read(summaryview, &prev); + } +} + +static void summary_select_next_flagged(SummaryView *summaryview, + MsgPermFlags flags, + const gchar *title, + const gchar *ask_msg, + const gchar *notice) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter next, iter; + gboolean start_from_next = FALSE; + gboolean found; + + if (!gtkut_tree_row_reference_get_iter(model, summaryview->selected, + &iter)) { + if (!gtk_tree_model_get_iter_first(model, &iter)) + return; + } + + if (!messageview_is_visible(summaryview->messageview) || + summary_row_is_displayed(summaryview, &iter)) + start_from_next = TRUE; + + found = summary_find_next_flagged_msg + (summaryview, &next, &iter, flags, start_from_next); + + if (!found) { + AlertValue val; + + val = alertpanel(title, ask_msg, GTK_STOCK_YES, GTK_STOCK_NO, + NULL); + if (val != G_ALERTDEFAULT) return; + found = summary_find_next_flagged_msg(summaryview, &next, NULL, + flags, start_from_next); + } + + if (!found) { + if (notice) + alertpanel_notice("%s", notice); + } else { + gboolean visible; + visible = messageview_is_visible(summaryview->messageview); + summary_select_row(summaryview, &next, visible, FALSE); + if (visible) + summary_mark_displayed_read(summaryview, &next); + } +} + +static void summary_select_next_flagged_or_folder(SummaryView *summaryview, + MsgPermFlags flags, + const gchar *title, + const gchar *ask_msg, + const gchar *notice) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter, next; + gboolean start_from_next = FALSE; + gboolean visible; + + if (!gtkut_tree_row_reference_get_iter(model, summaryview->selected, + &iter)) { + if (!gtk_tree_model_get_iter_first(model, &iter)) + return; + } + + if (!messageview_is_visible(summaryview->messageview) || + summary_row_is_displayed(summaryview, &iter)) + start_from_next = TRUE; + + while (summary_find_next_flagged_msg + (summaryview, &next, &iter, flags, start_from_next) == FALSE) { + AlertValue val; + + val = alertpanel(title, ask_msg, + GTK_STOCK_YES, GTK_STOCK_NO, + _("_Search again")); + + if (val == G_ALERTDEFAULT) { + folderview_select_next_unread(summaryview->folderview); + return; + } else if (val == G_ALERTOTHER) { + start_from_next = FALSE; + if (!gtk_tree_model_get_iter_first(model, &iter)) + return; + } else + return; + } + + visible = messageview_is_visible(summaryview->messageview); + summary_select_row(summaryview, &next, visible, FALSE); + if (visible) + summary_mark_displayed_read(summaryview, &next); +} + +void summary_select_prev_unread(SummaryView *summaryview) +{ + summary_select_prev_flagged(summaryview, MSG_UNREAD, + _("No more unread messages"), + _("No unread message found. " + "Search from the end?"), + _("No unread messages.")); +} + +void summary_select_next_unread(SummaryView *summaryview) +{ + summary_select_next_flagged_or_folder(summaryview, MSG_UNREAD, + _("No more unread messages"), + _("No unread message found. " + "Go to next unread folder?"), + NULL); +} + +void summary_select_prev_new(SummaryView *summaryview) +{ + summary_select_prev_flagged(summaryview, MSG_NEW, + _("No more new messages"), + _("No new message found. " + "Search from the end?"), + _("No new messages.")); +} + +void summary_select_next_new(SummaryView *summaryview) +{ + summary_select_next_flagged_or_folder(summaryview, MSG_NEW, + _("No more new messages"), + _("No new message found. " + "Go to next folder which has new messages?"), + NULL); +} + +void summary_select_prev_marked(SummaryView *summaryview) +{ + summary_select_prev_flagged(summaryview, MSG_MARKED, + _("No more marked messages"), + _("No marked message found. " + "Search from the end?"), + _("No marked messages.")); +} + +void summary_select_next_marked(SummaryView *summaryview) +{ + summary_select_next_flagged(summaryview, MSG_MARKED, + _("No more marked messages"), + _("No marked message found. " + "Search from the beginning?"), + _("No marked messages.")); +} + +void summary_select_prev_labeled(SummaryView *summaryview) +{ + summary_select_prev_flagged(summaryview, MSG_CLABEL_FLAG_MASK, + _("No more labeled messages"), + _("No labeled message found. " + "Search from the end?"), + _("No labeled messages.")); +} + +void summary_select_next_labeled(SummaryView *summaryview) +{ + summary_select_next_flagged(summaryview, MSG_CLABEL_FLAG_MASK, + _("No more labeled messages"), + _("No labeled message found. " + "Search from the beginning?"), + _("No labeled messages.")); +} + +void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum) +{ + GtkTreeIter iter; + + if (summary_find_msg_by_msgnum(summaryview, msgnum, &iter)) + summary_select_row(summaryview, &iter, FALSE, TRUE); +} + +gboolean summary_select_by_msginfo(SummaryView *summaryview, MsgInfo *msginfo) +{ + GtkTreeIter iter; + + if (summaryview->folder_item != msginfo->folder) + return FALSE; + + if (summary_find_msg_by_msgnum(summaryview, msginfo->msgnum, &iter)) { + summary_select_row(summaryview, &iter, + messageview_is_visible(summaryview->messageview), + TRUE); + return TRUE; + } + + return FALSE; +} + +MsgInfo *summary_get_msginfo_by_msgnum(SummaryView *summaryview, guint msgnum) +{ + GtkTreeIter iter; + MsgInfo *msginfo = NULL; + + if (summary_find_msg_by_msgnum(summaryview, msgnum, &iter)) + GET_MSG_INFO(msginfo, &iter); + + return msginfo; +} + +static gboolean summary_select_true_func(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean cur_selected, gpointer data) +{ + return TRUE; +} + +/** + * summary_select_row: + * @summaryview: Summary view. + * @node: Summary tree node. + * @display_msg: TRUE to display the selected message. + * @do_refresh: TRUE to refresh the widget. + * + * Select @node (bringing it into view by scrolling and expanding its + * thread, if necessary) and unselect all others. If @display_msg is + * TRUE, display the corresponding message in the message view. + * If @do_refresh is TRUE, the widget is refreshed. + **/ +void summary_select_row(SummaryView *summaryview, GtkTreeIter *iter, + gboolean display_msg, gboolean do_refresh) +{ + GtkTreePath *path; + + if (!iter) + return; + + gtkut_tree_view_expand_parent_all + (GTK_TREE_VIEW(summaryview->treeview), iter); + + summaryview->display_msg = display_msg; + path = gtk_tree_model_get_path(GTK_TREE_MODEL(summaryview->store), + iter); + if (!display_msg) + gtk_tree_selection_set_select_function(summaryview->selection, + summary_select_true_func, + summaryview, NULL); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(summaryview->treeview), path, + NULL, FALSE); + if (!display_msg) + gtk_tree_selection_set_select_function(summaryview->selection, + summary_select_func, + summaryview, NULL); + if (do_refresh) { + GTK_EVENTS_FLUSH(); + gtk_tree_view_scroll_to_cell + (GTK_TREE_VIEW(summaryview->treeview), + path, NULL, TRUE, 0.5, 0.0); + } else { + gtkut_tree_view_scroll_to_cell + (GTK_TREE_VIEW(summaryview->treeview), path, + !summaryview->on_button_press); + } + + gtk_tree_path_free(path); +} + +static void summary_scroll_to_selected(SummaryView *summaryview, + gboolean align_center) +{ + GtkTreePath *path; + + if (!summaryview->selected) + return; + + path = gtk_tree_row_reference_get_path(summaryview->selected); + if (path) { + if (align_center) + gtk_tree_view_scroll_to_cell + (GTK_TREE_VIEW(summaryview->treeview), + path, NULL, TRUE, 0.5, 0.0); + else + gtkut_tree_view_scroll_to_cell + (GTK_TREE_VIEW(summaryview->treeview), path, + FALSE); + gtk_tree_path_free(path); + } +} + +static MsgInfo *summary_get_msginfo(SummaryView *summaryview, + GtkTreeRowReference *row) +{ + GtkTreeIter iter; + MsgInfo *msginfo = NULL; + + if (!row) + return 0; + if (!gtkut_tree_row_reference_get_iter + (GTK_TREE_MODEL(summaryview->store), row, &iter)) + return 0; + + gtk_tree_model_get(GTK_TREE_MODEL(summaryview->store), &iter, + S_COL_MSG_INFO, &msginfo, -1); + + return msginfo; +} + +static guint summary_get_msgnum(SummaryView *summaryview, + GtkTreeRowReference *row) +{ + MsgInfo *msginfo; + + msginfo = summary_get_msginfo(summaryview, row); + if (!msginfo) + return 0; + + return msginfo->msgnum; +} + +static gboolean summary_find_prev_msg(SummaryView *summaryview, + GtkTreeIter *prev, GtkTreeIter *iter) +{ + GtkTreeIter iter_; + MsgInfo *msginfo; + gboolean valid = TRUE; + + if (!iter) + return FALSE; + + iter_ = *iter; + + while (valid) { + GET_MSG_INFO(msginfo, &iter_); + if (msginfo && !MSG_IS_INVALID(msginfo->flags) && + !MSG_IS_DELETED(msginfo->flags)) { + *prev = iter_; + return TRUE; + } + valid = gtkut_tree_model_prev + (GTK_TREE_MODEL(summaryview->store), &iter_); + } + + return FALSE; +} + +static gboolean summary_find_next_msg(SummaryView *summaryview, + GtkTreeIter *next, GtkTreeIter *iter) +{ + GtkTreeIter iter_; + MsgInfo *msginfo; + gboolean valid = TRUE; + + if (!iter) + return FALSE; + + iter_ = *iter; + + while (valid) { + GET_MSG_INFO(msginfo, &iter_); + if (msginfo && !MSG_IS_INVALID(msginfo->flags) && + !MSG_IS_DELETED(msginfo->flags)) { + *next = iter_; + return TRUE; + } + valid = gtkut_tree_model_next + (GTK_TREE_MODEL(summaryview->store), &iter_); + } + + return FALSE; +} + +static gboolean summary_find_nearest_msg(SummaryView *summaryview, + GtkTreeIter *target, GtkTreeIter *iter) +{ + gboolean valid; + + valid = summary_find_next_msg(summaryview, target, iter); + if (!valid) + valid = summary_find_prev_msg(summaryview, target, iter); + + return valid; +} + +static gboolean summary_find_prev_flagged_msg(SummaryView *summaryview, + GtkTreeIter *prev, + GtkTreeIter *iter, + MsgPermFlags flags, + gboolean start_from_prev) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter_; + MsgInfo *msginfo; + gboolean valid = TRUE; + + if (iter) { + iter_ = *iter; + if (start_from_prev) + valid = gtkut_tree_model_prev(model, &iter_); + } else + valid = gtkut_tree_model_get_iter_last(model, &iter_); + + for (; valid == TRUE; valid = gtkut_tree_model_prev(model, &iter_)) { + GET_MSG_INFO(msginfo, &iter_); + if (msginfo && (msginfo->flags.perm_flags & flags) != 0) { + *prev = iter_; + return TRUE; + } + } + + return FALSE; +} + +static gboolean summary_find_next_flagged_msg(SummaryView *summaryview, + GtkTreeIter *next, + GtkTreeIter *iter, + MsgPermFlags flags, + gboolean start_from_next) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter_; + MsgInfo *msginfo; + gboolean valid = TRUE; + + if (iter) { + iter_ = *iter; + if (start_from_next) + valid = gtkut_tree_model_next(model, &iter_); + } else + valid = gtk_tree_model_get_iter_first(model, &iter_); + + for (; valid == TRUE; valid = gtkut_tree_model_next(model, &iter_)) { + GET_MSG_INFO(msginfo, &iter_); + if (msginfo && (msginfo->flags.perm_flags & flags) != 0) { + *next = iter_; + return TRUE; + } + } + + return FALSE; +} + +static gboolean summary_find_msg_by_msgnum(SummaryView *summaryview, + guint msgnum, GtkTreeIter *found) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter; + MsgInfo *msginfo; + gboolean valid; + + for (valid = gtk_tree_model_get_iter_first(model, &iter); + valid == TRUE; valid = gtkut_tree_model_next(model, &iter)) { + gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1); + if (msginfo && msginfo->msgnum == msgnum) { + *found = iter; + return TRUE; + } + } + + return FALSE; +} + +static void summary_update_display_state(SummaryView *summaryview, + guint disp_msgnum, guint sel_msgnum) +{ + GtkTreeIter iter; + + if (summary_find_msg_by_msgnum(summaryview, disp_msgnum, &iter)) { + GtkTreePath *path; + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_row_reference_free(summaryview->displayed); + summaryview->displayed = + gtk_tree_row_reference_new(model, path); + gtk_tree_path_free(path); + } else + messageview_clear(summaryview->messageview); + + summary_select_by_msgnum(summaryview, sel_msgnum); +} + +static guint attract_hash_func(gconstpointer key) +{ + gchar str[BUFFSIZE]; + gchar *p; + guint h; + + strncpy2(str, (const gchar *)key, sizeof(str)); + trim_subject_for_compare(str); + + p = str; + h = *p; + + if (h) { + for (p += 1; *p != '\0'; p++) + h = (h << 5) - h + *p; + } + + return h; +} + +static gint attract_compare_func(gconstpointer a, gconstpointer b) +{ + return subject_compare((const gchar *)a, (const gchar *)b) == 0; +} + +void summary_attract_by_subject(SummaryView *summaryview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter; + MsgInfo *msginfo, *dest_msginfo; + GHashTable *subject_table, *order_table; + GSList *mlist = NULL, *list, *dest, *last = NULL, *next = NULL; + gboolean valid; + gint count, i; + gint *new_order; + + if (!summaryview->folder_item) + return; + if (summaryview->folder_item->sort_key != SORT_BY_NONE) + return; + + valid = gtk_tree_model_get_iter_first(model, &iter); + if (!valid) + return; + + debug_print("Attracting messages by subject..."); + STATUSBAR_PUSH(summaryview->mainwin, + _("Attracting messages by subject...")); + + main_window_cursor_wait(summaryview->mainwin); + + order_table = g_hash_table_new(NULL, NULL); + + for (count = 1; valid == TRUE; ++count) { + gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1); + g_hash_table_insert(order_table, msginfo, + GINT_TO_POINTER(count)); + mlist = g_slist_prepend(mlist, msginfo); + valid = gtk_tree_model_iter_next(model, &iter); + } + --count; + + mlist = g_slist_reverse(mlist); + + subject_table = g_hash_table_new(attract_hash_func, + attract_compare_func); + + for (list = mlist; list != NULL; list = next) { + msginfo = (MsgInfo *)list->data; + + next = list->next; + + if (!msginfo->subject) { + last = list; + continue; + } + + /* find attracting node */ + dest = g_hash_table_lookup(subject_table, msginfo->subject); + + if (dest) { + dest_msginfo = (MsgInfo *)dest->data; + + /* if the time difference is more than 30 days, + don't attract */ + if (ABS(msginfo->date_t - dest_msginfo->date_t) + > 60 * 60 * 24 * 30) { + last = list; + continue; + } + + if (dest->next != list) { + last->next = list->next; + list->next = dest->next; + dest->next = list; + } else + last = list; + } else + last = list; + + g_hash_table_replace(subject_table, msginfo->subject, list); + } + + g_hash_table_destroy(subject_table); + + new_order = g_new(gint, count); + for (list = mlist, i = 0; list != NULL; list = list->next, ++i) { + gint old_pos; + + msginfo = (MsgInfo *)list->data; + + old_pos = GPOINTER_TO_INT + (g_hash_table_lookup(order_table, msginfo)); + new_order[i] = old_pos - 1; + } + gtk_tree_store_reorder(GTK_TREE_STORE(model), NULL, new_order); + g_free(new_order); + + g_slist_free(mlist); + g_hash_table_destroy(order_table); + + summaryview->folder_item->cache_dirty = TRUE; + summary_selection_list_free(summaryview); + summary_update_msg_list(summaryview); + + summary_scroll_to_selected(summaryview, TRUE); + + debug_print("done.\n"); + STATUSBAR_POP(summaryview->mainwin); + + main_window_cursor_normal(summaryview->mainwin); +} + +static void summary_update_status(SummaryView *summaryview) +{ + GSList *cur; + MsgInfo *msginfo; + gint64 total_size = 0; + gint deleted = 0, moved = 0, copied = 0; + gint64 flt_total_size = 0; + gint flt_deleted = 0, flt_moved = 0, flt_copied = 0; + gint flt_new = 0, flt_unread = 0, flt_total = 0; + + for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + + if (MSG_IS_DELETED(msginfo->flags)) + deleted++; + if (MSG_IS_MOVE(msginfo->flags)) + moved++; + if (MSG_IS_COPY(msginfo->flags)) + copied++; + total_size += msginfo->size; + } + + for (cur = summaryview->flt_mlist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + + if (MSG_IS_DELETED(msginfo->flags)) + flt_deleted++; + if (MSG_IS_MOVE(msginfo->flags)) + flt_moved++; + if (MSG_IS_COPY(msginfo->flags)) + flt_copied++; + if (MSG_IS_NEW(msginfo->flags)) + flt_new++; + if (MSG_IS_UNREAD(msginfo->flags)) + flt_unread++; + flt_total++; + flt_total_size += msginfo->size; + } + + summaryview->total_size = total_size; + summaryview->deleted = deleted; + summaryview->moved = moved; + summaryview->copied = copied; + summaryview->total_flt_msg_size = flt_total_size; + summaryview->flt_msg_total = flt_total; + summaryview->flt_deleted = flt_deleted; + summaryview->flt_moved = flt_moved; + summaryview->flt_copied = flt_copied; + summaryview->flt_new = flt_new; + summaryview->flt_unread = flt_unread; +} + +static void summary_status_show(SummaryView *summaryview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GString *str; + gchar *name; + GList *rowlist, *cur; + guint n_selected = 0; + gint64 sel_size = 0; + MsgInfo *msginfo; + gint deleted, moved, copied; + gint new, unread, total; + gint64 total_size; + + if (!summaryview->folder_item) { + gtk_label_set(GTK_LABEL(summaryview->statlabel_folder), ""); + gtk_label_set(GTK_LABEL(summaryview->statlabel_select), ""); + gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs), ""); + return; + } + + rowlist = summary_get_selected_rows(summaryview); + for (cur = rowlist; cur != NULL; cur = cur->next) { + GtkTreeIter iter; + GtkTreePath *path = (GtkTreePath *)cur->data; + + if (gtk_tree_model_get_iter(model, &iter, path)) { + gtk_tree_model_get(model, &iter, + S_COL_MSG_INFO, &msginfo, -1); + sel_size += msginfo->size; + n_selected++; + } + } + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) { + gchar *group; + group = get_abbrev_newsgroup_name + (g_basename(summaryview->folder_item->path), + prefs_common.ng_abbrev_len); + name = trim_string_before(group, 32); + g_free(group); + } else + name = trim_string_before(summaryview->folder_item->path, 32); + gtk_label_set(GTK_LABEL(summaryview->statlabel_folder), name); + g_free(name); + + if (summaryview->on_filter) { + deleted = summaryview->flt_deleted; + moved = summaryview->flt_moved; + copied = summaryview->flt_copied; + } else { + deleted = summaryview->deleted; + moved = summaryview->moved; + copied = summaryview->copied; + } + + str = g_string_sized_new(128); + + if (n_selected) + g_string_append_printf(str, "%d%s (%s)", n_selected, + _(" item(s) selected"), + to_human_readable(sel_size)); + if (str->len > 0 && (deleted || moved || copied)) + g_string_append(str, " "); + if (deleted) + g_string_append_printf(str, _("%d deleted"), deleted); + if (moved) + g_string_append_printf(str, _("%s%d moved"), + deleted ? _(", ") : "", moved); + if (copied) + g_string_append_printf(str, _("%s%d copied"), + deleted || moved ? _(", ") : "", copied); + + gtk_label_set(GTK_LABEL(summaryview->statlabel_select), str->str); + g_string_truncate(str, 0); + + new = summaryview->folder_item->new; + unread = summaryview->folder_item->unread; + total = summaryview->folder_item->total; + total_size = summaryview->total_size; + + if (summaryview->on_filter) { + gint f_new, f_unread, f_total; + gint64 f_total_size; + gchar f_ts[16], ts[16]; + + f_new = summaryview->flt_new; + f_unread = summaryview->flt_unread; + f_total = summaryview->flt_msg_total; + f_total_size = summaryview->total_flt_msg_size; + + g_string_printf(str, _("%d/%d new, %d/%d unread, %d/%d total"), + f_new, new, f_unread, unread, f_total, total); + if (FOLDER_IS_LOCAL(summaryview->folder_item->folder)) { + g_string_append_printf(str, " (%s/%s)", + to_human_readable_buf(f_ts, sizeof(f_ts), f_total_size), to_human_readable_buf(ts, sizeof(ts), total_size)); + } + } else { + if (FOLDER_IS_LOCAL(summaryview->folder_item->folder)) { + g_string_printf(str, + _("%d new, %d unread, %d total (%s)"), + new, unread, total, + to_human_readable(total_size)); + } else { + g_string_printf(str, _("%d new, %d unread, %d total"), + new, unread, total); + } + } + + gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs), str->str); + g_string_free(str, TRUE); + + folderview_update_opened_msg_num(summaryview->folderview); +} + +void summary_sort(SummaryView *summaryview, + FolderSortKey sort_key, FolderSortType sort_type) +{ + FolderItem *item = summaryview->folder_item; + GtkTreeSortable *sortable = GTK_TREE_SORTABLE(summaryview->store); + SummaryColumnType col_type, prev_col_type; + GtkTreeViewColumn *column; + + g_return_if_fail(sort_key >= SORT_BY_NONE && sort_key <= SORT_BY_TO); + + if (!item || !item->path || !item->parent || item->no_select) return; + + if (item->sort_key != sort_key || item->sort_type != sort_type) + item->cache_dirty = TRUE; + + col_type = sort_key_to_col[sort_key]; + prev_col_type = sort_key_to_col[item->sort_key]; + + if (col_type == -1) { + item->sort_key = SORT_BY_NONE; + item->sort_type = SORT_ASCENDING; + summary_unset_sort_column_id(summaryview); + summary_set_menu_sensitive(summaryview); + return; + } + + debug_print("Sorting summary by key: %d...\n", sort_key); + STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary...")); + + main_window_cursor_wait(summaryview->mainwin); + + item->sort_key = sort_key; + item->sort_type = sort_type; + + gtk_tree_sortable_set_sort_column_id(sortable, col_type, + (GtkSortType)sort_type); + + if (prev_col_type != -1 && col_type != prev_col_type && + prev_col_type < N_SUMMARY_VISIBLE_COLS) { + column = summaryview->columns[prev_col_type]; + column->sort_column_id = -1; + gtk_tree_view_column_set_sort_indicator(column, FALSE); + } + if (col_type != S_COL_MARK && col_type != S_COL_UNREAD && + col_type != S_COL_MIME && col_type < N_SUMMARY_VISIBLE_COLS) { + column = summaryview->columns[col_type]; + column->sort_column_id = col_type; + gtk_tree_view_column_set_sort_indicator(column, TRUE); + gtk_tree_view_column_set_sort_order(column, + (GtkSortType)sort_type); + } + + summary_selection_list_free(summaryview); + if (summaryview->all_mlist) + summary_update_msg_list(summaryview); + + summary_set_menu_sensitive(summaryview); + + summary_scroll_to_selected(summaryview, TRUE); + + debug_print("done.\n"); + STATUSBAR_POP(summaryview->mainwin); + + main_window_cursor_normal(summaryview->mainwin); +} + +static gboolean summary_have_unread_children(SummaryView *summaryview, + GtkTreeIter *iter) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter_; + MsgInfo *msginfo; + gboolean valid; + + gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); + if (MSG_IS_UNREAD(msginfo->flags)) + return TRUE; + + valid = gtk_tree_model_iter_children(model, &iter_, iter); + + while (valid) { + if (summary_have_unread_children(summaryview, &iter_)) + return TRUE; + + valid = gtk_tree_model_iter_next(model, &iter_); + } + + return FALSE; +} + +static void summary_set_row(SummaryView *summaryview, GtkTreeIter *iter, + MsgInfo *msginfo) +{ + GtkTreeStore *store = GTK_TREE_STORE(summaryview->store); + gchar date_modified[80]; + const gchar *date_s; + gchar *sw_from_s = NULL; + gchar *subject_s = NULL; + gchar *to_s = NULL; + const gchar *disp_from = NULL; + GdkPixbuf *mark_pix = NULL; + GdkPixbuf *unread_pix = NULL; + GdkPixbuf *mime_pix = NULL; + GdkColor *foreground = NULL; + PangoWeight weight = PANGO_WEIGHT_NORMAL; + MsgFlags flags; + GdkColor color; + gint color_val; + + if (!msginfo) { + GET_MSG_INFO(msginfo, iter); + } + + if (msginfo->date_t) { + procheader_date_get_localtime(date_modified, + sizeof(date_modified), + msginfo->date_t); + date_s = date_modified; + } else if (msginfo->date) + date_s = msginfo->date; + else + date_s = _("(No Date)"); + if (prefs_common.swap_from && msginfo->from && msginfo->to) { + gchar from[BUFFSIZE]; + + strncpy2(from, msginfo->from, sizeof(from)); + extract_address(from); + if (account_address_exist(from)) { + sw_from_s = g_strconcat("-->", msginfo->to, NULL); + disp_from = sw_from_s; + } + } + + if (!disp_from) { + /* prevent address-like display-name */ + if (!msginfo->fromname || + strchr(msginfo->fromname, '@') != NULL) + disp_from = msginfo->from; + else + disp_from = msginfo->fromname; + } + + if (msginfo->subject && *msginfo->subject) { + if (msginfo->folder && msginfo->folder->trim_summary_subject) { + subject_s = g_strdup(msginfo->subject); + trim_subject(subject_s); + } + } + + if (msginfo->to) + to_s = procheader_get_toname(msginfo->to); + + flags = msginfo->flags; + + /* set flag pixbufs */ + if (MSG_IS_DELETED(flags)) { + mark_pix = deleted_pixbuf; + foreground = &summaryview->color_dim; + } else if (MSG_IS_MOVE(flags)) { + /* mark_pix = move_pixbuf; */ + foreground = &summaryview->color_marked; + } else if (MSG_IS_COPY(flags)) { + /* mark_pix = copy_pixbuf; */ + foreground = &summaryview->color_marked; + } else if (MSG_IS_MARKED(flags)) + mark_pix = mark_pixbuf; + + if (MSG_IS_NEW(flags)) + unread_pix = new_pixbuf; + else if (MSG_IS_UNREAD(flags)) + unread_pix = unread_pixbuf; + else if (MSG_IS_REPLIED(flags)) + unread_pix = replied_pixbuf; + else if (MSG_IS_FORWARDED(flags)) + unread_pix = forwarded_pixbuf; + + if (MSG_IS_MIME(flags)) { + mime_pix = clip_pixbuf; + } + if (MSG_IS_MIME_HTML(flags)) { + mime_pix = html_pixbuf; + } + + if (prefs_common.bold_unread) { + if (MSG_IS_UNREAD(flags)) + weight = PANGO_WEIGHT_BOLD; + else if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), + iter)) { + GtkTreePath *path; + + path = gtk_tree_model_get_path + (GTK_TREE_MODEL(store), iter); + if (!gtk_tree_view_row_expanded + (GTK_TREE_VIEW(summaryview->treeview), path) && + summary_have_unread_children(summaryview, iter)) + weight = PANGO_WEIGHT_BOLD; + gtk_tree_path_free(path); + } + } + + color_val = MSG_GET_COLORLABEL_VALUE(flags); + if (color_val != 0) { + color = colorlabel_get_color(color_val - 1); + foreground = &color; + } + + gtk_tree_store_set(store, iter, + S_COL_MARK, mark_pix, + S_COL_UNREAD, unread_pix, + S_COL_MIME, mime_pix, + S_COL_SUBJECT, subject_s ? subject_s : + msginfo->subject && *msginfo->subject ? msginfo->subject : + _("(No Subject)"), + S_COL_FROM, disp_from ? disp_from : _("(No From)"), + S_COL_DATE, date_s, + S_COL_SIZE, to_human_readable(msginfo->size), + S_COL_NUMBER, msginfo->msgnum, + S_COL_TO, to_s ? to_s : "", + + S_COL_MSG_INFO, msginfo, + + S_COL_LABEL, color_val, + + S_COL_FOREGROUND, foreground, + S_COL_BOLD, weight, + -1); + + if (to_s) + g_free(to_s); + if (subject_s) + g_free(subject_s); + if (sw_from_s) + g_free(sw_from_s); +} + +static void summary_insert_gnode(SummaryView *summaryview, GtkTreeStore *store, + GtkTreeIter *iter, GtkTreeIter *parent, + GtkTreeIter *sibling, GNode *gnode) +{ + MsgInfo *msginfo = (MsgInfo *)gnode->data; + + if (parent && !sibling) + gtk_tree_store_append(store, iter, parent); + else + gtk_tree_store_insert_after(store, iter, parent, sibling); + + summary_set_row(summaryview, iter, msginfo); + + if (!parent) { + guint tdate; + + tdate = procmsg_get_thread_date(gnode); + gtk_tree_store_set(store, iter, S_COL_TDATE, tdate, -1); + } + + for (gnode = gnode->children; gnode != NULL; gnode = gnode->next) { + GtkTreeIter child; + + summary_insert_gnode(summaryview, store, &child, iter, NULL, + gnode); + } +} + +static void summary_insert_gnode_before(SummaryView *summaryview, + GtkTreeStore *store, + GtkTreeIter *iter, GtkTreeIter *parent, + GtkTreeIter *sibling, GNode *gnode) +{ + MsgInfo *msginfo = (MsgInfo *)gnode->data; + + gtk_tree_store_insert_before(store, iter, parent, sibling); + + summary_set_row(summaryview, iter, msginfo); + + if (!parent) { + guint tdate; + + tdate = procmsg_get_thread_date(gnode); + gtk_tree_store_set(store, iter, S_COL_TDATE, tdate, -1); + } + + for (gnode = gnode->children; gnode != NULL; gnode = gnode->next) { + GtkTreeIter child; + + summary_insert_gnode_before(summaryview, store, &child, iter, + NULL, gnode); + } +} + +static void summary_set_tree_model_from_list(SummaryView *summaryview, + GSList *mlist) +{ + GtkTreeStore *store = GTK_TREE_STORE(summaryview->store); + GtkTreeIter iter; + MsgInfo *msginfo; + GSList *cur; + + debug_print(_("\tSetting summary from message data...")); + STATUSBAR_PUSH(summaryview->mainwin, + _("Setting summary from message data...")); + gdk_flush(); + + /* temporarily remove the model for speed up */ + gtk_tree_view_set_model(GTK_TREE_VIEW(summaryview->treeview), NULL); + + if (summaryview->folder_item->threaded) { + GNode *root, *gnode; + + root = procmsg_get_thread_tree(mlist); + + for (gnode = root->children; gnode != NULL; + gnode = gnode->next) { + summary_insert_gnode + (summaryview, store, &iter, NULL, NULL, gnode); + if (gnode->children && !prefs_common.expand_thread && + prefs_common.bold_unread && + summary_have_unread_children(summaryview, &iter)) { + gtk_tree_store_set(store, &iter, + S_COL_BOLD, + PANGO_WEIGHT_BOLD, -1); + } + } + + g_node_destroy(root); + + for (cur = mlist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + + if (MSG_IS_DELETED(msginfo->flags)) + summaryview->deleted++; + if (MSG_IS_MOVE(msginfo->flags)) + summaryview->moved++; + if (MSG_IS_COPY(msginfo->flags)) + summaryview->copied++; + summaryview->total_size += msginfo->size; + } + } else { + GSList *rev_mlist; + GtkTreeIter iter; + + rev_mlist = g_slist_reverse(g_slist_copy(mlist)); + for (cur = rev_mlist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + + gtk_tree_store_prepend(store, &iter, NULL); + summary_set_row(summaryview, &iter, msginfo); + + if (MSG_IS_DELETED(msginfo->flags)) + summaryview->deleted++; + if (MSG_IS_MOVE(msginfo->flags)) + summaryview->moved++; + if (MSG_IS_COPY(msginfo->flags)) + summaryview->copied++; + summaryview->total_size += msginfo->size; + } + g_slist_free(rev_mlist); + } + + gtk_tree_view_set_model(GTK_TREE_VIEW(summaryview->treeview), + GTK_TREE_MODEL(store)); + + if (summaryview->folder_item->threaded && prefs_common.expand_thread) + gtk_tree_view_expand_all + (GTK_TREE_VIEW(summaryview->treeview)); + + if (summaryview->folder_item->sort_key != SORT_BY_NONE) { + summary_sort(summaryview, summaryview->folder_item->sort_key, + summaryview->folder_item->sort_type); + } + + debug_print(_("done.\n")); + STATUSBAR_POP(summaryview->mainwin); +} + +struct wcachefp +{ + FILE *cache_fp; + FILE *mark_fp; +}; + +gint summary_write_cache(SummaryView *summaryview) +{ + struct wcachefp fps; + FolderItem *item; + gchar *buf; + GSList *cur; + + item = summaryview->folder_item; + if (!item || !item->path) + return -1; + if (item->mark_queue) + item->mark_dirty = TRUE; + if (!item->cache_dirty && !item->mark_dirty) + return 0; + + if (item->cache_dirty) { + fps.cache_fp = procmsg_open_cache_file(item, DATA_WRITE); + if (fps.cache_fp == NULL) + return -1; + item->mark_dirty = TRUE; + } else + fps.cache_fp = NULL; + + if (item->mark_dirty && item->stype != F_VIRTUAL) { + fps.mark_fp = procmsg_open_mark_file(item, DATA_WRITE); + if (fps.mark_fp == NULL) { + if (fps.cache_fp) + fclose(fps.cache_fp); + return -1; + } + } else + fps.mark_fp = NULL; + + if (item->cache_dirty) { + buf = g_strdup_printf(_("Writing summary cache (%s)..."), + item->path); + debug_print("%s", buf); + STATUSBAR_PUSH(summaryview->mainwin, buf); + gdk_flush(); + g_free(buf); + } + + for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) { + MsgInfo *msginfo = (MsgInfo *)cur->data; + + if (msginfo->folder && msginfo->folder->mark_queue != NULL) { + MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW); + } + if (fps.cache_fp) + procmsg_write_cache(msginfo, fps.cache_fp); + if (fps.mark_fp) + procmsg_write_flags(msginfo, fps.mark_fp); + } + + if (item->cache_queue) + procmsg_flush_cache_queue(item, fps.cache_fp); + if (item->mark_queue) + procmsg_flush_mark_queue(item, fps.mark_fp); + + item->unmarked_num = 0; + + if (fps.cache_fp) + fclose(fps.cache_fp); + if (fps.mark_fp) + fclose(fps.mark_fp); + + if (item->stype == F_VIRTUAL) { + GSList *mlist; + + mlist = summary_get_changed_msg_list(summaryview); + if (mlist) { + procmsg_write_flags_for_multiple_folders(mlist); + g_slist_free(mlist); + folderview_update_all_updated(FALSE); + } + } + + debug_print(_("done.\n")); + + if (item->cache_dirty) { + STATUSBAR_POP(summaryview->mainwin); + } + + item->cache_dirty = item->mark_dirty = FALSE; + + return 0; +} + +static gboolean summary_row_is_displayed(SummaryView *summaryview, + GtkTreeIter *iter) +{ + GtkTreePath *disp_path, *path; + gint ret; + + if (!summaryview->displayed || !iter) + return FALSE; + + disp_path = gtk_tree_row_reference_get_path(summaryview->displayed); + if (!disp_path) + return FALSE; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(summaryview->store), + iter); + if (!path) { + gtk_tree_path_free(disp_path); + return FALSE; + } + + ret = gtk_tree_path_compare(disp_path, path); + gtk_tree_path_free(path); + gtk_tree_path_free(disp_path); + + return (ret == 0); +} + +static void summary_display_msg(SummaryView *summaryview, GtkTreeIter *iter) +{ + summary_display_msg_full(summaryview, iter, FALSE, FALSE, FALSE); +} + +static void summary_display_msg_full(SummaryView *summaryview, + GtkTreeIter *iter, + gboolean new_window, gboolean all_headers, + gboolean redisplay) +{ + GtkTreePath *path; + MsgInfo *msginfo = NULL; + gint val; + gboolean do_mark_read = FALSE; + + g_return_if_fail(iter != NULL); + + if (!new_window && !redisplay && + summary_row_is_displayed(summaryview, iter)) + return; + + if (summary_is_read_locked(summaryview)) return; + summary_lock(summaryview); + + STATUSBAR_POP(summaryview->mainwin); + + gtk_tree_model_get(GTK_TREE_MODEL(summaryview->store), iter, + S_COL_MSG_INFO, &msginfo, -1); + + do_mark_read = prefs_common.always_mark_read_on_show_msg; + + if (new_window) { + MessageView *msgview; + + msgview = messageview_create_with_new_window(); + val = messageview_show(msgview, msginfo, all_headers); + do_mark_read = TRUE; + } else { + MessageView *msgview = summaryview->messageview; + gboolean prev_mimeview; + + if (!messageview_is_visible(msgview)) { + main_window_toggle_message_view(summaryview->mainwin); + GTK_EVENTS_FLUSH(); + } + prev_mimeview = + messageview_get_selected_mime_part(msgview) != NULL; + + val = messageview_show(msgview, msginfo, all_headers); + if (prev_mimeview && + !messageview_get_selected_mime_part(msgview)) + gtk_widget_grab_focus(summaryview->treeview); + } + + if (val == 0 && do_mark_read) { + if (MSG_IS_NEW(msginfo->flags) || + MSG_IS_UNREAD(msginfo->flags)) { + summary_mark_row_as_read(summaryview, iter); + if (MSG_IS_IMAP(msginfo->flags)) + imap_msg_unset_perm_flags + (msginfo, MSG_NEW | MSG_UNREAD); + summary_set_row(summaryview, iter, msginfo); + summary_status_show(summaryview); + } + } + + path = gtk_tree_model_get_path + (GTK_TREE_MODEL(summaryview->store), iter); + if (!new_window) { + gtk_tree_row_reference_free(summaryview->displayed); + summaryview->displayed = + gtk_tree_row_reference_new + (GTK_TREE_MODEL(summaryview->store), path); + } + gtkut_tree_view_scroll_to_cell + (GTK_TREE_VIEW(summaryview->treeview), path, + !summaryview->on_button_press); + gtk_tree_path_free(path); + + if (summaryview->folder_item->sort_key == SORT_BY_UNREAD) + summary_selection_list_free(summaryview); + + summary_set_menu_sensitive(summaryview); + main_window_set_toolbar_sensitive(summaryview->mainwin); + + trayicon_set_tooltip(NULL); + trayicon_set_notify(FALSE); + + statusbar_pop_all(); + + summary_unlock(summaryview); +} + +void summary_display_msg_selected(SummaryView *summaryview, + gboolean new_window, gboolean all_headers) +{ + GtkTreeIter iter; + + if (summary_is_read_locked(summaryview)) return; + + if (summaryview->selected) { + if (gtkut_tree_row_reference_get_iter + (GTK_TREE_MODEL(summaryview->store), + summaryview->selected, &iter)) { + summary_display_msg_full(summaryview, &iter, + new_window, all_headers, TRUE); + } + } +} + +void summary_redisplay_msg(SummaryView *summaryview) +{ + GtkTreeIter iter; + + if (summaryview->displayed) { + if (gtkut_tree_row_reference_get_iter + (GTK_TREE_MODEL(summaryview->store), + summaryview->displayed, &iter)) { + summary_display_msg_full(summaryview, &iter, + FALSE, FALSE, TRUE); + } + } +} + +void summary_open_msg(SummaryView *summaryview) +{ + summary_display_msg_selected(summaryview, TRUE, FALSE); +} + +static void summary_activate_selected(SummaryView *summaryview) +{ + if (!summaryview->folder_item) + return; + + if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item)) + summary_reedit(summaryview); + else + summary_open_msg(summaryview); + + summaryview->display_msg = FALSE; +} + +void summary_view_source(SummaryView *summaryview) +{ + GtkTreeIter iter; + MsgInfo *msginfo; + SourceWindow *srcwin; + + if (summaryview->selected) { + if (gtkut_tree_row_reference_get_iter + (GTK_TREE_MODEL(summaryview->store), + summaryview->selected, &iter)) { + GET_MSG_INFO(msginfo, &iter); + + srcwin = source_window_create(); + source_window_show_msg(srcwin, msginfo); + source_window_show(srcwin); + } + } +} + +void summary_reedit(SummaryView *summaryview) +{ + GtkTreeIter iter; + MsgInfo *msginfo; + + if (!summaryview->selected) return; + if (!FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item)) return; + + if (gtkut_tree_row_reference_get_iter + (GTK_TREE_MODEL(summaryview->store), + summaryview->selected, &iter)) { + GET_MSG_INFO(msginfo, &iter); + compose_reedit(msginfo); + } +} + +gboolean summary_step(SummaryView *summaryview, GtkScrollType type) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter; + + if (summary_is_read_locked(summaryview)) return FALSE; + + if (!gtkut_tree_row_reference_get_iter + (model, summaryview->selected, &iter)) + return FALSE; + + if (type == GTK_SCROLL_STEP_FORWARD) { + if (!gtkut_tree_model_next(model, &iter)) + return FALSE; + } else { + if (!gtkut_tree_model_prev(model, &iter)) + return FALSE; + } + + summary_select_row(summaryview, &iter, + messageview_is_visible(summaryview->messageview), + FALSE); + + return TRUE; +} + +void summary_toggle_view(SummaryView *summaryview) +{ + if (!messageview_is_visible(summaryview->messageview) && + summaryview->selected) { + summary_display_msg_selected(summaryview, FALSE, FALSE); + summary_mark_displayed_read(summaryview, NULL); + } else + main_window_toggle_message_view(summaryview->mainwin); +} + +void summary_update_selected_rows(SummaryView *summaryview) +{ + GList *rows, *cur; + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter; + GtkTreePath *path; + + rows = summary_get_selected_rows(summaryview); + for (cur = rows; cur != NULL; cur = cur->next) { + path = (GtkTreePath *)cur->data; + gtk_tree_model_get_iter(model, &iter, path); + summary_set_row(summaryview, &iter, NULL); + } +} + +void summary_update_by_msgnum(SummaryView *summaryview, guint msgnum) +{ + GtkTreeIter iter; + + if (summary_find_msg_by_msgnum(summaryview, msgnum, &iter)) + summary_set_row(summaryview, &iter, NULL); +} + +static void summary_mark_row(SummaryView *summaryview, GtkTreeIter *iter) +{ + MsgInfo *msginfo = NULL; + + GET_MSG_INFO(msginfo, iter); + + msginfo->to_folder = NULL; + if (MSG_IS_DELETED(msginfo->flags)) { + summaryview->deleted--; + MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED); + } + if (MSG_IS_MOVE(msginfo->flags)) + summaryview->moved--; + if (MSG_IS_COPY(msginfo->flags)) + summaryview->copied--; + MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY); + MSG_SET_PERM_FLAGS(msginfo->flags, MSG_MARKED); + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED); + summaryview->folder_item->mark_dirty = TRUE; + summary_set_row(summaryview, iter, msginfo); + + debug_print(_("Message %d is marked\n"), msginfo->msgnum); +} + +void summary_mark(SummaryView *summaryview) +{ + GList *rows, *cur; + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter; + FolderSortKey sort_key = SORT_BY_NONE; + FolderSortType sort_type = SORT_ASCENDING; + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP && + summary_is_read_locked(summaryview)) + return; + + summary_lock(summaryview); + SORT_BLOCK(SORT_BY_MARK); + + rows = summary_get_selected_rows(summaryview); + for (cur = rows; cur != NULL; cur = cur->next) { + GtkTreePath *path = (GtkTreePath *)cur->data; + gtk_tree_model_get_iter(model, &iter, path); + summary_mark_row(summaryview, &iter); + } + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) { + GSList *msglist; + + msglist = summary_get_selected_msg_list(summaryview); + imap_msg_list_set_perm_flags(msglist, MSG_MARKED); + g_slist_free(msglist); + } + + SORT_UNBLOCK(SORT_BY_MARK); + summary_unlock(summaryview); + + summary_status_show(summaryview); +} + +static void summary_mark_row_as_read(SummaryView *summaryview, + GtkTreeIter *iter) +{ + MsgInfo *msginfo = NULL; + + GET_MSG_INFO(msginfo, iter); + + if (MSG_IS_NEW(msginfo->flags)) { + if (summaryview->folder_item->new > 0) + summaryview->folder_item->new--; + if (summaryview->on_filter && summaryview->flt_new > 0) + summaryview->flt_new--; + inc_block_notify(TRUE); + } + if (MSG_IS_UNREAD(msginfo->flags)) { + if (summaryview->folder_item->unread > 0) + summaryview->folder_item->unread--; + if (summaryview->on_filter && summaryview->flt_unread > 0) + summaryview->flt_unread--; + } + + if (summaryview->folder_item->stype == F_VIRTUAL) { + if (MSG_IS_NEW(msginfo->flags) && msginfo->folder->new > 0) + msginfo->folder->new--; + if (MSG_IS_UNREAD(msginfo->flags) && + msginfo->folder->unread > 0) + msginfo->folder->unread--; + folderview_update_item(msginfo->folder, FALSE); + } + + if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) { + MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD); + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED); + summaryview->folder_item->mark_dirty = TRUE; + summary_set_row(summaryview, iter, msginfo); + debug_print(_("Message %d is marked as being read\n"), + msginfo->msgnum); + } +} + +void summary_mark_as_read(SummaryView *summaryview) +{ + GList *rows, *cur; + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter; + FolderSortKey sort_key = SORT_BY_NONE; + FolderSortType sort_type = SORT_ASCENDING; + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP && + summary_is_read_locked(summaryview)) + return; + + summary_lock(summaryview); + SORT_BLOCK(SORT_BY_UNREAD); + + rows = summary_get_selected_rows(summaryview); + + for (cur = rows; cur != NULL; cur = cur->next) { + GtkTreePath *path = (GtkTreePath *)cur->data; + + gtk_tree_model_get_iter(model, &iter, path); + summary_mark_row_as_read(summaryview, &iter); + } + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) { + GSList *msglist; + + msglist = summary_get_selected_msg_list(summaryview); + imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD); + g_slist_free(msglist); + } + + SORT_UNBLOCK(SORT_BY_UNREAD); + summary_unlock(summaryview); + + trayicon_set_tooltip(NULL); + trayicon_set_notify(FALSE); + summary_status_show(summaryview); +} + +static gboolean prepend_thread_rows_func(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + GSList **thr_rows = (GSList **)data; + GtkTreePath *top_path; + + top_path = gtk_tree_path_copy(path); + *thr_rows = g_slist_prepend(*thr_rows, top_path); + return FALSE; +} + +void summary_mark_thread_as_read(SummaryView *summaryview) +{ + GList *rows, *cur; + GSList *thr_rows = NULL, *top_rows = NULL, *s_cur; + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview); + GtkTreeIter iter, parent; + GtkTreePath *path, *top_path; + GHashTable *row_table; + MsgInfo *msginfo; + GSList *msglist = NULL; + FolderSortKey sort_key = SORT_BY_NONE; + FolderSortType sort_type = SORT_ASCENDING; + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP && + summary_is_read_locked(summaryview)) + return; + + summary_lock(summaryview); + SORT_BLOCK(SORT_BY_UNREAD); + + rows = summary_get_selected_rows(summaryview); + + row_table = g_hash_table_new(NULL, NULL); + + for (cur = rows; cur != NULL; cur = cur->next) { + path = (GtkTreePath *)cur->data; + gtk_tree_model_get_iter(model, &iter, path); + while (gtk_tree_model_iter_parent(model, &parent, &iter)) + iter = parent; + gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1); + if (!g_hash_table_lookup(row_table, msginfo)) { + g_hash_table_insert(row_table, msginfo, + GINT_TO_POINTER(1)); + top_path = gtk_tree_model_get_path(model, &iter); + top_rows = g_slist_prepend(top_rows, top_path); + gtkut_tree_model_foreach(model, &iter, + prepend_thread_rows_func, + &thr_rows); + } + } + top_rows = g_slist_reverse(top_rows); + thr_rows = g_slist_reverse(thr_rows); + + g_hash_table_destroy(row_table); + + for (s_cur = thr_rows; s_cur != NULL; s_cur = s_cur->next) { + path = (GtkTreePath *)s_cur->data; + gtk_tree_model_get_iter(model, &iter, path); + summary_mark_row_as_read(summaryview, &iter); + gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1); + msglist = g_slist_prepend(msglist, msginfo); + } + + if (prefs_common.bold_unread) { + for (s_cur = top_rows; s_cur != NULL; s_cur = s_cur->next) { + path = (GtkTreePath *)s_cur->data; + gtk_tree_model_get_iter(model, &iter, path); + if (gtk_tree_model_iter_has_child(model, &iter) && + !gtk_tree_view_row_expanded(treeview, path) && + !summary_have_unread_children(summaryview, &iter)) { + gtk_tree_store_set(GTK_TREE_STORE(model), &iter, + S_COL_BOLD, + PANGO_WEIGHT_NORMAL, -1); + } + } + } + msglist = g_slist_reverse(msglist); + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) { + imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD); + } + + g_slist_free(msglist); + g_slist_foreach(top_rows, (GFunc)gtk_tree_path_free, NULL); + g_slist_free(top_rows); + g_slist_foreach(thr_rows, (GFunc)gtk_tree_path_free, NULL); + g_slist_free(thr_rows); + + SORT_UNBLOCK(SORT_BY_UNREAD); + summary_unlock(summaryview); + + trayicon_set_tooltip(NULL); + trayicon_set_notify(FALSE); + summary_status_show(summaryview); +} + +void summary_mark_all_read(SummaryView *summaryview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter; + gboolean valid; + FolderSortKey sort_key = SORT_BY_NONE; + FolderSortType sort_type = SORT_ASCENDING; + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP && + summary_is_read_locked(summaryview)) + return; + + summary_lock(summaryview); + SORT_BLOCK(SORT_BY_UNREAD); + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) { + GSList *msglist; + msglist = summary_get_flagged_msg_list(summaryview, + MSG_NEW | MSG_UNREAD); + imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD); + g_slist_free(msglist); + } + + valid = gtk_tree_model_get_iter_first(model, &iter); + while (valid) { + summary_mark_row_as_read(summaryview, &iter); + if (prefs_common.bold_unread) { + if (gtk_tree_model_iter_has_child(model, &iter)) { + GtkTreePath *path; + + path = gtk_tree_model_get_path(model, &iter); + if (!gtk_tree_view_row_expanded + (GTK_TREE_VIEW(summaryview->treeview), + path)) + gtk_tree_store_set + (GTK_TREE_STORE(model), &iter, + S_COL_BOLD, + PANGO_WEIGHT_NORMAL, -1); + gtk_tree_path_free(path); + } + } + valid = gtkut_tree_model_next(model, &iter); + } + + SORT_UNBLOCK(SORT_BY_UNREAD); + summary_unlock(summaryview); + + trayicon_set_tooltip(NULL); + trayicon_set_notify(FALSE); + summary_status_show(summaryview); +} + +static void summary_mark_row_as_unread(SummaryView *summaryview, + GtkTreeIter *iter) +{ + MsgInfo *msginfo = NULL; + + GET_MSG_INFO(msginfo, iter); + + if (MSG_IS_DELETED(msginfo->flags)) { + msginfo->to_folder = NULL; + MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED); + summaryview->deleted--; + summaryview->folder_item->mark_dirty = TRUE; + } + MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_REPLIED | MSG_FORWARDED); + if (!MSG_IS_UNREAD(msginfo->flags)) { + MSG_SET_PERM_FLAGS(msginfo->flags, MSG_UNREAD); + summaryview->folder_item->unread++; + if (summaryview->on_filter) + summaryview->flt_unread++; + summaryview->folder_item->mark_dirty = TRUE; + if (summaryview->folder_item->stype == F_VIRTUAL) { + msginfo->folder->unread++; + folderview_update_item(msginfo->folder, FALSE); + } + debug_print(_("Message %d is marked as unread\n"), + msginfo->msgnum); + } + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED); + summary_set_row(summaryview, iter, msginfo); +} + +void summary_mark_as_unread(SummaryView *summaryview) +{ + GList *rows, *cur; + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter; + FolderSortKey sort_key = SORT_BY_NONE; + FolderSortType sort_type = SORT_ASCENDING; + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP && + summary_is_read_locked(summaryview)) + return; + + summary_lock(summaryview); + SORT_BLOCK(SORT_BY_UNREAD); + + rows = summary_get_selected_rows(summaryview); + for (cur = rows; cur != NULL; cur = cur->next) { + GtkTreePath *path = (GtkTreePath *)cur->data; + + gtk_tree_model_get_iter(model, &iter, path); + summary_mark_row_as_unread(summaryview, &iter); + } + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) { + GSList *msglist; + msglist = summary_get_selected_msg_list(summaryview); + imap_msg_list_unset_perm_flags(msglist, MSG_REPLIED); + imap_msg_list_set_perm_flags(msglist, MSG_UNREAD); + g_slist_free(msglist); + } + + SORT_UNBLOCK(SORT_BY_UNREAD); + summary_unlock(summaryview); + + summary_status_show(summaryview); +} + +static void summary_delete_row(SummaryView *summaryview, GtkTreeIter *iter) +{ + MsgInfo *msginfo = NULL; + + GET_MSG_INFO(msginfo, iter); + + if (MSG_IS_DELETED(msginfo->flags)) return; + + msginfo->to_folder = NULL; + if (MSG_IS_MOVE(msginfo->flags)) { + summaryview->moved--; + if (summaryview->on_filter) + summaryview->flt_moved--; + } + if (MSG_IS_COPY(msginfo->flags)) { + summaryview->copied--; + if (summaryview->on_filter) + summaryview->flt_copied--; + } + MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY); + MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED); + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED); + summaryview->deleted++; + if (summaryview->on_filter) + summaryview->flt_deleted++; + summaryview->folder_item->mark_dirty = TRUE; + + if (!prefs_common.immediate_exec && summaryview->tmp_flag == 0) + summary_set_row(summaryview, iter, msginfo); + + debug_print(_("Message %s/%d is set to delete\n"), + msginfo->folder->path, msginfo->msgnum); +} + +static gboolean summary_delete_foreach_func(GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + summary_delete_row((SummaryView *)data, iter); + return FALSE; +} + +void summary_delete(SummaryView *summaryview) +{ + FolderItem *item = summaryview->folder_item; + GList *rows, *cur; + GtkTreeIter last_sel, next; + GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview); + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + gboolean is_trash; + + if (!item || FOLDER_TYPE(item->folder) == F_NEWS) return; + + if (summary_is_locked(summaryview)) return; + + /* if current folder is trash, ask for confirmation */ + is_trash = folder_item_is_trash(item); + if (is_trash) { + AlertValue aval; + + aval = alertpanel(_("Delete message(s)"), + _("Do you really want to delete message(s) from the trash?"), + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if (aval != G_ALERTDEFAULT) return; + } + + rows = summary_get_selected_rows(summaryview); + if (!rows) + return; + + summaryview->tmp_flag = is_trash ? 1 : 0; + + /* next code sets current row focus right. We need to find a row + * that is not deleted. */ + for (cur = rows; cur != NULL; cur = cur->next) { + GtkTreePath *path = (GtkTreePath *)cur->data; + + gtk_tree_model_get_iter(model, &last_sel, path); + if (gtk_tree_model_iter_has_child(model, &last_sel) && + !gtk_tree_view_row_expanded(treeview, path)) { + gtkut_tree_model_foreach + (model, &last_sel, summary_delete_foreach_func, + summaryview); + } else + summary_delete_row(summaryview, &last_sel); + } + + summaryview->tmp_flag = 0; + + if (prefs_common.immediate_exec || is_trash) { + summary_execute(summaryview); + } else { + if (summary_find_nearest_msg(summaryview, &next, &last_sel)) { + summary_select_row + (summaryview, &next, + messageview_is_visible + (summaryview->messageview), + FALSE); + } + summary_status_show(summaryview); + } +} + +static gboolean summary_delete_duplicated_func(GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + SummaryView *summaryview = (SummaryView *)data; + MsgInfo *msginfo; + GtkTreeIter *found; + GtkTreePath *found_path; + + gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); + + if (!msginfo || !msginfo->msgid || !*msginfo->msgid) + return FALSE; + + found = g_hash_table_lookup(summaryview->msgid_table, msginfo->msgid); + + if (found) { + found_path = gtk_tree_model_get_path(model, found); + if (gtk_tree_path_compare(path, found_path) != 0) + summary_delete_row(summaryview, iter); + gtk_tree_path_free(found_path); + } + + return FALSE; +} + +void summary_delete_duplicated(SummaryView *summaryview) +{ + if (!summaryview->folder_item || + FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return; + if (folder_item_is_trash(summaryview->folder_item)) return; + + if (summary_is_locked(summaryview)) return; + + main_window_cursor_wait(summaryview->mainwin); + debug_print("Deleting duplicated messages..."); + STATUSBAR_PUSH(summaryview->mainwin, + _("Deleting duplicated messages...")); + + summary_msgid_table_create(summaryview); + + gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), + summary_delete_duplicated_func, summaryview); + + summary_msgid_table_destroy(summaryview); + + if (prefs_common.immediate_exec) + summary_execute(summaryview); + else + summary_status_show(summaryview); + + debug_print("done.\n"); + STATUSBAR_POP(summaryview->mainwin); + main_window_cursor_normal(summaryview->mainwin); +} + +static void summary_unmark_row(SummaryView *summaryview, GtkTreeIter *iter) +{ + MsgInfo *msginfo = NULL; + + GET_MSG_INFO(msginfo, iter); + + msginfo->to_folder = NULL; + if (MSG_IS_DELETED(msginfo->flags)) { + summaryview->deleted--; + if (summaryview->on_filter) + summaryview->flt_deleted--; + } + if (MSG_IS_MOVE(msginfo->flags)) { + summaryview->moved--; + if (summaryview->on_filter) + summaryview->flt_moved--; + } + if (MSG_IS_COPY(msginfo->flags)) { + summaryview->copied--; + if (summaryview->on_filter) + summaryview->flt_copied--; + } + MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED | MSG_DELETED); + MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY); + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED); + summaryview->folder_item->mark_dirty = TRUE; + summary_set_row(summaryview, iter, msginfo); + + debug_print(_("Message %s/%d is unmarked\n"), + msginfo->folder->path, msginfo->msgnum); +} + +void summary_unmark(SummaryView *summaryview) +{ + GList *rows, *cur; + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter; + FolderSortKey sort_key = SORT_BY_NONE; + FolderSortType sort_type = SORT_ASCENDING; + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP && + summary_is_read_locked(summaryview)) + return; + + summary_lock(summaryview); + SORT_BLOCK(SORT_BY_MARK); + + rows = summary_get_selected_rows(summaryview); + for (cur = rows; cur != NULL; cur = cur->next) { + GtkTreePath *path = (GtkTreePath *)cur->data; + gtk_tree_model_get_iter(model, &iter, path); + summary_unmark_row(summaryview, &iter); + } + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) { + GSList *msglist; + + msglist = summary_get_selected_msg_list(summaryview); + imap_msg_list_unset_perm_flags(msglist, MSG_MARKED); + g_slist_free(msglist); + } + + SORT_UNBLOCK(SORT_BY_MARK); + summary_unlock(summaryview); + + summary_status_show(summaryview); +} + +static void summary_move_row_to(SummaryView *summaryview, GtkTreeIter *iter, + FolderItem *to_folder) +{ + MsgInfo *msginfo; + + g_return_if_fail(to_folder != NULL); + if (to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL) + return; + + GET_MSG_INFO(msginfo, iter); + + msginfo->to_folder = to_folder; + if (MSG_IS_DELETED(msginfo->flags)) { + summaryview->deleted--; + if (summaryview->on_filter) + summaryview->flt_deleted--; + MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED); + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED); + } + MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY); + if (!MSG_IS_MOVE(msginfo->flags)) { + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MOVE); + summaryview->moved++; + if (summaryview->on_filter) + summaryview->flt_moved++; + } + summaryview->folder_item->mark_dirty = TRUE; + if (!prefs_common.immediate_exec) + summary_set_row(summaryview, iter, msginfo); + + debug_print(_("Message %d is set to move to %s\n"), + msginfo->msgnum, to_folder->path); +} + +static gboolean summary_move_foreach_func(GtkTreeModel *model, + GtkTreePath *path, GtkTreeIter *iter, + gpointer data) +{ + SummaryView *summaryview = (SummaryView *)data; + + summary_move_row_to(summaryview, iter, summaryview->to_folder); + return FALSE; +} + +void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder) +{ + GList *rows, *cur; + GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview); + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter; + + if (!to_folder) return; + if (!summaryview->folder_item || + FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) + return; + if (summaryview->folder_item->stype == F_QUEUE || + to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL) + return; + + if (summary_is_locked(summaryview)) return; + + if (summaryview->folder_item == to_folder) { + alertpanel_warning(_("Destination is same as current folder.")); + return; + } + + rows = summary_get_selected_rows(summaryview); + for (cur = rows; cur != NULL; cur = cur->next) { + GtkTreePath *path = (GtkTreePath *)cur->data; + + gtk_tree_model_get_iter(model, &iter, path); + if (gtk_tree_model_iter_has_child(model, &iter) && + !gtk_tree_view_row_expanded(treeview, path)) { + summaryview->to_folder = to_folder; + gtkut_tree_model_foreach + (model, &iter, summary_move_foreach_func, + summaryview); + summaryview->to_folder = NULL; + } else + summary_move_row_to(summaryview, &iter, to_folder); + } + + if (prefs_common.immediate_exec) + summary_execute(summaryview); + else { + summary_step(summaryview, GTK_SCROLL_STEP_FORWARD); + summary_status_show(summaryview); + } +} + +void summary_move_to(SummaryView *summaryview) +{ + FolderItem *to_folder; + + if (!summaryview->folder_item || + FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return; + + to_folder = foldersel_folder_sel_full(summaryview->folder_item->folder, + FOLDER_SEL_MOVE, NULL, + _("Select folder to move")); + summary_move_selected_to(summaryview, to_folder); +} + +static void summary_copy_row_to(SummaryView *summaryview, GtkTreeIter *iter, + FolderItem *to_folder) +{ + MsgInfo *msginfo; + + g_return_if_fail(to_folder != NULL); + if (to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL) + return; + + GET_MSG_INFO(msginfo, iter); + + msginfo->to_folder = to_folder; + if (MSG_IS_DELETED(msginfo->flags)) { + summaryview->deleted--; + if (summaryview->on_filter) + summaryview->flt_deleted--; + MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED); + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED); + } + MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE); + if (!MSG_IS_COPY(msginfo->flags)) { + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_COPY); + summaryview->copied++; + if (summaryview->on_filter) + summaryview->flt_copied++; + } + summaryview->folder_item->mark_dirty = TRUE; + if (!prefs_common.immediate_exec) + summary_set_row(summaryview, iter, msginfo); + + debug_print(_("Message %d is set to copy to %s\n"), + msginfo->msgnum, to_folder->path); +} + +static gboolean summary_copy_foreach_func(GtkTreeModel *model, + GtkTreePath *path, GtkTreeIter *iter, + gpointer data) +{ + SummaryView *summaryview = (SummaryView *)data; + + summary_copy_row_to(summaryview, iter, summaryview->to_folder); + return FALSE; +} + +void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder) +{ + GList *rows, *cur; + GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview); + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter; + + if (!to_folder) return; + if (!summaryview->folder_item) return; + if (summaryview->folder_item->stype == F_QUEUE || + to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL) + return; + + if (summary_is_locked(summaryview)) return; + + if (summaryview->folder_item == to_folder) { + alertpanel_warning + (_("Destination for copy is same as current folder.")); + return; + } + + rows = summary_get_selected_rows(summaryview); + for (cur = rows; cur != NULL; cur = cur->next) { + GtkTreePath *path = (GtkTreePath *)cur->data; + + gtk_tree_model_get_iter(model, &iter, path); + if (gtk_tree_model_iter_has_child(model, &iter) && + !gtk_tree_view_row_expanded(treeview, path)) { + summaryview->to_folder = to_folder; + gtkut_tree_model_foreach + (model, &iter, summary_copy_foreach_func, + summaryview); + summaryview->to_folder = NULL; + } else + summary_copy_row_to(summaryview, &iter, to_folder); + } + + if (prefs_common.immediate_exec) + summary_execute(summaryview); + else { + summary_step(summaryview, GTK_SCROLL_STEP_FORWARD); + summary_status_show(summaryview); + } +} + +void summary_copy_to(SummaryView *summaryview) +{ + FolderItem *to_folder; + + if (!summaryview->folder_item) return; + + to_folder = foldersel_folder_sel_full(summaryview->folder_item->folder, + FOLDER_SEL_COPY, NULL, + _("Select folder to copy")); + summary_copy_selected_to(summaryview, to_folder); +} + +void summary_add_address(SummaryView *summaryview) +{ + GtkTreeIter iter; + MsgInfo *msginfo = NULL; + gchar from[BUFFSIZE]; + + if (!summaryview->selected) return; + + if (!gtkut_tree_row_reference_get_iter + (GTK_TREE_MODEL(summaryview->store), + summaryview->selected, &iter)) + return; + + GET_MSG_INFO(msginfo, &iter); + strncpy2(from, msginfo->from, sizeof(from)); + eliminate_address_comment(from); + extract_address(from); + addressbook_add_contact(msginfo->fromname, from, NULL); +} + +void summary_select_all(SummaryView *summaryview) +{ + gtk_tree_selection_select_all(summaryview->selection); +} + +void summary_unselect_all(SummaryView *summaryview) +{ + gtk_tree_selection_unselect_all(summaryview->selection); +} + +void summary_select_thread(SummaryView *summaryview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter, parent, child, next; + GtkTreePath *start_path, *end_path; + gboolean valid; + + valid = gtkut_tree_row_reference_get_iter(model, summaryview->selected, + &iter); + if (!valid) + return; + + while (gtk_tree_model_iter_parent(model, &parent, &iter)) + iter = parent; + + if (!gtk_tree_model_iter_children(model, &child, &iter)) + return; + + start_path = gtk_tree_model_get_path(model, &iter); + + for (;;) { + next = iter = child; + while (gtk_tree_model_iter_next(model, &next)) + iter = next; + if (!gtk_tree_model_iter_children(model, &child, &iter)) + break; + } + + end_path = gtk_tree_model_get_path(model, &iter); + + gtk_tree_view_expand_row(GTK_TREE_VIEW(summaryview->treeview), + start_path, TRUE); + gtk_tree_selection_select_range(summaryview->selection, + start_path, end_path); + + gtk_tree_path_free(end_path); + gtk_tree_path_free(start_path); +} + +void summary_save_as(SummaryView *summaryview) +{ + GtkTreeIter iter; + MsgInfo *msginfo = NULL; + gchar *filename; + gchar *src, *dest; + FileselFileType types[4] = {{NULL, NULL}}; + gint selected_type = 0; + gint result; + gboolean all_headers; + + if (!summaryview->selected) return; + if (!gtkut_tree_row_reference_get_iter + (GTK_TREE_MODEL(summaryview->store), + summaryview->selected, &iter)) + return; + + GET_MSG_INFO(msginfo, &iter); + if (!msginfo) return; + + if (msginfo->subject && *msginfo->subject) { + filename = g_strdup_printf("%s.eml", msginfo->subject); + } else { + filename = g_strdup_printf("%u.eml", msginfo->msgnum); + } + subst_for_filename(filename); + + types[0].type = _("Original (EML/RFC 822)"); + types[0].ext = "eml"; + types[1].type = _("Text"); + types[1].ext = "txt"; + types[2].type = _("Text (UTF-8)"); + types[2].ext = "txt"; + + dest = filesel_save_as_type(filename, types, prefs_common.save_file_type, &selected_type); + + g_free(filename); + if (!dest) + return; + + debug_print("summary_save_as: selected_type: %d\n", selected_type); + + all_headers = summaryview->messageview->textview->show_all_headers; + + if (selected_type == 1) { + result = procmsg_save_message_as_text(msginfo, dest, conv_get_locale_charset_str(), all_headers); + } else if (selected_type == 2) { + result = procmsg_save_message_as_text(msginfo, dest, NULL, all_headers); + } else { + src = procmsg_get_message_file(msginfo); + result = copy_file(src, dest, TRUE); + g_free(src); + } + + if (result < 0) { + gchar *utf8_dest; + + utf8_dest = conv_filename_to_utf8(dest); + alertpanel_error(_("Can't save the file `%s'."), + g_basename(utf8_dest)); + g_free(utf8_dest); + } + + g_free(dest); + + prefs_common.save_file_type = selected_type; +} + +void summary_print(SummaryView *summaryview) +{ + GSList *mlist; + gboolean all_headers; + + all_headers = summaryview->messageview->textview->show_all_headers; + mlist = summary_get_selected_msg_list(summaryview); + if (!mlist) + return; + printing_print_messages(mlist, all_headers); + g_slist_free(mlist); +} + +gboolean summary_execute(SummaryView *summaryview) +{ + gint val = 0; + + if (!summaryview->folder_item) return FALSE; + + if (summary_is_locked(summaryview)) return FALSE; + summary_lock(summaryview); + + val |= summary_execute_move(summaryview); + val |= summary_execute_copy(summaryview); + val |= summary_execute_delete(summaryview); + + summary_unlock(summaryview); + + summary_remove_invalid_messages(summaryview); + + statusbar_pop_all(); + STATUSBAR_POP(summaryview->mainwin); + + if (val != 0) { + alertpanel_error(_("Error occurred while processing messages.")); + } + + return TRUE; +} + +static void summary_remove_invalid_messages(SummaryView *summaryview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + MsgInfo *disp_msginfo = NULL, *msginfo; + FolderItem *item = summaryview->folder_item; + GtkTreeIter iter, next; + GtkTreePath *path; + gboolean valid; + + /* get currently displayed message */ + if (summaryview->displayed) { + GtkTreeIter displayed; + + valid = gtkut_tree_row_reference_get_iter + (model, summaryview->displayed, &displayed); + if (valid) { + gtk_tree_model_get(model, &displayed, + S_COL_MSG_INFO, &disp_msginfo, -1); + if (MSG_IS_INVALID(disp_msginfo->flags)) { + valid = FALSE; + disp_msginfo = NULL; + } + } + if (!valid) { + /* g_print("displayed became invalid before removing\n"); */ + messageview_clear(summaryview->messageview); + gtk_tree_row_reference_free(summaryview->displayed); + summaryview->displayed = NULL; + } + } + + if (item->threaded) + summary_modify_threads(summaryview); + + /* update selection */ + valid = gtkut_tree_row_reference_get_iter(model, summaryview->selected, + &iter); + if (valid) { + valid = summary_find_nearest_msg(summaryview, &next, &iter); + if (valid) { + gboolean display; + + gtk_tree_model_get(model, &next, + S_COL_MSG_INFO, &msginfo, -1); + if (disp_msginfo && disp_msginfo == msginfo) { + /* g_print("replace displayed\n"); */ + path = gtk_tree_model_get_path(model, &next); + gtk_tree_row_reference_free + (summaryview->displayed); + summaryview->displayed = + gtk_tree_row_reference_new(model, path); + gtk_tree_path_free(path); + } + display = prefs_common.immediate_exec && + messageview_is_visible + (summaryview->messageview); + summary_select_row + (summaryview, &next, display, FALSE); + if (display) + summary_mark_displayed_read(summaryview, &next); + } + } + if (!valid) + summary_unselect_all(summaryview); + + for (valid = gtk_tree_model_get_iter_first(model, &iter); + valid == TRUE; iter = next) { + next = iter; + valid = gtkut_tree_model_next(model, &next); + + gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1); + if (!MSG_IS_INVALID(msginfo->flags)) + continue; + + if (gtk_tree_model_iter_has_child(model, &iter)) { + g_warning("summary_remove_invalid_messages(): " + "tried to remove row which has child\n"); + continue; + } + + gtk_tree_store_remove(GTK_TREE_STORE(model), &iter); + summaryview->all_mlist = g_slist_remove(summaryview->all_mlist, + msginfo); + if (summaryview->flt_mlist) + summaryview->flt_mlist = + g_slist_remove(summaryview->flt_mlist, msginfo); + procmsg_msginfo_free(msginfo); + + item->cache_dirty = TRUE; + } + + /* selection list becomes invalid if modified */ + if (item->cache_dirty) + summary_selection_list_free(summaryview); + + if (summaryview->displayed && + !gtk_tree_row_reference_valid(summaryview->displayed)) { + /* g_print("displayed became invalid after removing. searching disp_msginfo...\n"); */ + if (disp_msginfo && + gtkut_tree_model_find_by_column_data + (model, &iter, NULL, S_COL_MSG_INFO, disp_msginfo)) { + /* g_print("replace displayed\n"); */ + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_row_reference_free(summaryview->displayed); + summaryview->displayed = + gtk_tree_row_reference_new(model, path); + gtk_tree_path_free(path); + } else { + messageview_clear(summaryview->messageview); + gtk_tree_row_reference_free(summaryview->displayed); + summaryview->displayed = NULL; + } + } + + if (gtk_tree_model_get_iter_first(model, &iter)) + gtk_widget_grab_focus(summaryview->treeview); + else { + menu_set_insensitive_all + (GTK_MENU_SHELL(summaryview->popupmenu)); + gtk_widget_grab_focus(summaryview->folderview->treeview); + } + + summary_write_cache(summaryview); + + summary_update_status(summaryview); + summary_status_show(summaryview); +} + +static gboolean summary_execute_move_func(GtkTreeModel *model, + GtkTreePath *path, GtkTreeIter *iter, + gpointer data) +{ + SummaryView *summaryview = data; + MsgInfo *msginfo; + + gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); + + if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) { + g_hash_table_insert(summaryview->folder_table, + msginfo->to_folder, GINT_TO_POINTER(1)); + + summaryview->tmp_mlist = + g_slist_prepend(summaryview->tmp_mlist, msginfo); + + MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE); + summary_set_row(summaryview, iter, msginfo); + } + + return FALSE; +} + +static gint summary_execute_move(SummaryView *summaryview) +{ + gint val = 0; + + summaryview->folder_table = g_hash_table_new(NULL, NULL); + + /* search moving messages and execute */ + gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), + summary_execute_move_func, summaryview); + + if (summaryview->tmp_mlist) { + summaryview->tmp_mlist = + g_slist_reverse(summaryview->tmp_mlist); + val = procmsg_move_messages(summaryview->tmp_mlist); + + folderview_update_item_foreach(summaryview->folder_table, + FALSE); + + g_slist_free(summaryview->tmp_mlist); + summaryview->tmp_mlist = NULL; + } + + g_hash_table_destroy(summaryview->folder_table); + summaryview->folder_table = NULL; + + return val; +} + +static gboolean summary_execute_copy_func(GtkTreeModel *model, + GtkTreePath *path, GtkTreeIter *iter, + gpointer data) +{ + SummaryView *summaryview = data; + MsgInfo *msginfo; + + gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); + + if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) { + g_hash_table_insert(summaryview->folder_table, + msginfo->to_folder, GINT_TO_POINTER(1)); + + summaryview->tmp_mlist = + g_slist_prepend(summaryview->tmp_mlist, msginfo); + + MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY); + summary_set_row(summaryview, iter, msginfo); + } + + return FALSE; +} + +static gint summary_execute_copy(SummaryView *summaryview) +{ + gint val = 0; + + summaryview->folder_table = g_hash_table_new(NULL, NULL); + + /* search copying messages and execute */ + gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), + summary_execute_copy_func, summaryview); + + if (summaryview->tmp_mlist) { + summaryview->tmp_mlist = + g_slist_reverse(summaryview->tmp_mlist); + val = procmsg_copy_messages(summaryview->tmp_mlist); + + folderview_update_item_foreach(summaryview->folder_table, + FALSE); + + g_slist_free(summaryview->tmp_mlist); + summaryview->tmp_mlist = NULL; + } + + g_hash_table_destroy(summaryview->folder_table); + summaryview->folder_table = NULL; + + return val; +} + +static gboolean summary_execute_delete_func(GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + SummaryView *summaryview = data; + MsgInfo *msginfo; + + gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); + + if (msginfo && MSG_IS_DELETED(msginfo->flags)) { + summaryview->tmp_mlist = + g_slist_prepend(summaryview->tmp_mlist, msginfo); + } + + return FALSE; +} + +static gint summary_execute_delete(SummaryView *summaryview) +{ + FolderItem *trash = NULL; + PrefsAccount *ac; + gint val = 0; + + ac = account_find_from_item_property(summaryview->folder_item); + if (ac && ac->set_trash_folder && ac->trash_folder) + trash = folder_find_item_from_identifier(ac->trash_folder); + if (!trash) + trash = summaryview->folder_item->folder->trash; + if (!trash) + folder_get_default_trash(); + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_MH) { + g_return_val_if_fail(trash != NULL, 0); + } + + /* search deleting messages and execute */ + gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), + summary_execute_delete_func, summaryview); + + if (!summaryview->tmp_mlist) return 0; + + summaryview->tmp_mlist = g_slist_reverse(summaryview->tmp_mlist); + + if (summaryview->folder_item != trash) + val = folder_item_move_msgs(trash, summaryview->tmp_mlist); + else + val = folder_item_remove_msgs(trash, summaryview->tmp_mlist); + + g_slist_free(summaryview->tmp_mlist); + summaryview->tmp_mlist = NULL; + + if (summaryview->folder_item != trash) { + folder_item_scan(trash); + folderview_update_item(trash, FALSE); + } + + return val == -1 ? -1 : 0; +} + +/* thread functions */ + +void summary_thread_build(SummaryView *summaryview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeStore *store = summaryview->store; + GtkTreeIter iter, next; + GtkTreePath *path; + GSList *mlist; + GNode *root, *node; + GHashTable *node_table; + MsgInfo *msginfo; + gboolean valid; + FolderSortKey sort_key = SORT_BY_NONE; + FolderSortType sort_type = SORT_ASCENDING; + MsgInfo *selected_msg, *displayed_msg; + + if (!summaryview->folder_item) + return; + + summary_lock(summaryview); + + debug_print(_("Building threads...")); + STATUSBAR_PUSH(summaryview->mainwin, _("Building threads...")); + main_window_cursor_wait(summaryview->mainwin); + + g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview), + (GSignalMatchType)G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, summaryview); + + selected_msg = summary_get_msginfo(summaryview, summaryview->selected); + displayed_msg = summary_get_msginfo + (summaryview, summaryview->displayed); + + summaryview->folder_item->threaded = TRUE; + + mlist = summary_get_msg_list(summaryview); + root = procmsg_get_thread_tree(mlist); + node_table = g_hash_table_new(NULL, NULL); + for (node = root->children; node != NULL; node = node->next) { + g_hash_table_insert(node_table, node->data, node); + } + + if (summaryview->folder_item->sort_key != SORT_BY_NONE) { + sort_key = summaryview->folder_item->sort_key; + sort_type = summaryview->folder_item->sort_type; + summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING); + } + + valid = gtk_tree_model_get_iter_first(model, &next); + while (valid) { + iter = next; + valid = gtk_tree_model_iter_next(model, &next); + + gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1); + node = g_hash_table_lookup(node_table, msginfo); + if (node) { + GNode *cur; + GtkTreeIter child; + guint tdate; + + for (cur = node->children; cur != NULL; + cur = cur->next) { + summary_insert_gnode(summaryview, store, &child, + &iter, NULL, cur); + } + + tdate = procmsg_get_thread_date(node); + gtk_tree_store_set(store, &iter, S_COL_TDATE, tdate, -1); + } else + gtk_tree_store_remove(store, &iter); + } + + if (sort_key != SORT_BY_NONE) + summary_sort(summaryview, sort_key, sort_type); + + g_hash_table_destroy(node_table); + g_node_destroy(root); + g_slist_free(mlist); + + if (prefs_common.expand_thread) + gtk_tree_view_expand_all(GTK_TREE_VIEW(summaryview->treeview)); + + if (!summaryview->selected || + (summaryview->selected && + !gtk_tree_row_reference_valid(summaryview->selected))) { + if (selected_msg && + gtkut_tree_model_find_by_column_data + (model, &iter, NULL, S_COL_MSG_INFO, selected_msg)) { + summary_select_row(summaryview, &iter, FALSE, TRUE); + } + } else + summary_scroll_to_selected(summaryview, TRUE); + + if (summaryview->displayed && + !gtk_tree_row_reference_valid(summaryview->displayed)) { + if (displayed_msg && + gtkut_tree_model_find_by_column_data + (model, &iter, NULL, S_COL_MSG_INFO, displayed_msg)) { + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_row_reference_free(summaryview->displayed); + summaryview->displayed = + gtk_tree_row_reference_new(model, path); + gtk_tree_path_free(path); + } else { + messageview_clear(summaryview->messageview); + gtk_tree_row_reference_free(summaryview->displayed); + summaryview->displayed = NULL; + } + } + + g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview), + (GSignalMatchType)G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, summaryview); + + debug_print(_("done.\n")); + STATUSBAR_POP(summaryview->mainwin); + main_window_cursor_normal(summaryview->mainwin); + + summary_unlock(summaryview); +} + +static void summary_unthread_node_recursive(SummaryView *summaryview, + GtkTreeIter *iter, + GtkTreeIter *sibling) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter_, child; + MsgInfo *msginfo; + gboolean valid; + + gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); + gtk_tree_store_insert_after(GTK_TREE_STORE(model), &iter_, + NULL, sibling); + summary_set_row(summaryview, &iter_, msginfo); + *sibling = iter_; + + valid = gtk_tree_model_iter_children(model, &child, iter); + while (valid) { + summary_unthread_node_recursive(summaryview, &child, sibling); + valid = gtk_tree_model_iter_next(model, &child); + } +} + +static void summary_unthread_node(SummaryView *summaryview, GtkTreeIter *iter) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter child, sibling, next; + gboolean valid; + + sibling = *iter; + + valid = gtk_tree_model_iter_children(model, &next, iter); + while (valid) { + child = next; + valid = gtk_tree_model_iter_next(model, &next); + summary_unthread_node_recursive(summaryview, &child, &sibling); + gtk_tree_store_remove(GTK_TREE_STORE(model), &child); + } +} + +void summary_unthread(SummaryView *summaryview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter, next; + GtkTreePath *path; + gboolean valid; + FolderSortKey sort_key = SORT_BY_NONE; + FolderSortType sort_type = SORT_ASCENDING; + MsgInfo *selected_msg, *displayed_msg; + + if (!summaryview->folder_item) + return; + + summary_lock(summaryview); + + debug_print(_("Unthreading...")); + STATUSBAR_PUSH(summaryview->mainwin, _("Unthreading...")); + main_window_cursor_wait(summaryview->mainwin); + + g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview), + (GSignalMatchType)G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, summaryview); + + selected_msg = summary_get_msginfo(summaryview, summaryview->selected); + displayed_msg = summary_get_msginfo + (summaryview, summaryview->displayed); + + summaryview->folder_item->threaded = FALSE; + + if (summaryview->folder_item->sort_key != SORT_BY_NONE) { + sort_key = summaryview->folder_item->sort_key; + sort_type = summaryview->folder_item->sort_type; + summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING); + } + + valid = gtk_tree_model_get_iter_first(model, &next); + while (valid) { + iter = next; + valid = gtk_tree_model_iter_next(model, &next); + gtk_tree_store_set(GTK_TREE_STORE(model), &iter, + S_COL_TDATE, 0, -1); + } + + valid = gtk_tree_model_get_iter_first(model, &next); + while (valid) { + iter = next; + valid = gtk_tree_model_iter_next(model, &next); + summary_unthread_node(summaryview, &iter); + } + + if (sort_key != SORT_BY_NONE) + summary_sort(summaryview, sort_key, sort_type); + + if (!summaryview->selected || + (summaryview->selected && + !gtk_tree_row_reference_valid(summaryview->selected))) { + if (selected_msg && + gtkut_tree_model_find_by_column_data + (model, &iter, NULL, S_COL_MSG_INFO, selected_msg)) { + summary_select_row(summaryview, &iter, FALSE, TRUE); + } + } else + summary_scroll_to_selected(summaryview, TRUE); + + if (summaryview->displayed && + !gtk_tree_row_reference_valid(summaryview->displayed)) { + if (displayed_msg && + gtkut_tree_model_find_by_column_data + (model, &iter, NULL, S_COL_MSG_INFO, displayed_msg)) { + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_row_reference_free(summaryview->displayed); + summaryview->displayed = + gtk_tree_row_reference_new(model, path); + gtk_tree_path_free(path); + } else { + messageview_clear(summaryview->messageview); + gtk_tree_row_reference_free(summaryview->displayed); + summaryview->displayed = NULL; + } + } + + g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview), + (GSignalMatchType)G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, summaryview); + + debug_print(_("done.\n")); + STATUSBAR_POP(summaryview->mainwin); + main_window_cursor_normal(summaryview->mainwin); + + summary_unlock(summaryview); +} + +static gboolean summary_has_invalid_node(GtkTreeModel *model, GtkTreeIter *iter) +{ + GtkTreeIter child; + MsgInfo *msginfo; + gboolean valid; + + gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); + if (MSG_IS_INVALID(msginfo->flags)) + return TRUE; + + valid = gtk_tree_model_iter_children(model, &child, iter); + while (valid) { + if (summary_has_invalid_node(model, &child)) + return TRUE; + valid = gtk_tree_model_iter_next(model, &child); + } + + return FALSE; +} + +static GNode *summary_get_modified_node(SummaryView *summaryview, + GtkTreeIter *iter, + GNode *parent, GNode *sibling) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GNode *node = NULL, *new_sibling; + GtkTreeIter child; + MsgInfo *msginfo; + gboolean valid; + + gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); + + if (!MSG_IS_INVALID(msginfo->flags)) { + node = g_node_new(msginfo); + g_node_insert_after(parent, sibling, node); + parent = node; + sibling = NULL; + } else { + summaryview->all_mlist = g_slist_remove(summaryview->all_mlist, + msginfo); + if (summaryview->flt_mlist) + summaryview->flt_mlist = + g_slist_remove(summaryview->flt_mlist, msginfo); + procmsg_msginfo_free(msginfo); + } + + valid = gtk_tree_model_iter_children(model, &child, iter); + + while (valid) { + new_sibling = summary_get_modified_node(summaryview, &child, + parent, sibling); + if (new_sibling) { + sibling = new_sibling; + if (!node) + node = sibling; + } + valid = gtk_tree_model_iter_next(model, &child); + } + + return node; +} + +#if 0 +static gboolean traverse(GNode *node, gpointer data) +{ + gint i; + + if (!node->data) + return FALSE; + for (i = 0; i < g_node_depth(node); i++) + g_print(" "); + g_print("%s\n", ((MsgInfo *)node->data)->subject); + return FALSE; +} +#endif + +static void summary_modify_node(SummaryView *summaryview, GtkTreeIter *iter, + GtkTreeIter *selected) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + MsgInfo *msginfo, *sel_msginfo = NULL; + GNode *root, *cur; + GtkTreeIter iter_, sibling; + GtkTreeIter *sibling_p = NULL; + GtkTreePath *path, *sel_path; + gboolean found = FALSE; + + if (!gtk_tree_model_iter_has_child(model, iter)) + return; + if (!summary_has_invalid_node(model, iter)) + return; + + gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); + + if (selected) { + path = gtk_tree_model_get_path(model, iter); + sel_path = gtk_tree_model_get_path(model, selected); + if (gtk_tree_path_compare(path, sel_path) == 0 || + gtk_tree_path_is_ancestor(path, sel_path)) + gtk_tree_model_get(model, selected, + S_COL_MSG_INFO, &sel_msginfo, -1); + gtk_tree_path_free(sel_path); + gtk_tree_path_free(path); + } + + root = g_node_new(NULL); + summary_get_modified_node(summaryview, iter, root, NULL); + + /* g_node_traverse(root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, traverse, NULL); */ + + sibling = *iter; + if (gtk_tree_model_iter_next(model, &sibling)) + sibling_p = &sibling; + + gtk_tree_store_remove(GTK_TREE_STORE(model), iter); + + for (cur = root->children; cur != NULL; cur = cur->next) { + summary_insert_gnode_before(summaryview, GTK_TREE_STORE(model), + &iter_, NULL, sibling_p, cur); + if (summaryview->folder_item->threaded && + prefs_common.expand_thread) { + path = gtk_tree_model_get_path(model, &iter_); + gtk_tree_view_expand_row + (GTK_TREE_VIEW(summaryview->treeview), + path, TRUE); + gtk_tree_path_free(path); + } + if (sel_msginfo && !found) { + found = gtkut_tree_model_find_by_column_data + (model, selected, &iter_, + S_COL_MSG_INFO, sel_msginfo); + } + } + + g_node_destroy(root); + + summaryview->folder_item->cache_dirty = TRUE; +} + +static void summary_modify_threads(SummaryView *summaryview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter, next, selected, new_selected; + GtkTreeIter *selected_p = NULL; + GtkTreePath *prev_path = NULL; + gboolean valid, has_selection; + + summary_lock(summaryview); + + debug_print("Modifying threads for execution..."); + + g_signal_handlers_block_by_func(summaryview->selection, + summary_selection_changed, summaryview); + + has_selection = gtkut_tree_row_reference_get_iter + (model, summaryview->selected, &selected); + if (has_selection) { + prev_path = gtk_tree_row_reference_get_path + (summaryview->selected); + if (summary_find_nearest_msg(summaryview, &next, &selected)) { + selected = next; + selected_p = &selected; + } else + has_selection = FALSE; + } + + valid = gtk_tree_model_get_iter_first(model, &next); + while (valid) { + iter = next; + valid = gtk_tree_model_iter_next(model, &next); + summary_modify_node(summaryview, &iter, selected_p); + } + + g_signal_handlers_unblock_by_func(summaryview->selection, + summary_selection_changed, + summaryview); + + if (summaryview->folder_item->cache_dirty) + summary_selection_list_free(summaryview); + + if (has_selection && + !gtk_tree_row_reference_valid(summaryview->selected)) { + if (prev_path && + gtk_tree_model_get_iter(model, &new_selected, prev_path)) + selected = new_selected; + summary_select_row(summaryview, &selected, FALSE, FALSE); + } + gtk_tree_path_free(prev_path); + + debug_print("done.\n"); + + summary_unlock(summaryview); +} + +void summary_expand_threads(SummaryView *summaryview) +{ + gtk_tree_view_expand_all(GTK_TREE_VIEW(summaryview->treeview)); +} + +void summary_collapse_threads(SummaryView *summaryview) +{ + gtk_tree_view_collapse_all(GTK_TREE_VIEW(summaryview->treeview)); +} + +static gboolean summary_filter_func(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + SummaryView *summaryview = (SummaryView *)data; + MsgInfo *msginfo; + FilterInfo *fltinfo; + + gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); + + summaryview->flt_count++; + { + gchar msg[1024]; + g_snprintf(msg, sizeof(msg), _("Filtering (%d / %d)..."), + summaryview->flt_count, summaryview->flt_total); + STATUSBAR_POP(summaryview->mainwin); + STATUSBAR_PUSH(summaryview->mainwin, msg); + if ((summaryview->flt_count % 100) == 0) { + GTK_EVENTS_FLUSH(); + } + } + + fltinfo = filter_info_new(); + fltinfo->flags = msginfo->flags; + filter_apply_msginfo(prefs_common.fltlist, msginfo, fltinfo); + if (fltinfo->actions[FLT_ACTION_MOVE] || + fltinfo->actions[FLT_ACTION_COPY] || + fltinfo->actions[FLT_ACTION_DELETE] || + fltinfo->actions[FLT_ACTION_EXEC] || + fltinfo->actions[FLT_ACTION_EXEC_ASYNC] || + fltinfo->actions[FLT_ACTION_MARK] || + fltinfo->actions[FLT_ACTION_COLOR_LABEL] || + fltinfo->actions[FLT_ACTION_MARK_READ] || + fltinfo->actions[FLT_ACTION_FORWARD] || + fltinfo->actions[FLT_ACTION_FORWARD_AS_ATTACHMENT] || + fltinfo->actions[FLT_ACTION_REDIRECT]) + summaryview->filtered++; + + if (msginfo->flags.perm_flags != fltinfo->flags.perm_flags) { + msginfo->flags = fltinfo->flags; + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED); + summaryview->folder_item->mark_dirty = TRUE; + summary_set_row(summaryview, iter, msginfo); + if (MSG_IS_IMAP(msginfo->flags)) { + if (fltinfo->actions[FLT_ACTION_MARK]) + imap_msg_set_perm_flags(msginfo, MSG_MARKED); + if (fltinfo->actions[FLT_ACTION_MARK_READ]) + imap_msg_unset_perm_flags(msginfo, + MSG_NEW|MSG_UNREAD); + } + } + + if (fltinfo->actions[FLT_ACTION_MOVE] && fltinfo->move_dest) + summary_move_row_to(summaryview, iter, fltinfo->move_dest); + else if (fltinfo->actions[FLT_ACTION_DELETE]) + summary_delete_row(summaryview, iter); + + filter_info_free(fltinfo); + + return FALSE; +} + +static gboolean summary_filter_junk_func(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + SummaryView *summaryview = (SummaryView *)data; + MsgInfo *msginfo; + FilterInfo *fltinfo; + + gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); + + summaryview->flt_count++; + { + gchar msg[1024]; + g_snprintf(msg, sizeof(msg), _("Filtering (%d / %d)..."), + summaryview->flt_count, summaryview->flt_total); + STATUSBAR_POP(summaryview->mainwin); + STATUSBAR_PUSH(summaryview->mainwin, msg); + if ((summaryview->flt_count % 100) == 0) { + GTK_EVENTS_FLUSH(); + } + } + + fltinfo = filter_info_new(); + fltinfo->flags = msginfo->flags; + filter_apply_msginfo(summaryview->junk_fltlist, msginfo, fltinfo); + + if (fltinfo->actions[FLT_ACTION_MOVE] || + fltinfo->actions[FLT_ACTION_COPY] || + fltinfo->actions[FLT_ACTION_DELETE] || + fltinfo->actions[FLT_ACTION_MARK_READ]) + summaryview->filtered++; + else if (fltinfo->error == FLT_ERROR_EXEC_FAILED || + fltinfo->last_exec_exit_status >= 3) { + if (summaryview->flt_count == 1) { + g_warning("summary_filter_junk_func: junk filter command returned %d", fltinfo->last_exec_exit_status); + alertpanel_error + (_("Execution of the junk filter command failed.\n" + "Please check the junk mail control setting.")); + } + return TRUE; + } + + if (msginfo->flags.perm_flags != fltinfo->flags.perm_flags) { + msginfo->flags = fltinfo->flags; + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED); + summaryview->folder_item->mark_dirty = TRUE; + summary_set_row(summaryview, iter, msginfo); + if (MSG_IS_IMAP(msginfo->flags)) { + if (fltinfo->actions[FLT_ACTION_MARK_READ]) + imap_msg_unset_perm_flags(msginfo, + MSG_NEW|MSG_UNREAD); + } + } + + if (fltinfo->actions[FLT_ACTION_MOVE] && fltinfo->move_dest) + summary_move_row_to(summaryview, iter, fltinfo->move_dest); + else if (fltinfo->actions[FLT_ACTION_DELETE]) + summary_delete_row(summaryview, iter); + + filter_info_free(fltinfo); + + return FALSE; +} + +static void summary_filter_real(SummaryView *summaryview, + GtkTreeModelForeachFunc func, + gboolean selected_only) +{ + GList *rows; + FolderSortKey sort_key; + FolderSortType sort_type; + + if (!summaryview->folder_item) return; + + if (summary_is_locked(summaryview)) return; + summary_lock(summaryview); + + STATUSBAR_POP(summaryview->mainwin); + + debug_print(_("filtering...")); + STATUSBAR_PUSH(summaryview->mainwin, _("Filtering...")); + main_window_cursor_wait(summaryview->mainwin); + GTK_EVENTS_FLUSH(); + + sort_key = summaryview->folder_item->sort_key; + sort_type = summaryview->folder_item->sort_type; + if (sort_key != SORT_BY_NONE) + summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING); + + summaryview->filtered = 0; + summaryview->flt_count = 0; + + if (selected_only) { + rows = summary_get_selected_rows(summaryview); + summaryview->flt_total = g_list_length(rows); + + gtk_tree_selection_selected_foreach + (summaryview->selection, + (GtkTreeSelectionForeachFunc)func, + summaryview); + } else { + summaryview->flt_total = summaryview->folder_item->total; + gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), + func, summaryview); + } + + if (sort_key != SORT_BY_NONE) + summary_sort(summaryview, sort_key, sort_type); + + summary_unlock(summaryview); + + if (prefs_common.immediate_exec) + summary_execute(summaryview); + else + summary_status_show(summaryview); + + folderview_update_all_updated(FALSE); + + debug_print(_("done.\n")); + STATUSBAR_POP(summaryview->mainwin); + main_window_cursor_normal(summaryview->mainwin); + + if (summaryview->filtered > 0) { + gchar result_msg[BUFFSIZE]; + g_snprintf(result_msg, sizeof(result_msg), + _("%d message(s) have been filtered."), + summaryview->filtered); + STATUSBAR_PUSH(summaryview->mainwin, result_msg); + } + summaryview->filtered = 0; + summaryview->flt_count = 0; + summaryview->flt_total = 0; +} + +void summary_filter(SummaryView *summaryview, gboolean selected_only) +{ + if (prefs_common.fltlist) + summary_filter_real(summaryview, summary_filter_func, + selected_only); +} + +void summary_filter_junk(SummaryView *summaryview, gboolean selected_only) +{ + FilterRule *rule; + GSList junk_fltlist = {NULL, NULL}; + FolderItem *item = summaryview->folder_item; + FolderItem *junk = NULL; + + if (!item) + return; + + if (item->folder) + junk = folder_get_junk(item->folder); + rule = filter_junk_rule_create(NULL, junk, TRUE); + if (rule) { + junk_fltlist.data = rule; + summaryview->junk_fltlist = &junk_fltlist; + summary_filter_real(summaryview, summary_filter_junk_func, + selected_only); + summaryview->junk_fltlist = NULL; + filter_rule_free(rule); + } +} + +void summary_filter_open(SummaryView *summaryview, FilterCreateType type) +{ + GtkTreeIter iter; + MsgInfo *msginfo = NULL; + gchar *header = NULL; + gchar *key = NULL; + + if (!summaryview->selected) return; + if (!gtkut_tree_row_reference_get_iter + (GTK_TREE_MODEL(summaryview->store), + summaryview->selected, &iter)) + return; + + GET_MSG_INFO(msginfo, &iter); + if (!msginfo) return; + + filter_get_keyword_from_msg(msginfo, &header, &key, type); + prefs_filter_open(msginfo, header, key); + + g_free(header); + g_free(key); +} + +static void summary_junk_func(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + FilterRule rule = {NULL, FLT_OR, NULL, NULL, FLT_TIMING_ANY, TRUE}; + FilterAction action1 = {FLT_ACTION_EXEC, NULL, 0}; + FilterAction action2 = {FLT_ACTION_MOVE, NULL, 0}; + FilterAction action3 = {FLT_ACTION_MARK_READ, NULL, 0}; + SummaryView *summaryview = (SummaryView *)data; + MsgInfo *msginfo; + FilterInfo *fltinfo; + gchar *file; + gchar *junk_id = NULL; + gint ret; + + gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); + file = procmsg_get_message_file(msginfo); + g_return_if_fail(file != NULL); + + if (summaryview->to_folder) + junk_id = folder_item_get_identifier(summaryview->to_folder); + + action1.str_value = prefs_common.junk_learncmd; + action2.str_value = junk_id; + + rule.action_list = g_slist_append(rule.action_list, &action1); + if (junk_id) + rule.action_list = g_slist_append(rule.action_list, &action2); + if (prefs_common.mark_junk_as_read) + rule.action_list = g_slist_append(rule.action_list, &action3); + + fltinfo = filter_info_new(); + fltinfo->flags = msginfo->flags; + + ret = filter_action_exec(&rule, msginfo, file, fltinfo); + + if (ret < 0 || fltinfo->last_exec_exit_status != 0) { + g_warning("summary_junk_func: junk filter command returned %d", + fltinfo->last_exec_exit_status); + alertpanel_error + (_("Execution of the junk filter command failed.\n" + "Please check the junk mail control setting.")); + } else { + if (ret == 0 && + msginfo->flags.perm_flags != fltinfo->flags.perm_flags) { + msginfo->flags = fltinfo->flags; + summary_set_row(summaryview, iter, msginfo); + if (MSG_IS_IMAP(msginfo->flags)) { + if (fltinfo->actions[FLT_ACTION_MARK_READ]) + imap_msg_unset_perm_flags + (msginfo, MSG_NEW | MSG_UNREAD); + } + } + if (ret == 0 && fltinfo->actions[FLT_ACTION_MOVE] && + fltinfo->move_dest) + summary_move_row_to(summaryview, iter, + fltinfo->move_dest); + } + + filter_info_free(fltinfo); + g_slist_free(rule.action_list); + g_free(junk_id); + g_free(file); +} + +static void summary_not_junk_func(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + FilterRule rule = {NULL, FLT_OR, NULL, NULL, FLT_TIMING_ANY, TRUE}; + FilterAction action = {FLT_ACTION_EXEC, NULL, 0}; + MsgInfo *msginfo; + FilterInfo *fltinfo; + gchar *file; + gint ret; + + gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); + file = procmsg_get_message_file(msginfo); + g_return_if_fail(file != NULL); + + action.str_value = prefs_common.nojunk_learncmd; + + rule.action_list = g_slist_append(rule.action_list, &action); + + fltinfo = filter_info_new(); + + ret = filter_action_exec(&rule, msginfo, file, fltinfo); + + if (ret < 0 || fltinfo->last_exec_exit_status != 0) { + g_warning("summary_not_junk_func: junk filter command returned %d", + fltinfo->last_exec_exit_status); + alertpanel_error + (_("Execution of the junk filter command failed.\n" + "Please check the junk mail control setting.")); + } + + filter_info_free(fltinfo); + g_slist_free(rule.action_list); + g_free(file); +} + +void summary_junk(SummaryView *summaryview) +{ + FolderItem *junk = NULL; + + if (!prefs_common.enable_junk) + return; + if (!prefs_common.junk_learncmd) + return; + + debug_print("summary_junk: setting selected mails as junk\n"); + + if (prefs_common.junk_folder) + junk = folder_find_item_from_identifier + (prefs_common.junk_folder); + if (!junk && + summaryview->folder_item && summaryview->folder_item->folder) + junk = folder_get_junk(summaryview->folder_item->folder); + if (!junk) + junk = folder_get_default_junk(); + if (!junk) { + g_warning("summary_junk: junk folder not found"); + return; + } + + summary_lock(summaryview); + + summaryview->to_folder = junk; + gtk_tree_selection_selected_foreach(summaryview->selection, + summary_junk_func, summaryview); + summaryview->to_folder = NULL; + + summary_unlock(summaryview); + + if (junk && prefs_common.immediate_exec) + summary_execute(summaryview); + else { + summary_step(summaryview, GTK_SCROLL_STEP_FORWARD); + summary_status_show(summaryview); + } + + folderview_update_all_updated(FALSE); +} + +void summary_not_junk(SummaryView *summaryview) +{ + if (!prefs_common.enable_junk) + return; + if (!prefs_common.nojunk_learncmd) + return; + + summary_lock(summaryview); + + debug_print("Set mail as not junk\n"); + + gtk_tree_selection_selected_foreach(summaryview->selection, + summary_not_junk_func, summaryview); + + summary_unlock(summaryview); +} + +void summary_reply(SummaryView *summaryview, ComposeMode mode) +{ + GSList *mlist; + MsgInfo *msginfo; + MsgInfo *displayed_msginfo = NULL; + gchar *text = NULL; + + mlist = summary_get_selected_msg_list(summaryview); + if (!mlist) return; + msginfo = (MsgInfo *)mlist->data; + + if (summaryview->displayed) { + GtkTreeIter iter; + + if (gtkut_tree_row_reference_get_iter + (GTK_TREE_MODEL(summaryview->store), + summaryview->displayed, &iter)) { + GET_MSG_INFO(displayed_msginfo, &iter); + } + } + + /* use selection only if the displayed message is selected */ + if (!mlist->next && msginfo == displayed_msginfo) { + TextView *textview; + + textview = messageview_get_current_textview + (summaryview->messageview); + if (textview) { + text = gtkut_text_view_get_selection + (GTK_TEXT_VIEW(textview->text)); + if (text && *text == '\0') { + g_free(text); + text = NULL; + } + } + } + + if (prefs_common.reply_with_quote) + mode |= COMPOSE_WITH_QUOTE; + + switch (COMPOSE_MODE(mode)) { + case COMPOSE_REPLY: + case COMPOSE_REPLY_TO_SENDER: + case COMPOSE_REPLY_TO_ALL: + case COMPOSE_REPLY_TO_LIST: + compose_reply(msginfo, summaryview->folder_item, mode, text); + break; + case COMPOSE_FORWARD: + compose_forward(mlist, summaryview->folder_item, FALSE, text); + break; + case COMPOSE_FORWARD_AS_ATTACH: + compose_forward(mlist, summaryview->folder_item, TRUE, NULL); + break; + case COMPOSE_REDIRECT: + compose_redirect(msginfo, summaryview->folder_item); + break; + default: + g_warning("summary_reply(): invalid mode: %d\n", mode); + } + + summary_update_selected_rows(summaryview); + g_free(text); + g_slist_free(mlist); +} + +/* color label */ + +#define N_COLOR_LABELS colorlabel_get_color_count() + +static void summary_colorlabel_menu_item_activate_cb(GtkWidget *widget, + gpointer data) +{ + guint color = GPOINTER_TO_UINT(data); + SummaryView *summaryview; + + summaryview = g_object_get_data(G_OBJECT(widget), "summaryview"); + g_return_if_fail(summaryview != NULL); + + /* "dont_toggle" state set? */ + if (g_object_get_data(G_OBJECT(summaryview->colorlabel_menu), + "dont_toggle")) + return; + + summary_set_colorlabel(summaryview, color, NULL); +} + +/* summary_set_colorlabel() - labelcolor parameter is the color *flag* + * for the messsage; not the color index */ +void summary_set_colorlabel(SummaryView *summaryview, guint labelcolor, + GtkWidget *widget) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GList *rows, *cur; + GtkTreeIter iter; + MsgInfo *msginfo; + FolderSortKey sort_key = SORT_BY_NONE; + FolderSortType sort_type = SORT_ASCENDING; + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP && + summary_is_read_locked(summaryview)) + return; + + summary_lock(summaryview); + SORT_BLOCK(SORT_BY_LABEL); + + rows = summary_get_selected_rows(summaryview); + for (cur = rows; cur != NULL; cur = cur->next) { + gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)cur->data); + gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1); + + MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_CLABEL_FLAG_MASK); + MSG_SET_COLORLABEL_VALUE(msginfo->flags, labelcolor); + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED); + summary_set_row(summaryview, &iter, msginfo); + } + + if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) { + GSList *msglist; + + msglist = summary_get_selected_msg_list(summaryview); + imap_msg_list_set_colorlabel_flags(msglist, labelcolor); + g_slist_free(msglist); + } + + if (rows) + summaryview->folder_item->mark_dirty = TRUE; + + SORT_UNBLOCK(SORT_BY_LABEL); + summary_unlock(summaryview); +} + +static void summary_colorlabel_menu_item_activate_item_cb(GtkMenuItem *menuitem, + gpointer data) +{ + SummaryView *summaryview; + GtkMenuShell *menu; + GtkCheckMenuItem **items; + gint n; + GList *menu_cur; + GSList *mlist, *cur; + + summaryview = (SummaryView *)data; + g_return_if_fail(summaryview != NULL); + + menu = GTK_MENU_SHELL(summaryview->colorlabel_menu); + g_return_if_fail(menu != NULL); + + mlist = summary_get_selected_msg_list(summaryview); + if (!mlist) return; + + items = (GtkCheckMenuItem **)g_new(GtkWidget *, N_COLOR_LABELS + 1); + + /* NOTE: don't return prematurely because we set the "dont_toggle" + * state for check menu items */ + g_object_set_data(G_OBJECT(menu), "dont_toggle", GINT_TO_POINTER(1)); + + /* clear items. get item pointers. */ + for (n = 0, menu_cur = menu->children; menu_cur != NULL; + menu_cur = menu_cur->next) { + if (GTK_IS_CHECK_MENU_ITEM(menu_cur->data)) { + gtk_check_menu_item_set_active + (GTK_CHECK_MENU_ITEM(menu_cur->data), FALSE); + items[n] = GTK_CHECK_MENU_ITEM(menu_cur->data); + n++; + } + } + + if (n == (N_COLOR_LABELS + 1)) { + /* iterate all messages and set the state of the appropriate + * items */ + for (cur = mlist; cur != NULL; cur = cur->next) { + MsgInfo *msginfo = (MsgInfo *)cur->data; + gint clabel; + + if (msginfo) { + clabel = MSG_GET_COLORLABEL_VALUE + (msginfo->flags); + if (!gtk_check_menu_item_get_active + (items[clabel])) + gtk_check_menu_item_set_active + (items[clabel], TRUE); + } + } + } else + g_warning("invalid number of color elements (%d)\n", n); + + /* reset "dont_toggle" state */ + g_object_set_data(G_OBJECT(menu), "dont_toggle", GINT_TO_POINTER(0)); + + g_free(items); +} + +static void summary_colorlabel_menu_create(SummaryView *summaryview) +{ + GtkWidget *label_menuitem; + GtkWidget *menu; + GtkWidget *item; + gint i; + + label_menuitem = gtk_item_factory_get_item(summaryview->popupfactory, + "/Color label"); + g_signal_connect(G_OBJECT(label_menuitem), "activate", + G_CALLBACK(summary_colorlabel_menu_item_activate_item_cb), + summaryview); + gtk_widget_show(label_menuitem); + + menu = gtk_menu_new(); + + /* create sub items. for the menu item activation callback we pass the + * color flag value as data parameter. Also we attach a data pointer + * so we can always get back the SummaryView pointer. */ + + item = gtk_check_menu_item_new_with_label(_("None")); + gtk_menu_append(GTK_MENU(menu), item); + g_signal_connect(G_OBJECT(item), "activate", + G_CALLBACK(summary_colorlabel_menu_item_activate_cb), + GUINT_TO_POINTER(0)); + g_object_set_data(G_OBJECT(item), "summaryview", summaryview); + gtk_widget_show(item); + + item = gtk_menu_item_new(); + gtk_menu_append(GTK_MENU(menu), item); + gtk_widget_show(item); + + /* create pixmap/label menu items */ + for (i = 0; i < N_COLOR_LABELS; i++) { + item = colorlabel_create_check_color_menu_item(i); + gtk_menu_append(GTK_MENU(menu), item); + g_signal_connect(G_OBJECT(item), "activate", + G_CALLBACK(summary_colorlabel_menu_item_activate_cb), + GUINT_TO_POINTER(i + 1)); + g_object_set_data(G_OBJECT(item), "summaryview", summaryview); + gtk_widget_show(item); + } + + gtk_widget_show(menu); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(label_menuitem), menu); + summaryview->colorlabel_menu = menu; +} + +static GtkWidget *summary_tree_view_create(SummaryView *summaryview) +{ + GtkWidget *treeview; + GtkTreeStore *store; + GtkTreeSelection *selection; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkWidget *image; + SummaryColumnType type; + + for (type = 0; type < N_SUMMARY_VISIBLE_COLS; type++) + summaryview->columns[type] = NULL; + + store = gtk_tree_store_new(N_SUMMARY_COLS, + GDK_TYPE_PIXBUF, + GDK_TYPE_PIXBUF, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_UINT, + G_TYPE_STRING, + + G_TYPE_POINTER, + + G_TYPE_INT, + G_TYPE_UINT, + + GDK_TYPE_COLOR, + G_TYPE_INT); + +#define SET_SORT(col, func) \ + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), \ + col, func, summaryview, NULL) + + SET_SORT(S_COL_MARK, summary_cmp_by_mark); + SET_SORT(S_COL_UNREAD, summary_cmp_by_unread); + SET_SORT(S_COL_MIME, summary_cmp_by_mime); + SET_SORT(S_COL_SUBJECT, summary_cmp_by_subject); + SET_SORT(S_COL_FROM, summary_cmp_by_from); + SET_SORT(S_COL_DATE, summary_cmp_by_date); + SET_SORT(S_COL_SIZE, summary_cmp_by_size); + SET_SORT(S_COL_NUMBER, summary_cmp_by_num); + SET_SORT(S_COL_TO, summary_cmp_by_to); + SET_SORT(S_COL_LABEL, summary_cmp_by_label); + SET_SORT(S_COL_TDATE, summary_cmp_by_thread_date); + +#undef SET_SORT + + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), + prefs_common.enable_rules_hint); + gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE); + gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), S_COL_SUBJECT); + gtk_tree_view_set_reorderable(GTK_TREE_VIEW(treeview), FALSE); + g_object_set(treeview, "fixed-height-mode", TRUE, NULL); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + gtk_tree_selection_set_select_function(selection, summary_select_func, + summaryview, NULL); + +#define ADD_COLUMN(title, type, col, text_attr, width, align) \ +{ \ + renderer = gtk_cell_renderer_ ## type ## _new(); \ + g_object_set(renderer, "xalign", align, "ypad", 0, NULL); \ + column = gtk_tree_view_column_new_with_attributes \ + (title, renderer, # type , col, NULL); \ + g_object_set_data(G_OBJECT(column), "column_id", \ + GINT_TO_POINTER(col)); \ + summaryview->columns[col] = column; \ + if (text_attr) { \ + gtk_tree_view_column_set_attributes \ + (column, renderer, \ + # type, col, \ + "foreground-gdk", S_COL_FOREGROUND, \ + "weight", S_COL_BOLD, \ + NULL); \ + gtk_tree_view_column_set_resizable(column, TRUE); \ + } \ + gtk_tree_view_column_set_alignment(column, align); \ + gtk_tree_view_column_set_sizing \ + (column, GTK_TREE_VIEW_COLUMN_FIXED); \ + gtk_tree_view_column_set_fixed_width(column, width); \ + gtk_tree_view_column_set_min_width(column, 8); \ + gtk_tree_view_column_set_clickable(column, TRUE); \ + /* gtk_tree_view_column_set_sort_column_id(column, col); */ \ + gtk_tree_view_column_set_reorderable(column, TRUE); \ + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); \ + g_signal_connect(G_OBJECT(column->button), "clicked", \ + G_CALLBACK(summary_column_clicked), \ + summaryview); \ + g_signal_connect(G_OBJECT(column->button), "size-allocate", \ + G_CALLBACK(summary_col_resized), summaryview); \ +} + + ADD_COLUMN(NULL, pixbuf, S_COL_MARK, FALSE, + SUMMARY_COL_MARK_WIDTH, 0.5); + image = stock_pixbuf_widget(treeview, STOCK_PIXMAP_MARK); + gtk_widget_show(image); + gtk_tree_view_column_set_widget(column, image); + ADD_COLUMN(NULL, pixbuf, S_COL_UNREAD, FALSE, + SUMMARY_COL_UNREAD_WIDTH, 0.5); + image = stock_pixbuf_widget(treeview, STOCK_PIXMAP_MAIL_SMALL); + gtk_widget_show(image); + gtk_tree_view_column_set_widget(column, image); + ADD_COLUMN(NULL, pixbuf, S_COL_MIME, FALSE, + SUMMARY_COL_MIME_WIDTH, 0.5); + image = stock_pixbuf_widget(treeview, STOCK_PIXMAP_CLIP); + gtk_widget_show(image); + gtk_tree_view_column_set_widget(column, image); + + ADD_COLUMN(_("Subject"), text, S_COL_SUBJECT, TRUE, + prefs_common.summary_col_size[S_COL_SUBJECT], 0.0); +#if GTK_CHECK_VERSION(2, 14, 0) + gtk_tree_view_column_set_expand(column, TRUE); +#endif + gtk_tree_view_set_expander_column(GTK_TREE_VIEW(treeview), column); + ADD_COLUMN(_("From"), text, S_COL_FROM, TRUE, + prefs_common.summary_col_size[S_COL_FROM], 0.0); + ADD_COLUMN(_("Date"), text, S_COL_DATE, TRUE, + prefs_common.summary_col_size[S_COL_DATE], 0.0); + ADD_COLUMN(_("Size"), text, S_COL_SIZE, TRUE, + prefs_common.summary_col_size[S_COL_SIZE], 1.0); + ADD_COLUMN(_("No."), text, S_COL_NUMBER, TRUE, + prefs_common.summary_col_size[S_COL_NUMBER], 1.0); + ADD_COLUMN(_("To"), text, S_COL_TO, TRUE, + prefs_common.summary_col_size[S_COL_TO], 0.0); + +#undef ADD_COLUMN + + /* add rightmost empty column */ + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_min_width(column, 0); + gtk_tree_view_column_set_max_width(column, 0); + gtk_tree_view_column_set_clickable(column, FALSE); + gtk_tree_view_column_set_reorderable(column, FALSE); + gtk_tree_view_set_column_drag_function(GTK_TREE_VIEW(treeview), + summary_column_drop_func, + summaryview, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + g_object_set_data(G_OBJECT(treeview), "user_data", summaryview); + + g_signal_connect(G_OBJECT(treeview), "button_press_event", + G_CALLBACK(summary_button_pressed), summaryview); + g_signal_connect(G_OBJECT(treeview), "button_release_event", + G_CALLBACK(summary_button_released), summaryview); + g_signal_connect(G_OBJECT(treeview), "key_press_event", + G_CALLBACK(summary_key_pressed), summaryview); + + g_signal_connect(G_OBJECT(selection), "changed", + G_CALLBACK(summary_selection_changed), summaryview); + + g_signal_connect(G_OBJECT(treeview), "row-expanded", + G_CALLBACK(summary_row_expanded), summaryview); + g_signal_connect(G_OBJECT(treeview), "row-collapsed", + G_CALLBACK(summary_row_collapsed), summaryview); + + g_signal_connect(G_OBJECT(treeview), "columns-changed", + G_CALLBACK(summary_columns_changed), summaryview); + + gtk_tree_view_enable_model_drag_source + (GTK_TREE_VIEW(treeview), + GDK_BUTTON1_MASK, summary_drag_types, N_DRAG_TYPES, + GDK_ACTION_MOVE | GDK_ACTION_COPY); + + g_signal_connect_after(G_OBJECT(treeview), "drag-begin", + G_CALLBACK(summary_drag_begin), summaryview); + g_signal_connect_after(G_OBJECT(treeview), "drag-end", + G_CALLBACK(summary_drag_end), summaryview); + g_signal_connect(G_OBJECT(treeview), "drag-data-get", + G_CALLBACK(summary_drag_data_get), summaryview); + + return treeview; +} + +void summary_set_column_order(SummaryView *summaryview) +{ + const SummaryColumnState *col_state; + SummaryColumnType type; + GtkTreeViewColumn *column, *last_column = NULL; + gint pos; + + g_signal_handlers_block_by_func(summaryview->treeview, + summary_columns_changed, summaryview); + + col_state = prefs_summary_column_get_config + (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item)); + + for (pos = 0; pos < N_SUMMARY_VISIBLE_COLS; pos++) { + summaryview->col_state[pos] = col_state[pos]; + type = col_state[pos].type; + summaryview->col_pos[type] = pos; + column = summaryview->columns[type]; + + gtk_tree_view_move_column_after + (GTK_TREE_VIEW(summaryview->treeview), + column, last_column); + gtk_tree_view_column_set_visible + (column, col_state[pos].visible); + last_column = column; + + debug_print("summary_set_column_order: " + "pos %d : type %d, vislble %d\n", + pos, type, summaryview->col_state[pos].visible); + } + + g_signal_handlers_unblock_by_func(summaryview->treeview, + summary_columns_changed, summaryview); +} + +void summary_get_column_order(SummaryView *summaryview) +{ + GtkTreeViewColumn *column; + GList *columns, *cur; + gint pos = 0; + SummaryColumnType type; + gboolean visible; + + columns = gtk_tree_view_get_columns + (GTK_TREE_VIEW(summaryview->treeview)); + + for (cur = columns; cur != NULL && pos < N_SUMMARY_VISIBLE_COLS; + cur = cur->next, pos++) { + column = (GtkTreeViewColumn *)cur->data; + type = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(column), "column_id")); + if (type < 0 || type >= N_SUMMARY_VISIBLE_COLS) { + g_warning("summary_get_column_order: " + "invalid type: %d\n", type); + break; + } + visible = gtk_tree_view_column_get_visible(column); + summaryview->col_state[pos].type = type; + summaryview->col_state[pos].visible = visible; + summaryview->col_pos[type] = pos; + debug_print("summary_get_column_order: " + "pos: %d, type: %d, visible: %d\n", + pos, type, visible); + } + + prefs_summary_column_set_config + (summaryview->col_state, + FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item)); + + g_list_free(columns); +} + +void summary_qsearch_reset(SummaryView *summaryview) +{ + guint selected_msgnum = 0; + guint displayed_msgnum = 0; + + if (!summaryview->on_filter) + return; + if (!summaryview->folder_item) + return; + + if (summary_is_read_locked(summaryview)) return; + + g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview), + (GSignalMatchType)G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, summaryview); + + quick_search_clear_entry(summaryview->qsearch); + gtk_option_menu_set_history + (GTK_OPTION_MENU(summaryview->qsearch->optmenu), 0); + summaryview->folder_item->qsearch_cond_type = QS_ALL; + + selected_msgnum = summary_get_msgnum(summaryview, + summaryview->selected); + displayed_msgnum = summary_get_msgnum(summaryview, + summaryview->displayed); + + summaryview->on_filter = FALSE; + g_slist_free(summaryview->flt_mlist); + summaryview->flt_mlist = NULL; + summaryview->total_flt_msg_size = 0; + summaryview->flt_msg_total = 0; + summaryview->flt_deleted = 0; + summaryview->flt_moved = 0; + summaryview->flt_copied = 0; + summaryview->flt_new = 0; + summaryview->flt_unread = 0; + + main_window_cursor_wait(summaryview->mainwin); + summary_lock(summaryview); + + gtkut_tree_view_fast_clear(GTK_TREE_VIEW(summaryview->treeview), + summaryview->store); + summary_unset_sort_column_id(summaryview); + summaryview->total_size = 0; + summary_set_tree_model_from_list(summaryview, summaryview->all_mlist); + summary_selection_list_free(summaryview); + + summary_unlock(summaryview); + main_window_cursor_normal(summaryview->mainwin); + + g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview), + (GSignalMatchType)G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, summaryview); + + summary_update_display_state(summaryview, displayed_msgnum, + selected_msgnum); + + summary_update_status(summaryview); + summary_status_show(summaryview); + summary_set_menu_sensitive(summaryview); + main_window_set_toolbar_sensitive(summaryview->mainwin); +} + +void summary_qsearch_clear_entry(SummaryView *summaryview) +{ + if (summary_is_read_locked(summaryview)) + return; + quick_search_clear_entry(summaryview->qsearch); + summary_qsearch(summaryview); +} + +void summary_qsearch(SummaryView *summaryview) +{ + QSearchCondType type; + GtkWidget *menuitem; + const gchar *key = NULL; + GSList *flt_mlist; + guint selected_msgnum = 0; + guint displayed_msgnum = 0; + + if (!summaryview->folder_item) + return; + + if (summary_is_read_locked(summaryview)) return; + + menuitem = gtk_menu_get_active(GTK_MENU(summaryview->qsearch->menu)); + type = GPOINTER_TO_INT + (g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID)); + summaryview->folder_item->qsearch_cond_type = type; + + if (!summaryview->all_mlist) + return; + + if (summaryview->qsearch->entry_entered) + key = gtk_entry_get_text + (GTK_ENTRY(summaryview->qsearch->entry)); + if (type == QS_ALL && (!key || *key == '\0')) { + summary_qsearch_reset(summaryview); + return; + } + + selected_msgnum = summary_get_msgnum(summaryview, + summaryview->selected); + displayed_msgnum = summary_get_msgnum(summaryview, + summaryview->displayed); + + summaryview->on_filter = FALSE; + g_slist_free(summaryview->flt_mlist); + summaryview->flt_mlist = NULL; + summaryview->total_flt_msg_size = 0; + summaryview->flt_msg_total = 0; + summaryview->flt_deleted = 0; + summaryview->flt_moved = 0; + summaryview->flt_copied = 0; + summaryview->flt_new = 0; + summaryview->flt_unread = 0; + + main_window_cursor_wait(summaryview->mainwin); + summary_lock(summaryview); + + flt_mlist = quick_search_filter(summaryview->qsearch, type, key); + summaryview->on_filter = TRUE; + summaryview->flt_mlist = flt_mlist; + + g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview), + (GSignalMatchType)G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, summaryview); + + gtkut_tree_view_fast_clear(GTK_TREE_VIEW(summaryview->treeview), + summaryview->store); + summary_unset_sort_column_id(summaryview); + summaryview->total_size = 0; + summary_set_tree_model_from_list(summaryview, flt_mlist); + summary_selection_list_free(summaryview); + + g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview), + (GSignalMatchType)G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, summaryview); + + summary_unlock(summaryview); + summary_update_display_state(summaryview, displayed_msgnum, + selected_msgnum); + + main_window_cursor_normal(summaryview->mainwin); + summary_update_status(summaryview); + summary_status_show(summaryview); + summary_set_menu_sensitive(summaryview); + main_window_set_toolbar_sensitive(summaryview->mainwin); +} + +void summary_mark_displayed_read(SummaryView *summaryview, GtkTreeIter *iter) +{ + MsgInfo *msginfo = NULL; + GtkTreeIter iter_; + + if (summary_is_read_locked(summaryview)) return; + + if (prefs_common.mark_as_read_on_new_window) + return; + + if (!iter) { + if (!gtkut_tree_row_reference_get_iter + (GTK_TREE_MODEL(summaryview->store), + summaryview->displayed, &iter_)) + return; + iter = &iter_; + } + + GET_MSG_INFO(msginfo, iter); + if (!msginfo) + return; + + summary_lock(summaryview); + + if (MSG_IS_NEW(msginfo->flags) || + MSG_IS_UNREAD(msginfo->flags)) { + summary_mark_row_as_read(summaryview, iter); + if (MSG_IS_IMAP(msginfo->flags)) + imap_msg_unset_perm_flags + (msginfo, MSG_NEW | MSG_UNREAD); + summary_set_row(summaryview, iter, msginfo); + summary_status_show(summaryview); + + /* sort order can be changed here */ + if (summaryview->folder_item->sort_key == SORT_BY_UNREAD) + summary_selection_list_free(summaryview); + } + + summary_unlock(summaryview); +} + + +/* callback functions */ + +static gboolean summary_toggle_pressed(GtkWidget *eventbox, + GdkEventButton *event, + SummaryView *summaryview) +{ + if (!event) return FALSE; + + summary_toggle_view(summaryview); + return FALSE; +} + +static gboolean summary_button_pressed(GtkWidget *treeview, + GdkEventButton *event, + SummaryView *summaryview) +{ + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeViewColumn *column = NULL; + gboolean is_selected; + gboolean mod_pressed; + gint px, py; + + if (!event) return FALSE; + + if (summaryview->folder_item && summaryview->folder_item->folder && + FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP && + summary_is_locked(summaryview)) + return TRUE; + + if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), + event->x, event->y, &path, &column, + NULL, NULL)) + return FALSE; + + /* pass through if the border of column titles is clicked */ + gtk_widget_get_pointer(treeview, &px, &py); + if (py == (gint)event->y) + return FALSE; + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(summaryview->store), + &iter, path)) + return FALSE; + + is_selected = gtk_tree_selection_path_is_selected + (summaryview->selection, path); + mod_pressed = ((event->state & + (GDK_SHIFT_MASK|GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0); + + if ((event->button == 1 || event->button == 2)) { + MsgInfo *msginfo; + FolderSortKey sort_key = SORT_BY_NONE; + FolderSortType sort_type = SORT_ASCENDING; + + GET_MSG_INFO(msginfo, &iter); + + if (column == summaryview->columns[S_COL_MARK]) { + if (MSG_IS_IMAP(msginfo->flags) && + summary_is_locked(summaryview)) { + gtk_tree_path_free(path); + return TRUE; + } + + SORT_BLOCK(SORT_BY_MARK); + + if (!MSG_IS_DELETED(msginfo->flags) && + !MSG_IS_MOVE(msginfo->flags) && + !MSG_IS_COPY(msginfo->flags)) { + if (MSG_IS_MARKED(msginfo->flags)) { + summary_unmark_row(summaryview, &iter); + if (MSG_IS_IMAP(msginfo->flags)) { + summary_lock(summaryview); + imap_msg_unset_perm_flags + (msginfo, MSG_MARKED); + summary_unlock(summaryview); + } + } else { + summary_mark_row(summaryview, &iter); + if (MSG_IS_IMAP(msginfo->flags)) { + summary_lock(summaryview); + imap_msg_set_perm_flags + (msginfo, MSG_MARKED); + summary_unlock(summaryview); + } + } + } + gtk_tree_path_free(path); + + SORT_UNBLOCK(SORT_BY_MARK); + + return TRUE; + } else if (column == summaryview->columns[S_COL_UNREAD]) { + if (MSG_IS_IMAP(msginfo->flags) && + summary_is_locked(summaryview)) { + gtk_tree_path_free(path); + return TRUE; + } + + SORT_BLOCK(SORT_BY_UNREAD); + + if (MSG_IS_UNREAD(msginfo->flags)) { + summary_mark_row_as_read(summaryview, &iter); + if (MSG_IS_IMAP(msginfo->flags)) { + summary_lock(summaryview); + imap_msg_unset_perm_flags + (msginfo, MSG_NEW | MSG_UNREAD); + summary_unlock(summaryview); + } + trayicon_set_tooltip(NULL); + trayicon_set_notify(FALSE); + summary_status_show(summaryview); + } else if (!MSG_IS_REPLIED(msginfo->flags) && + !MSG_IS_FORWARDED(msginfo->flags)) { + summary_mark_row_as_unread(summaryview, &iter); + if (MSG_IS_IMAP(msginfo->flags)) { + summary_lock(summaryview); + imap_msg_set_perm_flags + (msginfo, MSG_UNREAD); + summary_unlock(summaryview); + } + summary_status_show(summaryview); + } + gtk_tree_path_free(path); + + SORT_UNBLOCK(SORT_BY_UNREAD); + + return TRUE; + } + } + + if (event->button == 1) { + summaryview->on_button_press = TRUE; + + if (summary_get_selection_type(summaryview) == + SUMMARY_SELECTED_MULTIPLE && is_selected && + !mod_pressed) { + summaryview->can_toggle_selection = FALSE; + summaryview->pressed_path = gtk_tree_path_copy(path); + } else { + if (event->type == GDK_2BUTTON_PRESS && is_selected) + summary_activate_selected(summaryview); + else if (summary_get_selection_type(summaryview) == + SUMMARY_SELECTED_SINGLE && is_selected && + !mod_pressed && + summary_row_is_displayed(summaryview, &iter)) { + summary_mark_displayed_read(summaryview, &iter); + } else { + summaryview->can_toggle_selection = TRUE; + if (!mod_pressed && + messageview_is_visible(summaryview->messageview)) + summaryview->display_msg = TRUE; + } + } + + if (summaryview->on_button_press == FALSE) { + /* button released within sub event loop */ + gtk_tree_path_free(path); + return TRUE; + } + } else if (event->button == 2) { + summary_select_row(summaryview, &iter, TRUE, FALSE); + summary_mark_displayed_read(summaryview, &iter); + gtk_tree_path_free(path); + return TRUE; + } else if (event->button == 3) { + /* right clicked */ + syl_plugin_signal_emit("summaryview-menu-popup", + summaryview->popupfactory); + gtk_menu_popup(GTK_MENU(summaryview->popupmenu), NULL, NULL, + NULL, NULL, event->button, event->time); + if (is_selected) { + gtk_tree_path_free(path); + return TRUE; + } + } + + gtk_tree_path_free(path); + + return FALSE; +} + +static gboolean summary_button_released(GtkWidget *treeview, + GdkEventButton *event, + SummaryView *summaryview) +{ + if (!summaryview->can_toggle_selection && !summaryview->on_drag && + summaryview->pressed_path) { + summaryview->can_toggle_selection = TRUE; + summaryview->display_msg = + messageview_is_visible(summaryview->messageview); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(treeview), + summaryview->pressed_path, + NULL, FALSE); + } + + summaryview->on_button_press = FALSE; + summaryview->can_toggle_selection = TRUE; + summaryview->on_drag = FALSE; + if (summaryview->pressed_path) { + gtk_tree_path_free(summaryview->pressed_path); + summaryview->pressed_path = NULL; + } + + return FALSE; +} + +void summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event) +{ + summary_key_pressed(summaryview->treeview, event, summaryview); +} + +#define BREAK_ON_MODIFIER_KEY() \ + if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break + +static gboolean summary_key_pressed(GtkWidget *widget, GdkEventKey *event, + SummaryView *summaryview) +{ + MessageView *messageview; + TextView *textview; + GtkAdjustment *adj; + gboolean mod_pressed; + gboolean scrolled; + + if (!event) return FALSE; + + if (summary_is_read_locked(summaryview)) { + switch (event->keyval) { + case GDK_Home: + case GDK_End: + case GDK_Up: + case GDK_Down: + case GDK_Page_Up: + case GDK_Page_Down: + return TRUE; + default: + break; + } + return FALSE; + } + + switch (event->keyval) { + case GDK_Left: /* Move focus */ + case GDK_KP_Left: + adj = gtk_scrolled_window_get_hadjustment + (GTK_SCROLLED_WINDOW(summaryview->scrolledwin)); + if (adj->lower != adj->value) + return FALSE; + /* FALLTHROUGH */ + case GDK_Escape: + gtk_widget_grab_focus(summaryview->folderview->treeview); + return TRUE; + default: + break; + } + + if (!summaryview->selected) + return FALSE; + + messageview = summaryview->messageview; + if (messageview->type == MVIEW_MIME && + gtk_notebook_get_current_page + (GTK_NOTEBOOK(messageview->notebook)) == 1) + textview = messageview->mimeview->textview; + else + textview = messageview->textview; + + mod_pressed = + ((event->state & (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0); + + switch (event->keyval) { + case GDK_space: /* Page down or go to the next */ + case GDK_KP_Space: + if (summaryview->selected && + !gtkut_tree_row_reference_equal(summaryview->displayed, + summaryview->selected)) { + summary_display_msg_selected(summaryview, FALSE, FALSE); + summary_mark_displayed_read(summaryview, NULL); + } else if (mod_pressed) { + scrolled = textview_scroll_page(textview, TRUE); + if (!scrolled) + summary_select_prev_unread(summaryview); + } else { + scrolled = textview_scroll_page(textview, FALSE); + summary_mark_displayed_read(summaryview, NULL); + if (!scrolled) + summary_select_next_unread(summaryview); + } + return TRUE; + case GDK_BackSpace: /* Page up */ + textview_scroll_page(textview, TRUE); + return TRUE; + case GDK_Return: /* Scroll up/down one line */ + case GDK_KP_Enter: + if (summaryview->selected && + !gtkut_tree_row_reference_equal(summaryview->displayed, + summaryview->selected)) + summary_display_msg_selected(summaryview, FALSE, FALSE); + else + textview_scroll_one_line(textview, mod_pressed); + summary_mark_displayed_read(summaryview, NULL); + return TRUE; + case GDK_Delete: + case GDK_KP_Delete: + BREAK_ON_MODIFIER_KEY(); + summary_delete(summaryview); + return TRUE; + case GDK_F10: + case GDK_Menu: + if (event->keyval == GDK_F10 && + (event->state & GDK_SHIFT_MASK) == 0) + break; + syl_plugin_signal_emit("summaryview-menu-popup", + summaryview->popupfactory); + gtk_menu_popup(GTK_MENU(summaryview->popupmenu), NULL, NULL, + menu_widget_position, summaryview->treeview, + 0, GDK_CURRENT_TIME); + return TRUE; + default: + break; + } + + return FALSE; +} + +static void summary_set_bold_recursive(SummaryView *summaryview, + GtkTreeIter *iter) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter child; + MsgInfo *msginfo; + gboolean valid; + + if (!gtk_tree_model_iter_has_child(model, iter)) + return; + + GET_MSG_INFO(msginfo, iter); + if (!MSG_IS_UNREAD(msginfo->flags)) { + gtk_tree_store_set(summaryview->store, iter, + S_COL_BOLD, PANGO_WEIGHT_NORMAL, -1); + } + + valid = gtk_tree_model_iter_children(model, &child, iter); + while (valid) { + summary_set_bold_recursive(summaryview, &child); + valid = gtk_tree_model_iter_next(model, &child); + } +} + +static void summary_row_expanded(GtkTreeView *treeview, GtkTreeIter *iter, + GtkTreePath *path, SummaryView *summaryview) +{ + gtk_tree_view_expand_row(treeview, path, TRUE); + + if (prefs_common.bold_unread) + summary_set_bold_recursive(summaryview, iter); + + /* workaround for last row expand problem */ +#if GTK_CHECK_VERSION(2, 8, 0) + gtk_widget_queue_resize(GTK_WIDGET(treeview)); +#else + g_object_set(treeview, "fixed-height-mode", FALSE, NULL); + gtk_widget_queue_resize(GTK_WIDGET(treeview)); + g_object_set(treeview, "fixed-height-mode", TRUE, NULL); +#endif +} + +static void summary_row_collapsed(GtkTreeView *treeview, GtkTreeIter *iter, + GtkTreePath *path, SummaryView *summaryview) +{ + if (prefs_common.bold_unread && + summary_have_unread_children(summaryview, iter)) { + gtk_tree_store_set(summaryview->store, iter, + S_COL_BOLD, PANGO_WEIGHT_BOLD, -1); + } +} + +static void summary_columns_changed(GtkTreeView *treeview, + SummaryView *summaryview) +{ + summary_get_column_order(summaryview); +} + +static gboolean summary_select_func(GtkTreeSelection *selection, + GtkTreeModel *model, GtkTreePath *path, + gboolean cur_selected, gpointer data) +{ + SummaryView *summaryview = (SummaryView *)data; + + return summaryview->can_toggle_selection && + !summary_is_read_locked(summaryview); +} + +static gboolean summary_display_msg_idle_func(gpointer data) +{ + SummaryView *summaryview = (SummaryView *)data; + GtkTreePath *path; + GtkTreeIter iter; + + gdk_threads_enter(); + path = gtk_tree_row_reference_get_path(summaryview->selected); + if (path) { + gtk_tree_model_get_iter(GTK_TREE_MODEL(summaryview->store), + &iter, path); + gtk_tree_path_free(path); + summary_display_msg(summaryview, &iter); + summary_mark_displayed_read(summaryview, &iter); + } + gdk_threads_leave(); + + return FALSE; +} + +static void summary_selection_changed(GtkTreeSelection *selection, + SummaryView *summaryview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GtkTreeIter iter; + GtkTreePath *path; + GList *list; + gboolean single_selection = FALSE; + + summary_selection_list_free(summaryview); + list = summary_get_selected_rows(summaryview); + + summary_status_show(summaryview); + + gtk_tree_row_reference_free(summaryview->selected); + if (list) { + if (list->next == NULL) + single_selection = TRUE; + path = (GtkTreePath *)list->data; + gtk_tree_model_get_iter(model, &iter, path); + summaryview->selected = + gtk_tree_row_reference_new(model, path); + } else + summaryview->selected = NULL; + + if (!single_selection) { + summaryview->display_msg = FALSE; +#if 0 + if (summaryview->displayed && prefs_common.always_show_msg) { + messageview_clear(summaryview->messageview); + gtk_tree_row_reference_free(summaryview->displayed); + summaryview->displayed = NULL; + } +#endif + summary_set_menu_sensitive(summaryview); + main_window_set_toolbar_sensitive(summaryview->mainwin); + return; + } + + if (summaryview->display_msg || + (prefs_common.always_show_msg && + messageview_is_visible(summaryview->messageview))) { + summaryview->display_msg = FALSE; + if (!gtkut_tree_row_reference_equal(summaryview->displayed, + summaryview->selected)) { + if (summaryview->on_button_press) + g_idle_add(summary_display_msg_idle_func, + summaryview); + else + summary_display_msg(summaryview, &iter); + return; + } + } + + summary_set_menu_sensitive(summaryview); + main_window_set_toolbar_sensitive(summaryview->mainwin); +} + +static void summary_col_resized(GtkWidget *widget, GtkAllocation *allocation, + SummaryView *summaryview) +{ + SummaryColumnType type; + + for (type = 0; type < N_SUMMARY_VISIBLE_COLS; type++) { + if (summaryview->columns[type]->button == widget) { + prefs_common.summary_col_size[type] = allocation->width; + break; + } + } +} + +static void summary_reply_cb(SummaryView *summaryview, guint action, + GtkWidget *widget) +{ + summary_reply(summaryview, (ComposeMode)action); +} + +static void summary_show_all_header_cb(SummaryView *summaryview, + guint action, GtkWidget *widget) +{ + summary_display_msg_selected(summaryview, FALSE, + GTK_CHECK_MENU_ITEM(widget)->active); +} + +static void summary_add_address_cb(SummaryView *summaryview, + guint action, GtkWidget *widget) +{ + summary_add_address(summaryview); +} + +static void summary_create_filter_cb(SummaryView *summaryview, + guint action, GtkWidget *widget) +{ + summary_filter_open(summaryview, (FilterCreateType)action); +} + +static void summary_sort_by_column_click(SummaryView *summaryview, + SummaryColumnType type) +{ + FolderItem *item = summaryview->folder_item; + + if (!item) return; + + if (item->sort_key == col_to_sort_key[type]) + summary_sort(summaryview, col_to_sort_key[type], + item->sort_type == SORT_ASCENDING + ? SORT_DESCENDING : SORT_ASCENDING); + else + summary_sort(summaryview, col_to_sort_key[type], + SORT_ASCENDING); +} + +static void summary_column_clicked(GtkWidget *button, SummaryView *summaryview) +{ + SummaryColumnType type; + + for (type = 0; type < N_SUMMARY_VISIBLE_COLS; type++) { + if (summaryview->columns[type]->button == button) { + summary_sort_by_column_click(summaryview, type); + break; + } + } +} + +static gboolean summary_column_drop_func(GtkTreeView *treeview, + GtkTreeViewColumn *column, + GtkTreeViewColumn *prev_column, + GtkTreeViewColumn *next_column, + gpointer data) +{ + if (next_column == NULL) + return FALSE; + else + return TRUE; +} + +static void summary_drag_begin(GtkWidget *widget, GdkDragContext *drag_context, + SummaryView *summaryview) +{ + if (!summaryview->on_button_press) + g_warning("summary_drag_begin: drag began without button press"); + summaryview->on_drag = TRUE; + gtk_drag_set_icon_default(drag_context); +} + +static void summary_drag_end(GtkWidget *widget, GdkDragContext *drag_context, + SummaryView *summaryview) +{ + if (summaryview->drag_list) { + g_free(summaryview->drag_list); + summaryview->drag_list = NULL; + } +} + +static void summary_drag_data_get(GtkWidget *widget, + GdkDragContext *drag_context, + GtkSelectionData *selection_data, + guint info, + guint time, + SummaryView *summaryview) +{ + GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); + GList *rows, *cur; + gchar *mail_list = NULL; + gchar *file, *filename, *fs_filename, *tmp; + gint suffix = 0; + MsgInfo *msginfo; + GtkTreeIter iter; + + if (info == DRAG_TYPE_TEXT) { + gtk_selection_data_set(selection_data, selection_data->target, + 8, (guchar *)"drag-from-summary", 17); + return; + } + + if (!summaryview->drag_list) { + rows = summary_get_selected_rows(summaryview); + + for (cur = rows; cur != NULL; cur = cur->next) { + gtk_tree_model_get_iter(model, &iter, + (GtkTreePath *)cur->data); + gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, + &msginfo, -1); + file = procmsg_get_message_file(msginfo); + if (!file) continue; + + if (msginfo->subject && *msginfo->subject != '\0') { + filename = g_strdup(msginfo->subject); + subst_for_filename(filename); + } else + filename = g_strdup(g_basename(file)); + fs_filename = conv_filename_from_utf8(filename); + + suffix = 0; + do { + if (suffix == 0) + tmp = g_strdup_printf + ("%s%c%s.eml", get_tmp_dir(), + G_DIR_SEPARATOR, + fs_filename); + else + tmp = g_strdup_printf + ("%s%c%s_(%d).eml", + get_tmp_dir(), + G_DIR_SEPARATOR, fs_filename, + suffix); + + if (is_file_exist(tmp)) { + suffix++; + g_free(tmp); + } else + break; + } while (1); + + if (copy_file(file, tmp, FALSE) < 0) { + g_warning("Can't copy '%s'\n", file); + } else { + gchar *uri; + + uri = encode_uri(tmp); + + if (!mail_list) { + mail_list = uri; + } else { + gchar *list_tmp; + + list_tmp = g_strconcat + (mail_list, "\n", uri, NULL); + g_free(mail_list); + g_free(uri); + mail_list = list_tmp; + } + } + + g_free(tmp); + g_free(fs_filename); + g_free(filename); + g_free(file); + } + + summaryview->drag_list = mail_list; + } + + if (summaryview->drag_list) { + gtk_selection_data_set(selection_data, + selection_data->target, 8, + (guchar *)summaryview->drag_list, + strlen(summaryview->drag_list)); + } +} + +static void summary_text_adj_value_changed(GtkAdjustment *adj, + SummaryView *summaryview) +{ + static gdouble prev_vadj = 0.0; + + if (summaryview->displayed && adj->value > prev_vadj && + prefs_common.always_show_msg) + summary_mark_displayed_read(summaryview, NULL); + + prev_vadj = adj->value; +} + + +/* custom compare functions for sorting */ + +#define CMP_FUNC_DEF(func_name, val) \ +static gint func_name(GtkTreeModel *model, \ + GtkTreeIter *a, GtkTreeIter *b, gpointer data) \ +{ \ + MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; \ + gint ret; \ + \ + gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, -1); \ + gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, -1); \ + \ + if (!msginfo_a || !msginfo_b) \ + return 0; \ + \ + ret = (val); \ + return (ret != 0) ? ret : \ + (msginfo_a->date_t - msginfo_b->date_t); \ +} + +CMP_FUNC_DEF(summary_cmp_by_mark, + MSG_IS_MARKED(msginfo_a->flags) - MSG_IS_MARKED(msginfo_b->flags)) +CMP_FUNC_DEF(summary_cmp_by_unread, + MSG_IS_UNREAD(msginfo_a->flags) - MSG_IS_UNREAD(msginfo_b->flags)) +CMP_FUNC_DEF(summary_cmp_by_mime, + MSG_IS_MIME(msginfo_a->flags) - MSG_IS_MIME(msginfo_b->flags)) +CMP_FUNC_DEF(summary_cmp_by_label, + MSG_GET_COLORLABEL(msginfo_a->flags) - + MSG_GET_COLORLABEL(msginfo_b->flags)) +CMP_FUNC_DEF(summary_cmp_by_size, msginfo_a->size - msginfo_b->size) + +#undef CMP_FUNC_DEF +#define CMP_FUNC_DEF(func_name, val) \ +static gint func_name(GtkTreeModel *model, \ + GtkTreeIter *a, GtkTreeIter *b, gpointer data) \ +{ \ + MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; \ + \ + gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, -1); \ + gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, -1); \ + \ + if (!msginfo_a || !msginfo_b) \ + return 0; \ + \ + return (val); \ +} + +CMP_FUNC_DEF(summary_cmp_by_num, msginfo_a->msgnum - msginfo_b->msgnum) +CMP_FUNC_DEF(summary_cmp_by_date, msginfo_a->date_t - msginfo_b->date_t) + +#undef CMP_FUNC_DEF + +static gint summary_cmp_by_thread_date(GtkTreeModel *model, + GtkTreeIter *a, GtkTreeIter *b, + gpointer data) +{ + MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; + guint tdate_a, tdate_b; + + gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, S_COL_TDATE, + &tdate_a, -1); + gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, S_COL_TDATE, + &tdate_b, -1); + + if (!msginfo_a || !msginfo_b) + return 0; + + if (tdate_a == 0 && tdate_b == 0) + return msginfo_a->date_t - msginfo_b->date_t; + else + return tdate_a - tdate_b; +} + +#define CMP_FUNC_DEF(func_name, var_name) \ +static gint func_name(GtkTreeModel *model, \ + GtkTreeIter *a, GtkTreeIter *b, gpointer data) \ +{ \ + MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; \ + gint ret; \ + \ + gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, -1); \ + gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, -1); \ + \ + if (!msginfo_a || !msginfo_b) \ + return 0; \ + \ + if (msginfo_a->var_name == NULL) \ + return -(msginfo_b->var_name != NULL); \ + if (msginfo_b->var_name == NULL) \ + return (msginfo_a->var_name != NULL); \ + \ + ret = g_ascii_strcasecmp \ + (msginfo_a->var_name, msginfo_b->var_name); \ + \ + return (ret != 0) ? ret : \ + (msginfo_a->date_t - msginfo_b->date_t); \ +} + +CMP_FUNC_DEF(summary_cmp_by_from, fromname) + +#undef CMP_FUNC_DEF + +static gint summary_cmp_by_to(GtkTreeModel *model, + GtkTreeIter *a, GtkTreeIter *b, + gpointer data) +{ + MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; + gchar *to_a = NULL, *to_b = NULL; + gint ret; + + gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, + S_COL_TO, &to_a, -1); + gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, + S_COL_TO, &to_b, -1); + + if (!msginfo_a || !msginfo_b) { + g_free(to_b); + g_free(to_a); + return 0; + } + + ret = g_ascii_strcasecmp(to_a ? to_a : "", to_b ? to_b : ""); + + g_free(to_b); + g_free(to_a); + + return (ret != 0) ? ret : + (msginfo_a->date_t - msginfo_b->date_t); +} + +static gint summary_cmp_by_subject(GtkTreeModel *model, + GtkTreeIter *a, GtkTreeIter *b, + gpointer data) +{ + MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; + gint ret; + + gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, -1); + gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, -1); + + if (!msginfo_a || !msginfo_b) + return 0; + + if (msginfo_a->subject == NULL) + return -(msginfo_b->subject != NULL); + if (msginfo_b->subject == NULL) + return (msginfo_a->subject != NULL); + + ret = subject_compare_for_sort(msginfo_a->subject, msginfo_b->subject); + + return (ret != 0) ? ret : + (msginfo_a->date_t - msginfo_b->date_t); +} diff --git a/src/summaryview.h b/src/summaryview.h new file mode 100644 index 0000000..201a963 --- /dev/null +++ b/src/summaryview.h @@ -0,0 +1,306 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2010 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __SUMMARY_H__ +#define __SUMMARY_H__ + +#include +#include +#include +#include +#include +#include +#include + +typedef struct _SummaryView SummaryView; +typedef struct _SummaryColumnState SummaryColumnState; + +#include "enums.h" +#include "mainwindow.h" +#include "folderview.h" +#include "headerview.h" +#include "messageview.h" +#include "quick_search.h" +#include "compose.h" +#include "filter.h" +#include "folder.h" +#include "procmsg.h" + +typedef enum +{ + SUMMARY_NONE, + SUMMARY_SELECTED_NONE, + SUMMARY_SELECTED_SINGLE, + SUMMARY_SELECTED_MULTIPLE +} SummarySelection; + +struct _SummaryColumnState +{ + SummaryColumnType type; + gboolean visible; +}; + +struct _SummaryView +{ + GtkWidget *vbox; + + GtkWidget *scrolledwin; + GtkWidget *treeview; + + GtkTreeStore *store; + GtkTreeSelection *selection; + GtkTreeViewColumn *columns[N_SUMMARY_VISIBLE_COLS]; + + GtkWidget *hseparator; + GtkWidget *hbox; + GtkWidget *statlabel_folder; + GtkWidget *statlabel_select; + GtkWidget *statlabel_msgs; + GtkWidget *toggle_eventbox; + GtkWidget *toggle_arrow; + GtkWidget *popupmenu; + GtkWidget *colorlabel_menu; + + GtkItemFactory *popupfactory; + + GtkWidget *reedit_menuitem; + GtkWidget *reedit_separator; + GtkWidget *junk_menuitem; + GtkWidget *nojunk_menuitem; + GtkWidget *junk_separator; + + GtkWidget *window; + + GtkTreeRowReference *selected; + GtkTreeRowReference *displayed; + + GList *selection_list; + + gboolean display_msg; + + SummaryColumnState col_state[N_SUMMARY_VISIBLE_COLS]; + gint col_pos[N_SUMMARY_VISIBLE_COLS]; + + GdkColor color_marked; + GdkColor color_dim; + + guint lock_count; + + MainWindow *mainwin; + FolderView *folderview; + HeaderView *headerview; + MessageView *messageview; + QuickSearch *qsearch; + + FolderItem *folder_item; + + /* current message status */ + gint64 total_size; + gint deleted; + gint moved; + gint copied; + +/* +private: +*/ + /* table for looking up message-id */ + GHashTable *msgid_table; + + /* all message list */ + GSList *all_mlist; + /* filtered message list */ + GSList *flt_mlist; + + gint64 total_flt_msg_size; + gint flt_msg_total; + gint flt_deleted; + gint flt_moved; + gint flt_copied; + gint flt_new; + gint flt_unread; + + gboolean on_filter; + + /* list for moving/deleting messages */ + GSList *tmp_mlist; + FolderItem *to_folder; + /* table for updating folder tree */ + GHashTable *folder_table; + /* counter for filtering */ + gint filtered; + gint flt_count; + gint flt_total; + + gboolean on_button_press; + + /* for DnD */ + gboolean can_toggle_selection; + gboolean on_drag; + GtkTreePath *pressed_path; + gchar *drag_list; + + /* unthreading */ + GSList *pos_list; + + guint write_lock_count; + + /* junk filter list */ + GSList *junk_fltlist; + + /* generic flag */ + gint tmp_flag; +}; + +SummaryView *summary_create(void); + +void summary_init (SummaryView *summaryview); +gboolean summary_show (SummaryView *summaryview, + FolderItem *fitem, + gboolean update_cache); +void summary_clear_list (SummaryView *summaryview); +void summary_clear_all (SummaryView *summaryview); + +void summary_show_queued_msgs (SummaryView *summaryview); + +/* full lock */ +void summary_lock (SummaryView *summaryview); +void summary_unlock (SummaryView *summaryview); +gboolean summary_is_locked (SummaryView *summaryview); +gboolean summary_is_read_locked (SummaryView *summaryview); + +/* write lock (read only) */ +void summary_write_lock (SummaryView *summaryview); +void summary_write_unlock (SummaryView *summaryview); +gboolean summary_is_write_locked (SummaryView *summaryview); + +FolderItem *summary_get_current_folder (SummaryView *summaryview); + +SummarySelection summary_get_selection_type (SummaryView *summaryview); +GSList *summary_get_selected_msg_list (SummaryView *summaryview); +GSList *summary_get_changed_msg_list (SummaryView *summaryview); +GSList *summary_get_msg_list (SummaryView *summaryview); +GSList *summary_get_flagged_msg_list (SummaryView *summaryview, + MsgPermFlags flags); + +MsgInfo *summary_get_msginfo_by_msgnum (SummaryView *summaryview, + guint msgnum); + +void summary_select_prev_unread (SummaryView *summaryview); +void summary_select_next_unread (SummaryView *summaryview); +void summary_select_prev_new (SummaryView *summaryview); +void summary_select_next_new (SummaryView *summaryview); +void summary_select_prev_marked (SummaryView *summaryview); +void summary_select_next_marked (SummaryView *summaryview); +void summary_select_prev_labeled (SummaryView *summaryview); +void summary_select_next_labeled (SummaryView *summaryview); +void summary_select_by_msgnum (SummaryView *summaryview, + guint msgnum); +gboolean summary_select_by_msginfo(SummaryView *summaryview, + MsgInfo *msginfo); + +void summary_select_row (SummaryView *summaryview, + GtkTreeIter *iter, + gboolean display_msg, + gboolean do_refresh); + +void summary_thread_build (SummaryView *summaryview); +void summary_unthread (SummaryView *summaryview); + +void summary_expand_threads (SummaryView *summaryview); +void summary_collapse_threads (SummaryView *summaryview); + +void summary_filter (SummaryView *summaryview, + gboolean selected_only); +void summary_filter_open (SummaryView *summaryview, + FilterCreateType type); + +void summary_filter_junk (SummaryView *summaryview, + gboolean selected_only); + +void summary_junk (SummaryView *summaryview); +void summary_not_junk (SummaryView *summaryview); + +void summary_sort (SummaryView *summaryview, + FolderSortKey sort_key, + FolderSortType sort_type); + +void summary_delete (SummaryView *summaryview); +void summary_delete_duplicated (SummaryView *summaryview); + +gboolean summary_execute (SummaryView *summaryview); + +void summary_attract_by_subject (SummaryView *summaryview); + +gint summary_write_cache (SummaryView *summaryview); + +void summary_pass_key_press_event (SummaryView *summaryview, + GdkEventKey *event); + +void summary_display_msg_selected (SummaryView *summaryview, + gboolean new_window, + gboolean all_headers); +void summary_redisplay_msg (SummaryView *summaryview); +void summary_open_msg (SummaryView *summaryview); +void summary_view_source (SummaryView *summaryview); +void summary_reedit (SummaryView *summaryview); +gboolean summary_step (SummaryView *summaryview, + GtkScrollType type); +void summary_toggle_view (SummaryView *summaryview); + +void summary_update_selected_rows (SummaryView *summaryview); +void summary_update_by_msgnum (SummaryView *summaryview, + guint msgnum); + +void summary_move_selected_to (SummaryView *summaryview, + FolderItem *to_folder); +void summary_move_to (SummaryView *summaryview); +void summary_copy_selected_to (SummaryView *summaryview, + FolderItem *to_folder); +void summary_copy_to (SummaryView *summaryview); +void summary_save_as (SummaryView *summaryview); +void summary_print (SummaryView *summaryview); +void summary_mark (SummaryView *summaryview); +void summary_unmark (SummaryView *summaryview); +void summary_mark_as_unread (SummaryView *summaryview); +void summary_mark_as_read (SummaryView *summaryview); +void summary_mark_thread_as_read (SummaryView *summaryview); +void summary_mark_all_read (SummaryView *summaryview); +void summary_add_address (SummaryView *summaryview); +void summary_select_all (SummaryView *summaryview); +void summary_unselect_all (SummaryView *summaryview); +void summary_select_thread (SummaryView *summaryview); + +void summary_mark_displayed_read (SummaryView *summaryview, + GtkTreeIter *iter); + +void summary_reply (SummaryView *summaryview, + ComposeMode mode); + +void summary_set_colorlabel (SummaryView *summaryview, + guint labelcolor, + GtkWidget *widget); + +void summary_set_column_order (SummaryView *summaryview); +void summary_get_column_order (SummaryView *summaryview); + +void summary_qsearch_reset (SummaryView *summaryview); +void summary_qsearch_clear_entry (SummaryView *summaryview); +void summary_qsearch (SummaryView *summaryview); + +#endif /* __SUMMARY_H__ */ diff --git a/src/syldap.c b/src/syldap.c new file mode 100644 index 0000000..6c609ad --- /dev/null +++ b/src/syldap.c @@ -0,0 +1,1127 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Functions necessary to access LDAP servers. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef USE_LDAP + +#include +#include +#include +#include +#include +#define LDAP_DEPRECATED 1 +#include +#include +#include +/* #include */ + +#include "mgutils.h" +#include "addritem.h" +#include "addrcache.h" +#include "syldap.h" +#include "utils.h" + +/* +* Create new LDAP server interface object. +*/ +SyldapServer *syldap_create() { + SyldapServer *ldapServer; + + debug_print("Creating LDAP server interface object\n"); + + ldapServer = g_new0( SyldapServer, 1 ); + ldapServer->name = NULL; + ldapServer->hostName = NULL; + ldapServer->port = SYLDAP_DFL_PORT; + ldapServer->baseDN = NULL; + ldapServer->bindDN = NULL; + ldapServer->bindPass = NULL; + ldapServer->searchCriteria = NULL; + ldapServer->searchValue = NULL; + ldapServer->entriesRead = 0; + ldapServer->maxEntries = SYLDAP_MAX_ENTRIES; + ldapServer->timeOut = SYLDAP_DFL_TIMEOUT; + ldapServer->newSearch = TRUE; + ldapServer->addressCache = addrcache_create(); + ldapServer->thread = NULL; + ldapServer->busyFlag = FALSE; + ldapServer->retVal = MGU_SUCCESS; + ldapServer->callBack = NULL; + ldapServer->accessFlag = FALSE; + ldapServer->idleId = 0; + return ldapServer; +} + +/* +* Specify name to be used. +*/ +void syldap_set_name( SyldapServer* ldapServer, const gchar *value ) { + ldapServer->name = mgu_replace_string( ldapServer->name, value ); + g_strstrip( ldapServer->name ); +} + +/* +* Specify hostname to be used. +*/ +void syldap_set_host( SyldapServer* ldapServer, const gchar *value ) { + addrcache_refresh( ldapServer->addressCache ); + ldapServer->hostName = mgu_replace_string( ldapServer->hostName, value ); + g_strstrip( ldapServer->hostName ); +} + +/* +* Specify port to be used. +*/ +void syldap_set_port( SyldapServer* ldapServer, const gint value ) { + addrcache_refresh( ldapServer->addressCache ); + if( value > 0 ) { + ldapServer->port = value; + } + else { + ldapServer->port = SYLDAP_DFL_PORT; + } +} + +/* +* Specify base DN to be used. +*/ +void syldap_set_base_dn( SyldapServer* ldapServer, const gchar *value ) { + addrcache_refresh( ldapServer->addressCache ); + ldapServer->baseDN = mgu_replace_string( ldapServer->baseDN, value ); + g_strstrip( ldapServer->baseDN ); +} + +/* +* Specify bind DN to be used. +*/ +void syldap_set_bind_dn( SyldapServer* ldapServer, const gchar *value ) { + addrcache_refresh( ldapServer->addressCache ); + ldapServer->bindDN = mgu_replace_string( ldapServer->bindDN, value ); + g_strstrip( ldapServer->bindDN ); +} + +/* +* Specify bind password to be used. +*/ +void syldap_set_bind_password( SyldapServer* ldapServer, const gchar *value ) { + addrcache_refresh( ldapServer->addressCache ); + ldapServer->bindPass = mgu_replace_string( ldapServer->bindPass, value ); + g_strstrip( ldapServer->bindPass ); +} + +/* +* Specify search criteria to be used. +*/ +void syldap_set_search_criteria( SyldapServer* ldapServer, const gchar *value ) { + addrcache_refresh( ldapServer->addressCache ); + ldapServer->searchCriteria = mgu_replace_string( ldapServer->searchCriteria, value ); + g_strstrip( ldapServer->searchCriteria ); + ldapServer->newSearch = TRUE; +} + +/* +* Specify search value to be searched for. +*/ +void syldap_set_search_value( SyldapServer* ldapServer, const gchar *value ) { + addrcache_refresh( ldapServer->addressCache ); + ldapServer->searchValue = mgu_replace_string( ldapServer->searchValue, value ); + g_strstrip( ldapServer->searchValue ); + ldapServer->newSearch = TRUE; +} + +/* +* Specify maximum number of entries to retrieve. +*/ +void syldap_set_max_entries( SyldapServer* ldapServer, const gint value ) { + addrcache_refresh( ldapServer->addressCache ); + if( value > 0 ) { + ldapServer->maxEntries = value; + } + else { + ldapServer->maxEntries = SYLDAP_MAX_ENTRIES; + } +} + +/* +* Specify timeout value for LDAP operation (in seconds). +*/ +void syldap_set_timeout( SyldapServer* ldapServer, const gint value ) { + addrcache_refresh( ldapServer->addressCache ); + if( value > 0 ) { + ldapServer->timeOut = value; + } + else { + ldapServer->timeOut = SYLDAP_DFL_TIMEOUT; + } +} + +/* +* Register a callback function. When called, the function will be passed +* this object as an argument. +*/ +void syldap_set_callback( SyldapServer *ldapServer, void *func ) { + ldapServer->callBack = func; +} + +void syldap_set_accessed( SyldapServer *ldapServer, const gboolean value ) { + g_return_if_fail( ldapServer != NULL ); + ldapServer->accessFlag = value; +} + +/* +* Refresh internal variables to force a file read. +*/ +void syldap_force_refresh( SyldapServer *ldapServer ) { + addrcache_refresh( ldapServer->addressCache ); + ldapServer->newSearch = TRUE; +} + +gint syldap_get_status( SyldapServer *ldapServer ) { + g_return_val_if_fail( ldapServer != NULL, -1 ); + return ldapServer->retVal; +} + +ItemFolder *syldap_get_root_folder( SyldapServer *ldapServer ) { + g_return_val_if_fail( ldapServer != NULL, NULL ); + return addrcache_get_root_folder( ldapServer->addressCache ); +} + +gchar *syldap_get_name( SyldapServer *ldapServer ) { + g_return_val_if_fail( ldapServer != NULL, NULL ); + return ldapServer->name; +} + +gboolean syldap_get_accessed( SyldapServer *ldapServer ) { + g_return_val_if_fail( ldapServer != NULL, FALSE ); + return ldapServer->accessFlag; +} + +/* +* Free up LDAP server interface object by releasing internal memory. +*/ +void syldap_free( SyldapServer *ldapServer ) { + g_return_if_fail( ldapServer != NULL ); + + debug_print("Freeing LDAP server interface object\n"); + + ldapServer->callBack = NULL; + + /* Free internal stuff */ + g_free( ldapServer->name ); + g_free( ldapServer->hostName ); + g_free( ldapServer->baseDN ); + g_free( ldapServer->bindDN ); + g_free( ldapServer->bindPass ); + g_free( ldapServer->searchCriteria ); + g_free( ldapServer->searchValue ); + g_free( ldapServer->thread ); + + ldapServer->port = 0; + ldapServer->entriesRead = 0; + ldapServer->maxEntries = 0; + ldapServer->newSearch = FALSE; + + /* Clear cache */ + addrcache_clear( ldapServer->addressCache ); + addrcache_free( ldapServer->addressCache ); + + /* Clear pointers */ + ldapServer->name = NULL; + ldapServer->hostName = NULL; + ldapServer->baseDN = NULL; + ldapServer->bindDN = NULL; + ldapServer->bindPass = NULL; + ldapServer->searchCriteria = NULL; + ldapServer->searchValue = NULL; + ldapServer->addressCache = NULL; + ldapServer->thread = NULL; + ldapServer->busyFlag = FALSE; + ldapServer->retVal = MGU_SUCCESS; + ldapServer->accessFlag = FALSE; + + /* Now release LDAP object */ + g_free( ldapServer ); + +} + +/* +* Display object to specified stream. +*/ +void syldap_print_data( SyldapServer *ldapServer, FILE *stream ) { + g_return_if_fail( ldapServer != NULL ); + + fprintf( stream, "SyldapServer:\n" ); + fprintf( stream, " name: '%s'\n", ldapServer->name ); + fprintf( stream, "host name: '%s'\n", ldapServer->hostName ); + fprintf( stream, " port: %d\n", ldapServer->port ); + fprintf( stream, " base dn: '%s'\n", ldapServer->baseDN ); + fprintf( stream, " bind dn: '%s'\n", ldapServer->bindDN ); + fprintf( stream, "bind pass: '%s'\n", ldapServer->bindPass ); + fprintf( stream, " criteria: '%s'\n", ldapServer->searchCriteria ); + fprintf( stream, "searchval: '%s'\n", ldapServer->searchValue ); + fprintf( stream, "max entry: %d\n", ldapServer->maxEntries ); + fprintf( stream, " num read: %d\n", ldapServer->entriesRead ); + fprintf( stream, " ret val: %d\n", ldapServer->retVal ); + addrcache_print( ldapServer->addressCache, stream ); + addritem_print_item_folder( ldapServer->addressCache->rootFolder, stream ); +} + +/* +* Display object to specified stream. +*/ +void syldap_print_short( SyldapServer *ldapServer, FILE *stream ) { + g_return_if_fail( ldapServer != NULL ); + + fprintf( stream, "SyldapServer:\n" ); + fprintf( stream, " name: '%s'\n", ldapServer->name ); + fprintf( stream, "host name: '%s'\n", ldapServer->hostName ); + fprintf( stream, " port: %d\n", ldapServer->port ); + fprintf( stream, " base dn: '%s'\n", ldapServer->baseDN ); + fprintf( stream, " bind dn: '%s'\n", ldapServer->bindDN ); + fprintf( stream, "bind pass: '%s'\n", ldapServer->bindPass ); + fprintf( stream, " criteria: '%s'\n", ldapServer->searchCriteria ); + fprintf( stream, "searchval: '%s'\n", ldapServer->searchValue ); + fprintf( stream, "max entry: %d\n", ldapServer->maxEntries ); + fprintf( stream, " num read: %d\n", ldapServer->entriesRead ); + fprintf( stream, " ret val: %d\n", ldapServer->retVal ); +} + +#if 0 +/* +* Build an address list entry and append to list of address items. Name is formatted +* as it appears in the common name (cn) attribute. +*/ +static void syldap_build_items_cn( SyldapServer *ldapServer, GSList *listName, GSList *listAddr ) { + ItemPerson *person; + ItemEMail *email; + GSList *nodeName = listName; + + while( nodeName ) { + GSList *nodeAddress = listAddr; + person = addritem_create_item_person(); + addritem_person_set_common_name( person, nodeName->data ); + addrcache_id_person( ldapServer->addressCache, person ); + addrcache_add_person( ldapServer->addressCache, person ); + + while( nodeAddress ) { + email = addritem_create_item_email(); + addritem_email_set_address( email, nodeAddress->data ); + addrcache_id_email( ldapServer->addressCache, email ); + addrcache_person_add_email( ldapServer->addressCache, person, email ); + nodeAddress = g_slist_next( nodeAddress ); + ldapServer->entriesRead++; + } + nodeName = g_slist_next( nodeName ); + } +} +#endif + +/* +* Build an address list entry and append to list of address items. Name is formatted +* as " ". +*/ +static void syldap_build_items_fl( SyldapServer *ldapServer, GSList *listAddr, GSList *listFirst, GSList *listLast ) { + GSList *nodeFirst = listFirst; + GSList *nodeAddress = listAddr; + gchar *firstName = NULL, *lastName = NULL, *fullName = NULL; + gint iLen = 0, iLenT = 0; + ItemPerson *person; + ItemEMail *email; + + /* Find longest first name in list */ + while( nodeFirst ) { + if( firstName == NULL ) { + firstName = nodeFirst->data; + iLen = strlen( firstName ); + } + else { + if( ( iLenT = strlen( nodeFirst->data ) ) > iLen ) { + firstName = nodeFirst->data; + iLen = iLenT; + } + } + nodeFirst = g_slist_next( nodeFirst ); + } + + /* Format name */ + if( listLast ) { + lastName = listLast->data; + } + + if( firstName ) { + if( lastName ) { + fullName = g_strdup_printf( "%s %s", firstName, lastName ); + } + else { + fullName = g_strdup_printf( "%s", firstName ); + } + } + else { + if( lastName ) { + fullName = g_strdup_printf( "%s", lastName ); + } + } + if( fullName ) { + g_strchug( fullName ); g_strchomp( fullName ); + } + + if( nodeAddress ) { + person = addritem_create_item_person(); + addritem_person_set_common_name( person, fullName ); + addritem_person_set_first_name( person, firstName ); + addritem_person_set_last_name( person, lastName ); + addrcache_id_person( ldapServer->addressCache, person ); + addrcache_add_person( ldapServer->addressCache, person ); + } + + /* Add address item */ + while( nodeAddress ) { + email = addritem_create_item_email(); + addritem_email_set_address( email, nodeAddress->data ); + addrcache_id_email( ldapServer->addressCache, email ); + addrcache_person_add_email( ldapServer->addressCache, person, email ); + nodeAddress = g_slist_next( nodeAddress ); + ldapServer->entriesRead++; + } + g_free( fullName ); + fullName = firstName = lastName = NULL; + +} + +/* +* Add all attribute values to a list. +*/ +static GSList *syldap_add_list_values( LDAP *ld, LDAPMessage *entry, char *attr ) { + GSList *list = NULL; + gint i; + gchar **vals; + + if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) { + for( i = 0; vals[i] != NULL; i++ ) { + /* printf( "lv\t%s: %s\n", attr, vals[i] ); */ + list = g_slist_append( list, g_strdup( vals[i] ) ); + } + } + ldap_value_free( vals ); + return list; +} + +/* +* Add a single attribute value to a list. +*/ +static GSList *syldap_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) { + GSList *list = NULL; + gchar **vals; + + if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) { + if( vals[0] != NULL ) { + /* printf( "sv\t%s: %s\n", attr, vals[0] ); */ + list = g_slist_append( list, g_strdup( vals[0] ) ); + } + } + ldap_value_free( vals ); + return list; +} + +/* +* Free linked lists of character strings. +*/ +static void syldap_free_lists( GSList *listName, GSList *listAddr, GSList *listID, GSList *listDN, GSList *listFirst, GSList *listLast ) { + mgu_free_list( listName ); + mgu_free_list( listAddr ); + mgu_free_list( listID ); + mgu_free_list( listDN ); + mgu_free_list( listFirst ); + mgu_free_list( listLast ); +} + +/* +* Check parameters that are required for a search. This should +* be called before performing a search. +* Return: TRUE if search criteria appear OK. +*/ +gboolean syldap_check_search( SyldapServer *ldapServer ) { + g_return_val_if_fail( ldapServer != NULL, FALSE ); + + ldapServer->retVal = MGU_LDAP_CRITERIA; + + /* Test search criteria */ + if( ldapServer->searchCriteria == NULL ) { + return FALSE; + } + if( strlen( ldapServer->searchCriteria ) < 1 ) { + return FALSE; + } + + if( ldapServer->searchValue == NULL ) { + return FALSE; + } + if( strlen( ldapServer->searchValue ) < 1 ) { + return FALSE; + } + + ldapServer->retVal = MGU_SUCCESS; + return TRUE; +} + +/* +* Perform the LDAP search, reading LDAP entries into cache. +* Note that one LDAP entry can have multiple values for many of its +* attributes. If these attributes are E-Mail addresses; these are +* broken out into separate address items. For any other attribute, +* only the first occurrence is read. +*/ +gint syldap_search( SyldapServer *ldapServer ) { + LDAP *ld; + LDAPMessage *result, *e; + char *attribs[10]; + char *attribute; + gchar *criteria; + BerElement *ber; + gint rc; + GSList *listName = NULL, *listAddress = NULL, *listID = NULL; + GSList *listFirst = NULL, *listLast = NULL, *listDN = NULL; + struct timeval timeout; + gboolean entriesFound = FALSE; + + g_return_val_if_fail( ldapServer != NULL, -1 ); + + ldapServer->retVal = MGU_SUCCESS; + if( ! syldap_check_search( ldapServer ) ) { + return ldapServer->retVal; + } + + /* Set timeout */ + timeout.tv_sec = ldapServer->timeOut; + timeout.tv_usec = 0L; + + ldapServer->entriesRead = 0; + if( ( ld = ldap_init( ldapServer->hostName, ldapServer->port ) ) == NULL ) { + ldapServer->retVal = MGU_LDAP_INIT; + return ldapServer->retVal; + } + + /* printf( "connected to LDAP host %s on port %d\n", ldapServer->hostName, ldapServer->port ); */ + + /* Bind to the server, if required */ + if( ldapServer->bindDN ) { + if( * ldapServer->bindDN != '\0' ) { + /* printf( "binding...\n" ); */ + rc = ldap_simple_bind_s( ld, ldapServer->bindDN, ldapServer->bindPass ); + /* printf( "rc=%d\n", rc ); */ + if( rc != LDAP_SUCCESS ) { + /* printf( "LDAP Error: ldap_simple_bind_s: %s\n", ldap_err2string( rc ) ); */ + ldap_unbind( ld ); + ldapServer->retVal = MGU_LDAP_BIND; + return ldapServer->retVal; + } + } + } + + /* Define all attributes we are interested in. */ + attribs[0] = SYLDAP_ATTR_DN; + attribs[1] = SYLDAP_ATTR_COMMONNAME; + attribs[2] = SYLDAP_ATTR_GIVENNAME; + attribs[3] = SYLDAP_ATTR_SURNAME; + attribs[4] = SYLDAP_ATTR_EMAIL; + attribs[5] = SYLDAP_ATTR_UID; + attribs[6] = NULL; + + /* Create LDAP search string and apply search criteria */ + criteria = g_strdup_printf( ldapServer->searchCriteria, ldapServer->searchValue ); + rc = ldap_search_ext_s( ld, ldapServer->baseDN, LDAP_SCOPE_SUBTREE, criteria, attribs, 0, NULL, NULL, + &timeout, 0, &result ); + g_free( criteria ); + criteria = NULL; + if( rc == LDAP_TIMEOUT ) { + ldap_unbind( ld ); + ldapServer->retVal = MGU_LDAP_TIMEOUT; + return ldapServer->retVal; + } + if( rc != LDAP_SUCCESS ) { + /* printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) ); */ + ldap_unbind( ld ); + ldapServer->retVal = MGU_LDAP_SEARCH; + return ldapServer->retVal; + } + + /* printf( "Total results are: %d\n", ldap_count_entries( ld, result ) ); */ + + /* Clear the cache if we have new entries, otherwise leave untouched. */ + if( ldap_count_entries( ld, result ) > 0 ) { + addrcache_clear( ldapServer->addressCache ); + } + + /* Process results */ + ldapServer->entriesRead = 0; + for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) { + entriesFound = TRUE; + if( ldapServer->entriesRead >= ldapServer->maxEntries ) break; + /* printf( "DN: %s\n", ldap_get_dn( ld, e ) ); */ + + /* Process all attributes */ + for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL; + attribute = ldap_next_attribute( ld, e, ber ) ) { + if( g_ascii_strcasecmp( attribute, SYLDAP_ATTR_COMMONNAME ) == 0 ) { + listName = syldap_add_list_values( ld, e, attribute ); + } + if( g_ascii_strcasecmp( attribute, SYLDAP_ATTR_EMAIL ) == 0 ) { + listAddress = syldap_add_list_values( ld, e, attribute ); + } + if( g_ascii_strcasecmp( attribute, SYLDAP_ATTR_UID ) == 0 ) { + listID = syldap_add_single_value( ld, e, attribute ); + } + if( g_ascii_strcasecmp( attribute, SYLDAP_ATTR_GIVENNAME ) == 0 ) { + listFirst = syldap_add_list_values( ld, e, attribute ); + } + if( g_ascii_strcasecmp( attribute, SYLDAP_ATTR_SURNAME ) == 0 ) { + listLast = syldap_add_single_value( ld, e, attribute ); + } + if( g_ascii_strcasecmp( attribute, SYLDAP_ATTR_DN ) == 0 ) { + listDN = syldap_add_single_value( ld, e, attribute ); + } + + /* Free memory used to store attribute */ + ldap_memfree( attribute ); + } + + /* Format and add items to cache */ + syldap_build_items_fl( ldapServer, listAddress, listFirst, listLast ); + + /* Free up */ + syldap_free_lists( listName, listAddress, listID, listDN, listFirst, listLast ); + listName = listAddress = listID = listFirst = listLast = listDN = NULL; + + if( ber != NULL ) { + ber_free( ber, 0 ); + } + } + + syldap_free_lists( listName, listAddress, listID, listDN, listFirst, listLast ); + listName = listAddress = listID = listFirst = listLast = listDN = NULL; + + /* Free up and disconnect */ + ldap_msgfree( result ); + ldap_unbind( ld ); + ldapServer->newSearch = FALSE; + if( entriesFound ) { + ldapServer->retVal = MGU_SUCCESS; + } + else { + ldapServer->retVal = MGU_LDAP_NOENTRIES; + } + return ldapServer->retVal; +} + +/* syldap_display_search_results() - updates the ui. this function is called from the + * main thread (the thread running the GTK event loop). */ +static gboolean syldap_display_search_results(SyldapServer *ldapServer) +{ + /* NOTE: when this function is called the accompanying thread should + * already be terminated. */ + gdk_threads_enter(); + ldapServer->callBack(ldapServer); + gdk_threads_leave(); + /* FIXME: match should know whether to free this SyldapServer stuff. */ + g_free(ldapServer->thread); + ldapServer->thread = NULL; + return FALSE; +} + +/* ============================================================================================ */ +/* +* Read data into list. Main entry point +* Return: TRUE if file read successfully. +*/ +/* ============================================================================================ */ +gint syldap_read_data( SyldapServer *ldapServer ) { + g_return_val_if_fail( ldapServer != NULL, -1 ); + + ldapServer->accessFlag = FALSE; + pthread_detach( pthread_self() ); + if( ldapServer->newSearch ) { + /* Read data into the list */ + syldap_search( ldapServer ); + + /* Mark cache */ + ldapServer->addressCache->modified = FALSE; + ldapServer->addressCache->dataRead = TRUE; + ldapServer->accessFlag = FALSE; + } + + /* Callback */ + ldapServer->busyFlag = FALSE; + if( ldapServer->callBack ) { + /* make the ui thread update the search results */ + ldapServer->idleId = g_idle_add((GSourceFunc)syldap_display_search_results, ldapServer); + } + + return ldapServer->retVal; +} + +/* ============================================================================================ */ +/* +* Cancel read with thread. +*/ +/* ============================================================================================ */ +void syldap_cancel_read( SyldapServer *ldapServer ) { + g_return_if_fail( ldapServer != NULL ); + + /* DELETEME: this is called from inside UI thread so it's OK, Christoph! */ + if( ldapServer->thread ) { + /* printf( "thread cancelled\n" ); */ + pthread_cancel( *ldapServer->thread ); + } + g_free(ldapServer->thread); + ldapServer->thread = NULL; + ldapServer->busyFlag = FALSE; +} + +/* ============================================================================================ */ +/* +* Read data into list using a background thread. +* Return: TRUE if file read successfully. Callback function will be +* notified when search is complete. +*/ +/* ============================================================================================ */ +gint syldap_read_data_th( SyldapServer *ldapServer ) { + g_return_val_if_fail( ldapServer != NULL, -1 ); + + ldapServer->busyFlag = FALSE; + syldap_check_search( ldapServer ); + if( ldapServer->retVal == MGU_SUCCESS ) { + /* debug_print("Staring LDAP read thread\n"); */ + + ldapServer->busyFlag = TRUE; + ldapServer->thread = g_new0(pthread_t, 1); + pthread_create( ldapServer->thread, NULL, (void *) syldap_read_data, (void *) ldapServer ); + } + return ldapServer->retVal; +} + +/* +* Return link list of persons. +*/ +GList *syldap_get_list_person( SyldapServer *ldapServer ) { + g_return_val_if_fail( ldapServer != NULL, NULL ); + return addrcache_get_list_person( ldapServer->addressCache ); +} + +/* +* Return link list of folders. This is always NULL since there are +* no folders in GnomeCard. +* Return: NULL. +*/ +GList *syldap_get_list_folder( SyldapServer *ldapServer ) { + g_return_val_if_fail( ldapServer != NULL, NULL ); + return NULL; +} + +#define SYLDAP_TEST_FILTER "(objectclass=*)" +#define SYLDAP_SEARCHBASE_V2 "cn=config" +#define SYLDAP_SEARCHBASE_V3 "" +#define SYLDAP_V2_TEST_ATTR "database" +#define SYLDAP_V3_TEST_ATTR "namingcontexts" + +/* +* Attempt to discover the base DN for the server. +* Enter: +* host Host name +* port Port number +* bindDN Bind DN (optional). +* bindPW Bind PW (optional). +* tov Timeout value (seconds), or 0 for none, default 30 secs. +* Return: List of Base DN's, or NULL if could not read. Base DN should +* be g_free() when done. +*/ +GList *syldap_read_basedn_s( const gchar *host, const gint port, const gchar *bindDN, const gchar *bindPW, const gint tov ) { + GList *baseDN = NULL; + LDAP *ld; + gint rc, i; + LDAPMessage *result, *e; + gchar *attribs[10]; + BerElement *ber; + gchar *attribute; + gchar **vals; + struct timeval timeout; + + if( host == NULL ) return baseDN; + if( port < 1 ) return baseDN; + + /* Set timeout */ + timeout.tv_usec = 0L; + if( tov > 0 ) { + timeout.tv_sec = tov; + } + else { + timeout.tv_sec = 30L; + } + + /* Connect to server. */ + if( ( ld = ldap_init( host, port ) ) == NULL ) { + return baseDN; + } + + /* Bind to the server, if required */ + if( bindDN ) { + if( *bindDN != '\0' ) { + rc = ldap_simple_bind_s( ld, bindDN, bindPW ); + if( rc != LDAP_SUCCESS ) { + /* printf( "LDAP Error: ldap_simple_bind_s: %s\n", ldap_err2string( rc ) ); */ + ldap_unbind( ld ); + return baseDN; + } + } + } + + /* Test for LDAP version 3 */ + attribs[0] = SYLDAP_V3_TEST_ATTR; + attribs[1] = NULL; + rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V3, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs, + 0, NULL, NULL, &timeout, 0, &result ); + if( rc == LDAP_SUCCESS ) { + /* Process entries */ + for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) { + /* printf( "DN: %s\n", ldap_get_dn( ld, e ) ); */ + + /* Process attributes */ + for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL; + attribute = ldap_next_attribute( ld, e, ber ) ) { + if( g_ascii_strcasecmp( attribute, SYLDAP_V3_TEST_ATTR ) == 0 ) { + if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) { + for( i = 0; vals[i] != NULL; i++ ) { + /* printf( "\t%s: %s\n", attribute, vals[i] ); */ + baseDN = g_list_append( baseDN, g_strdup( vals[i] ) ); + } + } + ldap_value_free( vals ); + } + ldap_memfree( attribute ); + } + if( ber != NULL ) { + ber_free( ber, 0 ); + } + } + ldap_msgfree( result ); + } + else { + } + + if( baseDN == NULL ) { + /* Test for LDAP version 2 */ + attribs[0] = NULL; + rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V2, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs, + 0, NULL, NULL, &timeout, 0, &result ); + if( rc == LDAP_SUCCESS ) { + /* Process entries */ + for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) { + /* if( baseDN ) break; */ + /* printf( "DN: %s\n", ldap_get_dn( ld, e ) ); */ + + /* Process attributes */ + for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL; + attribute = ldap_next_attribute( ld, e, ber ) ) { + /* if( baseDN ) break; */ + if( g_ascii_strcasecmp( attribute, SYLDAP_V2_TEST_ATTR ) == 0 ) { + if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) { + for( i = 0; vals[i] != NULL; i++ ) { + char *ch; + /* Strip the 'ldb:' from the front of the value */ + ch = ( char * ) strchr( vals[i], ':' ); + if( ch ) { + gchar *bn = g_strdup( ++ch ); + g_strchomp( bn ); + g_strchug( bn ); + baseDN = g_list_append( baseDN, g_strdup( bn ) ); + } + } + } + ldap_value_free( vals ); + } + ldap_memfree( attribute ); + } + if( ber != NULL ) { + ber_free( ber, 0 ); + } + } + ldap_msgfree( result ); + } + } + ldap_unbind( ld ); + return baseDN; +} + +/* +* Attempt to discover the base DN for the server. +* Enter: ldapServer Server to test. +* Return: List of Base DN's, or NULL if could not read. Base DN should +* be g_free() when done. Return code set in ldapServer. +*/ +GList *syldap_read_basedn( SyldapServer *ldapServer ) { + GList *baseDN = NULL; + LDAP *ld; + gint rc, i; + LDAPMessage *result, *e; + gchar *attribs[10]; + BerElement *ber; + gchar *attribute; + gchar **vals; + struct timeval timeout; + + if( ldapServer == NULL ) return baseDN; + ldapServer->retVal = MGU_BAD_ARGS; + if( ldapServer->hostName == NULL ) return baseDN; + if( ldapServer->port < 1 ) return baseDN; + + /* Set timeout */ + timeout.tv_usec = 0L; + if( ldapServer->timeOut > 0 ) { + timeout.tv_sec = ldapServer->timeOut; + } + else { + timeout.tv_sec = 30L; + } + + /* Connect to server. */ + if( ( ld = ldap_init( ldapServer->hostName, ldapServer->port ) ) == NULL ) { + ldapServer->retVal = MGU_LDAP_INIT; + return baseDN; + } + + /* Bind to the server, if required */ + if( ldapServer->bindDN ) { + if( *ldapServer->bindDN != '\0' ) { + rc = ldap_simple_bind_s( ld, ldapServer->bindDN, ldapServer->bindPass ); + if( rc != LDAP_SUCCESS ) { + /* printf( "LDAP Error: ldap_simple_bind_s: %s\n", ldap_err2string( rc ) ); */ + ldap_unbind( ld ); + ldapServer->retVal = MGU_LDAP_BIND; + return baseDN; + } + } + } + + ldapServer->retVal = MGU_LDAP_SEARCH; + + /* Test for LDAP version 3 */ + attribs[0] = SYLDAP_V3_TEST_ATTR; + attribs[1] = NULL; + rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V3, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs, + 0, NULL, NULL, &timeout, 0, &result ); + if( rc == LDAP_SUCCESS ) { + /* Process entries */ + for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) { + /* printf( "DN: %s\n", ldap_get_dn( ld, e ) ); */ + + /* Process attributes */ + for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL; + attribute = ldap_next_attribute( ld, e, ber ) ) { + if( g_ascii_strcasecmp( attribute, SYLDAP_V3_TEST_ATTR ) == 0 ) { + if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) { + for( i = 0; vals[i] != NULL; i++ ) { + /* printf( "\t%s: %s\n", attribute, vals[i] ); */ + baseDN = g_list_append( baseDN, g_strdup( vals[i] ) ); + } + } + ldap_value_free( vals ); + } + ldap_memfree( attribute ); + } + if( ber != NULL ) { + ber_free( ber, 0 ); + } + } + ldap_msgfree( result ); + ldapServer->retVal = MGU_SUCCESS; + } + else if( rc == LDAP_TIMEOUT ) { + ldapServer->retVal = MGU_LDAP_TIMEOUT; + } + + if( baseDN == NULL ) { + /* Test for LDAP version 2 */ + attribs[0] = NULL; + rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V2, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs, + 0, NULL, NULL, &timeout, 0, &result ); + if( rc == LDAP_SUCCESS ) { + /* Process entries */ + for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) { + /* if( baseDN ) break; */ + /* printf( "DN: %s\n", ldap_get_dn( ld, e ) ); */ + + /* Process attributes */ + for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL; + attribute = ldap_next_attribute( ld, e, ber ) ) { + /* if( baseDN ) break; */ + if( g_ascii_strcasecmp( attribute, SYLDAP_V2_TEST_ATTR ) == 0 ) { + if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) { + for( i = 0; vals[i] != NULL; i++ ) { + char *ch; + /* Strip the 'ldb:' from the front of the value */ + ch = ( char * ) strchr( vals[i], ':' ); + if( ch ) { + gchar *bn = g_strdup( ++ch ); + g_strchomp( bn ); + g_strchug( bn ); + baseDN = g_list_append( baseDN, g_strdup( bn ) ); + } + } + } + ldap_value_free( vals ); + } + ldap_memfree( attribute ); + } + if( ber != NULL ) { + ber_free( ber, 0 ); + } + } + ldap_msgfree( result ); + ldapServer->retVal = MGU_SUCCESS; + } + else if( rc == LDAP_TIMEOUT ) { + ldapServer->retVal = MGU_LDAP_TIMEOUT; + } + } + ldap_unbind( ld ); + + return baseDN; +} + +/* +* Attempt to connect to the server. +* Enter: +* host Host name +* port Port number +* Return: TRUE if connected successfully. +*/ +gboolean syldap_test_connect_s( const gchar *host, const gint port ) { + gboolean retVal = FALSE; + LDAP *ld; + + if( host == NULL ) return retVal; + if( port < 1 ) return retVal; + if( ( ld = ldap_open( host, port ) ) != NULL ) { + retVal = TRUE; + } + if( ld != NULL ) { + ldap_unbind( ld ); + } + return retVal; +} + +/* +* Attempt to connect to the server. +* Enter: ldapServer Server to test. +* Return: TRUE if connected successfully. Return code set in ldapServer. +*/ +gboolean syldap_test_connect( SyldapServer *ldapServer ) { + gboolean retVal = FALSE; + LDAP *ld; + + if( ldapServer == NULL ) return retVal; + ldapServer->retVal = MGU_BAD_ARGS; + if( ldapServer->hostName == NULL ) return retVal; + if( ldapServer->port < 1 ) return retVal; + ldapServer->retVal = MGU_LDAP_INIT; + if( ( ld = ldap_open( ldapServer->hostName, ldapServer->port ) ) != NULL ) { + ldapServer->retVal = MGU_SUCCESS; + retVal = TRUE; + } + if( ld != NULL ) { + ldap_unbind( ld ); + } + return retVal; +} + +#define LDAP_LINK_LIB_NAME_1 "libldap.so" +#define LDAP_LINK_LIB_NAME_2 "liblber.so" +#define LDAP_LINK_LIB_NAME_3 "libresolv.so" +#define LDAP_LINK_LIB_NAME_4 "libpthread.so" + +/* +* Test whether LDAP libraries installed. +* Return: TRUE if library available. +*/ +#if 0 +gboolean syldap_test_ldap_lib() { + void *handle, *fun; + + /* Get library */ + handle = dlopen( LDAP_LINK_LIB_NAME_1, RTLD_LAZY ); + if( ! handle ) { + return FALSE; + } + + /* Test for symbols we need */ + fun = dlsym( handle, "ldap_init" ); + if( ! fun ) { + dlclose( handle ); + return FALSE; + } + dlclose( handle ); handle = NULL; fun = NULL; + + handle = dlopen( LDAP_LINK_LIB_NAME_2, RTLD_LAZY ); + if( ! handle ) { + return FALSE; + } + fun = dlsym( handle, "ber_init" ); + if( ! fun ) { + dlclose( handle ); + return FALSE; + } + dlclose( handle ); handle = NULL; fun = NULL; + + handle = dlopen( LDAP_LINK_LIB_NAME_3, RTLD_LAZY ); + if( ! handle ) { + return FALSE; + } + fun = dlsym( handle, "res_query" ); + if( ! fun ) { + dlclose( handle ); + return FALSE; + } + dlclose( handle ); handle = NULL; fun = NULL; + + handle = dlopen( LDAP_LINK_LIB_NAME_4, RTLD_LAZY ); + if( ! handle ) { + return FALSE; + } + fun = dlsym( handle, "pthread_create" ); + if( ! fun ) { + dlclose( handle ); + return FALSE; + } + dlclose( handle ); handle = NULL; fun = NULL; + + return TRUE; +} +#endif /* 0 */ + +#endif /* USE_LDAP */ + +/* +* End of Source. +*/ diff --git a/src/syldap.h b/src/syldap.h new file mode 100644 index 0000000..f760dfa --- /dev/null +++ b/src/syldap.h @@ -0,0 +1,111 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Definitions necessary to access LDAP servers. + */ + +#ifndef __SYLDAP_H__ +#define __SYLDAP_H__ + +#ifdef USE_LDAP + +#include +#include + +#include "addritem.h" +#include "addrcache.h" + +#define SYLDAP_DFL_PORT 389 +#define SYLDAP_MAX_ENTRIES 20 +#define SYLDAP_DFL_TIMEOUT 30 +#define SYLDAP_DFL_CRITERIA "(&(mail=*)(cn=%s*))" + +#define SYLDAP_ATTR_DN "dn" +#define SYLDAP_ATTR_COMMONNAME "cn" +#define SYLDAP_ATTR_GIVENNAME "givenName" +#define SYLDAP_ATTR_SURNAME "sn" +#define SYLDAP_ATTR_EMAIL "mail" +#define SYLDAP_ATTR_UID "uid" + +typedef struct _SyldapServer SyldapServer; +struct _SyldapServer { + gchar *name; + gchar *hostName; + gint port; + gchar *baseDN; + gchar *bindDN; + gchar *bindPass; + gchar *searchCriteria; + gchar *searchValue; + gint entriesRead; + gint maxEntries; + gint timeOut; + gboolean newSearch; + AddressCache *addressCache; + /* ItemFolder *rootFolder; */ + gboolean accessFlag; + gint retVal; + pthread_t *thread; + gboolean busyFlag; + void (*callBack)( void * ); + guint idleId; +}; + +/* Function prototypes */ +SyldapServer *syldap_create ( void ); +void syldap_set_name ( SyldapServer* ldapServer, const gchar *value ); +void syldap_set_host ( SyldapServer* ldapServer, const gchar *value ); +void syldap_set_port ( SyldapServer* ldapServer, const gint value ); +void syldap_set_base_dn ( SyldapServer* ldapServer, const gchar *value ); +void syldap_set_bind_dn ( SyldapServer* ldapServer, const gchar *value ); +void syldap_set_bind_password ( SyldapServer* ldapServer, const gchar *value ); +void syldap_set_search_criteria ( SyldapServer* ldapServer, const gchar *value ); +void syldap_set_search_value ( SyldapServer* ldapServer, const gchar *value ); +void syldap_set_max_entries ( SyldapServer* ldapServer, const gint value ); +void syldap_set_timeout ( SyldapServer* ldapServer, const gint value ); +void syldap_set_callback ( SyldapServer *ldapServer, void *func ); +void syldap_set_accessed ( SyldapServer *ldapServer, const gboolean value ); +void syldap_force_refresh ( SyldapServer *ldapServer ); +void syldap_free ( SyldapServer *ldapServer ); +gint syldap_get_status ( SyldapServer *ldapServer ); +gboolean syldap_get_accessed ( SyldapServer *ldapServer ); +gchar *syldap_get_name ( SyldapServer *ldapServer ); + +void syldap_print_data ( SyldapServer *ldapServer, FILE *stream ); +gboolean syldap_check_search ( SyldapServer *ldapServer ); +gint syldap_read_data ( SyldapServer *ldapServer ); +gint syldap_read_data_th ( SyldapServer *ldapServer ); +void syldap_cancel_read ( SyldapServer *ldapServer ); + +/* GList *syldap_get_address_list ( const SyldapServer *ldapServer ); */ +ItemFolder *syldap_get_root_folder ( SyldapServer *ldapServer ); +GList *syldap_get_list_person ( SyldapServer *ldapServer ); +GList *syldap_get_list_folder ( SyldapServer *ldapServer ); + +GList *syldap_read_basedn_s ( const gchar *host, const gint port, const gchar *bindDN, + const gchar *bindPW, const gint tov ); +GList *syldap_read_basedn ( SyldapServer *ldapServer ); +gboolean syldap_test_connect_s ( const gchar *host, const gint port ); +gboolean syldap_test_connect ( SyldapServer *ldapServer ); +/* gboolean syldap_test_ldap_lib ( void ); */ + +#endif /* USE_LDAP */ + +#endif /* __SYLDAP_H__ */ diff --git a/src/sylpheed-marshal.c b/src/sylpheed-marshal.c new file mode 100644 index 0000000..630bfc8 --- /dev/null +++ b/src/sylpheed-marshal.c @@ -0,0 +1,88 @@ + +#include + + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_char (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* VOID:POINTER (sylpheed-marshal.list:1) */ + +/* VOID:INT,POINTER (sylpheed-marshal.list:2) */ +void +sylpheed_marshal_VOID__INT_POINTER (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__INT_POINTER) (gpointer data1, + gint arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__INT_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__INT_POINTER) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + data2); +} + diff --git a/src/sylpheed-marshal.h b/src/sylpheed-marshal.h new file mode 100644 index 0000000..36edaf4 --- /dev/null +++ b/src/sylpheed-marshal.h @@ -0,0 +1,23 @@ + +#ifndef __sylpheed_marshal_MARSHAL_H__ +#define __sylpheed_marshal_MARSHAL_H__ + +#include + +G_BEGIN_DECLS + +/* VOID:POINTER (sylpheed-marshal.list:1) */ +#define sylpheed_marshal_VOID__POINTER g_cclosure_marshal_VOID__POINTER + +/* VOID:INT,POINTER (sylpheed-marshal.list:2) */ +extern void sylpheed_marshal_VOID__INT_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +G_END_DECLS + +#endif /* __sylpheed_marshal_MARSHAL_H__ */ + diff --git a/src/sylpheed-marshal.list b/src/sylpheed-marshal.list new file mode 100644 index 0000000..d0f15c7 --- /dev/null +++ b/src/sylpheed-marshal.list @@ -0,0 +1,2 @@ +VOID:POINTER +VOID:INT,POINTER diff --git a/src/sylpheed.exe.manifest b/src/sylpheed.exe.manifest new file mode 100644 index 0000000..1d0f12b --- /dev/null +++ b/src/sylpheed.exe.manifest @@ -0,0 +1,12 @@ + + + + + + true + + + diff --git a/src/sylpheed.rc.in b/src/sylpheed.rc.in new file mode 100644 index 0000000..cdef5d8 --- /dev/null +++ b/src/sylpheed.rc.in @@ -0,0 +1,34 @@ +1 VERSIONINFO + FILEVERSION @MAJOR_VERSION@,@MINOR_VERSION@,@MICRO_VERSION@,@BUILD_REVISION@ + PRODUCTVERSION @MAJOR_VERSION@,@MINOR_VERSION@,@MICRO_VERSION@,0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x20L + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Hiroyuki Yamamoto\0" + VALUE "FileDescription", "Sylpheed\0" + VALUE "FileVersion", "@MAJOR_VERSION@.@MINOR_VERSION@.@MICRO_VERSION@.@BUILD_REVISION@\0" + VALUE "InternalName", "sylpheed\0" + VALUE "LegalCopyright", "Copyright (C) 1999-2017 Hiroyuki Yamamoto\0" + VALUE "OriginalFilename", "sylpheed.exe\0" + VALUE "ProductName", "Sylpheed\0" + VALUE "ProductVersion", "@VERSION@\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +SYLPHEED ICON icons/sylpheed.ico + +#include + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST sylpheed.exe.manifest diff --git a/src/template.c b/src/template.c new file mode 100644 index 0000000..f20750d --- /dev/null +++ b/src/template.c @@ -0,0 +1,245 @@ +/* + * Sylpheed templates subsystem + * Copyright (C) 2001 Alexander Barinov + * Copyright (C) 2001-2006 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "defs.h" + +#include +#include +#include +#include +#include + +#include "main.h" +#include "template.h" +#include "utils.h" + +static GSList *template_list; + +static Template *template_load(gchar *filename, guint tmplid) +{ + Template *tmpl; + FILE *fp; + gchar buf[BUFFSIZE]; + + if ((fp = g_fopen(filename, "rb")) == NULL) { + FILE_OP_ERROR(filename, "fopen"); + return NULL; + } + + tmpl = g_new(Template, 1); + tmpl->tmplid = tmplid; + tmpl->name = NULL; + tmpl->to = NULL; + tmpl->cc = NULL; + tmpl->bcc = NULL; + tmpl->replyto = NULL; + tmpl->subject = NULL; + tmpl->value = NULL; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (buf[0] == '\n') + break; + else if (!g_ascii_strncasecmp(buf, "Name:", 5)) + tmpl->name = g_strdup(g_strstrip(buf + 5)); + else if (!g_ascii_strncasecmp(buf, "To:", 3)) + tmpl->to = g_strdup(g_strstrip(buf + 3)); + else if (!g_ascii_strncasecmp(buf, "Cc:", 3)) + tmpl->cc = g_strdup(g_strstrip(buf + 3)); + else if (!g_ascii_strncasecmp(buf, "Bcc:", 3)) + tmpl->bcc = g_strdup(g_strstrip(buf + 4)); + else if (!g_ascii_strncasecmp(buf, "Reply-To:", 9)) + tmpl->replyto = g_strdup(g_strstrip(buf + 9)); + else if (!g_ascii_strncasecmp(buf, "Subject:", 8)) + tmpl->subject = g_strdup(g_strstrip(buf + 8)); + } + + if (!tmpl->name) { + g_warning("wrong template format\n"); + template_free(tmpl); + fclose(fp); + return NULL; + } + + tmpl->value = file_read_stream_to_str(fp); + if (!tmpl->value) { + g_warning("cannot read template body\n"); + template_free(tmpl); + fclose(fp); + return NULL; + } + fclose(fp); + + return tmpl; +} + +void template_free(Template *tmpl) +{ + g_free(tmpl->name); + g_free(tmpl->to); + g_free(tmpl->cc); + g_free(tmpl->subject); + g_free(tmpl->value); + g_free(tmpl); +} + +void template_clear_config(GSList *tmpl_list) +{ + GSList *cur; + Template *tmpl; + + for (cur = tmpl_list; cur != NULL; cur = cur->next) { + tmpl = (Template *)cur->data; + template_free(tmpl); + } + g_slist_free(tmpl_list); +} + +static gint template_compare_id(gconstpointer a, gconstpointer b) +{ + const Template *ta, *tb; + + ta = a; + tb = b; + return (ta->tmplid - tb->tmplid); +} + +GSList *template_read_config(void) +{ + const gchar *path; + gchar *filename; + GDir *dir; + const gchar *dir_name; + GStatBuf s; + Template *tmpl; + guint tmplid; + GSList *tmpl_list = NULL; + + path = get_template_dir(); + debug_print("%s:%d reading templates dir %s\n", + __FILE__, __LINE__, path); + + if (!is_dir_exist(path)) { + if (make_dir(path) < 0) + return NULL; + } + + if ((dir = g_dir_open(path, 0, NULL)) == NULL) { + g_warning("failed to open directory: %s\n", path); + return NULL; + } + + while ((dir_name = g_dir_read_name(dir)) != NULL) { + tmplid = atoi(dir_name); + if (tmplid <= 0) { + continue; + } + + filename = g_strconcat(path, G_DIR_SEPARATOR_S, + dir_name, NULL); + + if (g_stat(filename, &s) != 0 || !S_ISREG(s.st_mode) ) { + debug_print("%s:%d %s is not an ordinary file\n", + __FILE__, __LINE__, filename); + g_free(filename); + continue; + } + + tmpl = template_load(filename, tmplid); + if (tmpl) + tmpl_list = g_slist_insert_sorted(tmpl_list, tmpl, + template_compare_id); + + g_free(filename); + } + + g_dir_close(dir); + + return tmpl_list; +} + +void template_write_config(GSList *tmpl_list) +{ + const gchar *path; + GSList *cur; + Template *tmpl; + FILE *fp; + + debug_print("%s:%d writing templates\n", __FILE__, __LINE__); + + path = get_template_dir(); + + if (!is_dir_exist(path)) { + if (is_file_exist(path)) { + g_warning(_("file %s already exists\n"), path); + return; + } + if (make_dir(path) < 0) + return; + } + + remove_all_files(path); + + for (cur = tmpl_list; cur != NULL; cur = cur->next) { + gchar *filename; + + tmpl = cur->data; + + filename = g_strconcat(path, G_DIR_SEPARATOR_S, + itos(tmpl->tmplid), NULL); + + if ((fp = g_fopen(filename, "wb")) == NULL) { + FILE_OP_ERROR(filename, "fopen"); + g_free(filename); + return; + } + + fprintf(fp, "Name: %s\n", tmpl->name); + if (tmpl->to && *tmpl->to != '\0') + fprintf(fp, "To: %s\n", tmpl->to); + if (tmpl->cc && *tmpl->cc != '\0') + fprintf(fp, "Cc: %s\n", tmpl->cc); + if (tmpl->bcc && *tmpl->bcc != '\0') + fprintf(fp, "Bcc: %s\n", tmpl->bcc); + if (tmpl->replyto && *tmpl->replyto != '\0') + fprintf(fp, "Reply-To: %s\n", tmpl->replyto); + if (tmpl->subject && *tmpl->subject != '\0') + fprintf(fp, "Subject: %s\n", tmpl->subject); + fputs("\n", fp); + fwrite(tmpl->value, sizeof(gchar) * strlen(tmpl->value), 1, fp); + + fclose(fp); + g_free(filename); + } +} + +GSList *template_get_config(void) +{ + if (!template_list) + template_list = template_read_config(); + + return template_list; +} + +void template_set_config(GSList *tmpl_list) +{ + template_clear_config(template_list); + template_write_config(tmpl_list); + template_list = tmpl_list; +} diff --git a/src/template.h b/src/template.h new file mode 100644 index 0000000..a1a416b --- /dev/null +++ b/src/template.h @@ -0,0 +1,49 @@ +/* + * Sylpheed templates subsystem + * Copyright (C) 2001 Alexander Barinov + * Copyright (C) 2001-2006 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __TEMPLATE_H__ +#define __TEMPLATE_H__ + +#include + +typedef struct _Template Template; + +struct _Template { + guint tmplid; + + gchar *name; + gchar *to; + gchar *cc; + gchar *bcc; + gchar *replyto; + gchar *subject; + gchar *value; +}; + +void template_free (Template *tmpl); +void template_clear_config (GSList *tmpl_list); + +GSList *template_read_config (void); +void template_write_config (GSList *tmpl_list); + +GSList *template_get_config (void); +void template_set_config (GSList *tmpl_list); + +#endif /* __TEMPLATE_H__ */ diff --git a/src/textview.c b/src/textview.c new file mode 100644 index 0000000..e55eea9 --- /dev/null +++ b/src/textview.c @@ -0,0 +1,2765 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2013 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "summaryview.h" +#include "imageview.h" +#include "procheader.h" +#include "prefs_common.h" +#include "codeconv.h" +#include "statusbar.h" +#include "utils.h" +#include "gtkutils.h" +#include "procmime.h" +#include "account.h" +#include "html.h" +#include "compose.h" +#include "displayheader.h" +#include "filesel.h" +#include "alertpanel.h" +#include "menu.h" +#include "plugin.h" + +typedef struct _RemoteURI RemoteURI; + +struct _RemoteURI +{ + gchar *uri; + + gchar *filename; + + guint start; + guint end; +}; + +static GdkColor quote_colors[3] = { + {(gulong)0, (gushort)0, (gushort)0, (gushort)0}, + {(gulong)0, (gushort)0, (gushort)0, (gushort)0}, + {(gulong)0, (gushort)0, (gushort)0, (gushort)0} +}; + +static GdkColor uri_color = { + (gulong)0, + (gushort)0, + (gushort)0, + (gushort)0 +}; + +static GdkColor emphasis_color = { + (gulong)0, + (gushort)0, + (gushort)0, + (gushort)0xcfff +}; + +static GdkColor error_color = { + (gulong)0, + (gushort)0xefff, + (gushort)0, + (gushort)0 +}; + +#if USE_GPGME +static GdkColor good_sig_color = { + (gulong)0, + (gushort)0, + (gushort)0xbfff, + (gushort)0 +}; + +static GdkColor untrusted_sig_color = { + (gulong)0, + (gushort)0xefff, + (gushort)0, + (gushort)0 +}; + +static GdkColor nocheck_sig_color = { + (gulong)0, + (gushort)0, + (gushort)0, + (gushort)0xcfff +}; + +static GdkColor bad_sig_color = { + (gulong)0, + (gushort)0xefff, + (gushort)0, + (gushort)0 +}; +#endif + +#define STATUSBAR_PUSH(textview, str) \ +{ \ + gtk_statusbar_push(GTK_STATUSBAR(textview->messageview->statusbar), \ + textview->messageview->statusbar_cid, str); \ +} + +#define STATUSBAR_POP(textview) \ +{ \ + gtk_statusbar_pop(GTK_STATUSBAR(textview->messageview->statusbar), \ + textview->messageview->statusbar_cid); \ +} + +static GdkCursor *hand_cursor = NULL; +static GdkCursor *regular_cursor = NULL; + + +static void textview_part_menu_create (TextView *textview); + +static void textview_add_part (TextView *textview, + MimeInfo *mimeinfo, + FILE *fp); +#if USE_GPGME +static void textview_add_sig_part (TextView *textview, + MimeInfo *mimeinfo); +#endif +static void textview_add_parts (TextView *textview, + MimeInfo *mimeinfo, + FILE *fp); +static void textview_write_body (TextView *textview, + MimeInfo *mimeinfo, + FILE *fp, + const gchar *charset); +static void textview_show_html (TextView *textview, + FILE *fp, + CodeConverter *conv); + +static void textview_write_line (TextView *textview, + const gchar *str, + CodeConverter *conv); +static void textview_write_link (TextView *textview, + const gchar *str, + const gchar *uri, + CodeConverter *conv); + +static GPtrArray *textview_scan_header (TextView *textview, + FILE *fp, + const gchar *encoding); +static void textview_show_header (TextView *textview, + GPtrArray *headers); + +static void textview_insert_border (TextView *textview, + GtkTextIter *iter, + gint padding); +static void textview_insert_pad (TextView *textview, + GtkTextIter *iter, + gint padding); + +static gboolean textview_key_pressed (GtkWidget *widget, + GdkEventKey *event, + TextView *textview); +static gboolean textview_event_after (GtkWidget *widget, + GdkEvent *event, + TextView *textview); +static gboolean textview_motion_notify (GtkWidget *widget, + GdkEventMotion *event, + TextView *textview); +static gboolean textview_leave_notify (GtkWidget *widget, + GdkEventCrossing *event, + TextView *textview); +static gboolean textview_visibility_notify (GtkWidget *widget, + GdkEventVisibility *event, + TextView *textview); + +static void textview_populate_popup (GtkWidget *widget, + GtkMenu *menu, + TextView *textview); +static void textview_popup_menu_activate_open_uri_cb + (GtkMenuItem *menuitem, + gpointer data); +static void textview_popup_menu_activate_reply_cb + (GtkMenuItem *menuitem, + gpointer data); +static void textview_popup_menu_activate_add_address_cb + (GtkMenuItem *menuitem, + gpointer data); +static void textview_popup_menu_activate_copy_cb(GtkMenuItem *menuitem, + gpointer data); +static void textview_popup_menu_activate_image_cb + (GtkMenuItem *menuitem, + gpointer data); + +static void textview_adj_value_changed (GtkAdjustment *adj, + gpointer data); + +static void textview_smooth_scroll_do (TextView *textview, + gfloat old_value, + gfloat last_value, + gint step); +static void textview_smooth_scroll_one_line (TextView *textview, + gboolean up); +static gboolean textview_smooth_scroll_page (TextView *textview, + gboolean up); + +static gboolean textview_get_link_tag_bounds (TextView *textview, + GtkTextIter *iter, + GtkTextIter *start, + GtkTextIter *end); +static RemoteURI *textview_get_uri (TextView *textview, + GtkTextIter *start, + GtkTextIter *end); +static void textview_show_uri (TextView *textview, + GtkTextIter *start, + GtkTextIter *end); +static void textview_set_cursor (TextView *textview, + GtkTextView *text, + gint x, + gint y); + +static gboolean textview_uri_security_check (TextView *textview, + RemoteURI *uri); +static void textview_uri_list_remove_all (GSList *uri_list); +static void textview_uri_list_update_offsets (TextView *textview, + gint start, + gint add); + + +TextView *textview_create(void) +{ + TextView *textview; + GtkWidget *vbox; + GtkWidget *scrolledwin; + GtkWidget *text; + GtkTextBuffer *buffer; + GtkClipboard *clipboard; + + debug_print(_("Creating text view...\n")); + textview = g_new0(TextView, 1); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_ETCHED_IN); + gtk_widget_set_size_request + (scrolledwin, prefs_common.mainview_width, -1); + + text = gtk_text_view_new(); + gtk_widget_add_events(text, GDK_LEAVE_NOTIFY_MASK); + gtk_widget_show(text); + gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6); + gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); + clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY); + gtk_text_buffer_add_selection_clipboard(buffer, clipboard); + + gtk_widget_ref(scrolledwin); + + gtk_container_add(GTK_CONTAINER(scrolledwin), text); + + g_signal_connect(G_OBJECT(text), "key-press-event", + G_CALLBACK(textview_key_pressed), textview); + g_signal_connect(G_OBJECT(text), "event-after", + G_CALLBACK(textview_event_after), textview); + g_signal_connect(G_OBJECT(text), "motion-notify-event", + G_CALLBACK(textview_motion_notify), textview); + g_signal_connect(G_OBJECT(text), "leave-notify-event", + G_CALLBACK(textview_leave_notify), textview); + g_signal_connect(G_OBJECT(text), "visibility-notify-event", + G_CALLBACK(textview_visibility_notify), textview); + g_signal_connect(G_OBJECT(text), "populate-popup", + G_CALLBACK(textview_populate_popup), textview); + + g_signal_connect(G_OBJECT(GTK_TEXT_VIEW(text)->vadjustment), + "value-changed", + G_CALLBACK(textview_adj_value_changed), textview); + + gtk_widget_show(scrolledwin); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0); + + gtk_widget_show(vbox); + + textview->vbox = vbox; + textview->scrolledwin = scrolledwin; + textview->text = text; + textview->uri_list = NULL; + textview->body_pos = 0; + textview->show_all_headers = FALSE; + + textview_part_menu_create(textview); + + return textview; +} + +static void textview_create_tags(TextView *textview) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer; + static PangoFontDescription *font_desc; + GtkTextIter iter; + GtkTextMark *mark; + + if (!font_desc) + font_desc = gtkut_get_default_font_desc(); + + buffer = gtk_text_view_get_buffer(text); + + gtk_text_buffer_get_end_iter(buffer, &iter); + mark = gtk_text_buffer_create_mark(buffer, "attach-file-pos", + &iter, TRUE); + + gtk_text_buffer_create_tag(buffer, "header", + "pixels-above-lines", 1, + "pixels-above-lines-set", TRUE, + "pixels-below-lines", 0, + "pixels-below-lines-set", TRUE, + "font-desc", font_desc, + NULL); + gtk_text_buffer_create_tag(buffer, "header_title", + "weight", PANGO_WEIGHT_BOLD, + NULL); + + gtk_text_buffer_create_tag(buffer, "mimepart", + "pixels-above-lines", 1, + "pixels-above-lines-set", TRUE, + "pixels-below-lines", 1, + "pixels-below-lines-set", TRUE, + "font-desc", font_desc, + NULL); + + textview->quote0_tag = + gtk_text_buffer_create_tag(buffer, "quote0", + "foreground-gdk", "e_colors[0], + NULL); + textview->quote1_tag = + gtk_text_buffer_create_tag(buffer, "quote1", + "foreground-gdk", "e_colors[1], + NULL); + textview->quote2_tag = + gtk_text_buffer_create_tag(buffer, "quote2", + "foreground-gdk", "e_colors[2], + NULL); + textview->link_tag = + gtk_text_buffer_create_tag(buffer, "link", + "foreground-gdk", &uri_color, + NULL); + textview->hover_link_tag = + gtk_text_buffer_create_tag(buffer, "hover-link", + "foreground-gdk", &uri_color, + "underline", PANGO_UNDERLINE_SINGLE, + NULL); + + gtk_text_buffer_create_tag(buffer, "emphasis", + "foreground-gdk", &emphasis_color, + NULL); + gtk_text_buffer_create_tag(buffer, "error", + "foreground-gdk", &error_color, + NULL); +#if USE_GPGME + gtk_text_buffer_create_tag(buffer, "good-signature", + "foreground-gdk", &good_sig_color, + NULL); + gtk_text_buffer_create_tag(buffer, "untrusted-signature", + "foreground-gdk", &untrusted_sig_color, + NULL); + gtk_text_buffer_create_tag(buffer, "bad-signature", + "foreground-gdk", &bad_sig_color, + NULL); + gtk_text_buffer_create_tag(buffer, "nocheck-signature", + "foreground-gdk", &nocheck_sig_color, + NULL); +#endif /* USE_GPGME */ +} + +void textview_init(TextView *textview) +{ + if (!hand_cursor) + hand_cursor = gdk_cursor_new(GDK_HAND2); + if (!regular_cursor) + regular_cursor = gdk_cursor_new(GDK_XTERM); + + textview_create_tags(textview); + textview_reflect_prefs(textview); + textview_set_all_headers(textview, FALSE); + textview_set_font(textview, NULL); +} + +static void textview_update_message_colors(void) +{ + GdkColor black = {0, 0, 0, 0}; + + if (prefs_common.enable_color) { + /* grab the quote colors, converting from an int to a + GdkColor */ + gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col, + "e_colors[0]); + gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col, + "e_colors[1]); + gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col, + "e_colors[2]); + gtkut_convert_int_to_gdk_color(prefs_common.uri_col, + &uri_color); + } else { + quote_colors[0] = quote_colors[1] = quote_colors[2] = + uri_color = emphasis_color = black; + } +} + +static void textview_update_tags(TextView *textview) +{ + g_object_set(textview->quote0_tag, "foreground-gdk", "e_colors[0], + NULL); + g_object_set(textview->quote1_tag, "foreground-gdk", "e_colors[1], + NULL); + g_object_set(textview->quote2_tag, "foreground-gdk", "e_colors[2], + NULL); + g_object_set(textview->link_tag, "foreground-gdk", &uri_color, NULL); + g_object_set(textview->hover_link_tag, "foreground-gdk", &uri_color, + NULL); +} + +void textview_reflect_prefs(TextView *textview) +{ + textview_update_message_colors(); + textview_update_tags(textview); + gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textview->text), + prefs_common.textview_cursor_visible); +} + +static const gchar *textview_get_src_encoding(TextView *textview, + MimeInfo *mimeinfo) +{ + const gchar *charset; + + if (textview->messageview->forced_charset) + charset = textview->messageview->forced_charset; + else if (!textview->messageview->new_window && + prefs_common.force_charset) + charset = prefs_common.force_charset; + else if (mimeinfo->charset) + charset = mimeinfo->charset; + else if (prefs_common.default_encoding) + charset = prefs_common.default_encoding; + else + charset = NULL; + + return charset; +} + +void textview_show_message(TextView *textview, MimeInfo *mimeinfo, + const gchar *file) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer; + GtkTextMark *mark; + GtkTextIter iter; + FILE *fp; + const gchar *charset; + GPtrArray *headers = NULL; + + buffer = gtk_text_view_get_buffer(text); + + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + return; + } + + debug_print("textview_show_message: displaying: %s\n", file); + + charset = textview_get_src_encoding(textview, mimeinfo); + textview_set_font(textview, charset); + textview_clear(textview); + + if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0) perror("fseek"); + headers = textview_scan_header(textview, fp, charset); + if (headers) { + textview_show_header(textview, headers); + procheader_header_array_destroy(headers); + + gtk_text_buffer_get_end_iter(buffer, &iter); + textview->body_pos = gtk_text_iter_get_offset(&iter); + } else { + gtk_text_buffer_get_end_iter(buffer, &iter); + mark = gtk_text_buffer_get_mark(buffer, "attach-file-pos"); + gtk_text_buffer_move_mark(buffer, mark, &iter); + g_object_set_data(G_OBJECT(mark), "attach-file-count", GINT_TO_POINTER(0)); + } + +#if USE_GPGME + if (textview->messageview->msginfo->encinfo && + textview->messageview->msginfo->encinfo->decryption_failed) { + gtk_text_buffer_get_end_iter(buffer, &iter); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + gtk_text_buffer_insert_with_tags_by_name + (buffer, &iter, _("This message is encrypted, but its decryption failed.\n"), + -1, "error", "mimepart", NULL); + } +#endif + + textview_add_parts(textview, mimeinfo, fp); + +#if USE_GPGME + if (textview->messageview->msginfo->encinfo && + textview->messageview->msginfo->encinfo->sigstatus) + textview_add_sig_part(textview, NULL); +#endif + + fclose(fp); + + textview_set_position(textview, 0); + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_view_scroll_mark_onscreen(text, mark); +} + +void textview_show_part(TextView *textview, MimeInfo *mimeinfo, FILE *fp) +{ + gchar buf[BUFFSIZE]; + const gchar *boundary = NULL; + gint boundary_len = 0; + const gchar *charset; + GPtrArray *headers = NULL; + gboolean is_rfc822_part = FALSE; + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer; + GtkTextIter iter; + GtkTextMark *mark; + + g_return_if_fail(mimeinfo != NULL); + g_return_if_fail(fp != NULL); + + if (mimeinfo->mime_type == MIME_MULTIPART) { + textview_clear(textview); + textview_add_parts(textview, mimeinfo, fp); + return; + } + + if (mimeinfo->parent && mimeinfo->parent->boundary) { + boundary = mimeinfo->parent->boundary; + boundary_len = strlen(boundary); + } + + charset = textview_get_src_encoding(textview, mimeinfo); + + if (!boundary && mimeinfo->mime_type == MIME_TEXT) { + if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0) + perror("fseek"); + headers = textview_scan_header(textview, fp, charset); + } else { + if (mimeinfo->mime_type == MIME_TEXT && mimeinfo->parent) { + glong fpos; + MimeInfo *parent = mimeinfo->parent; + + while (parent->parent) { + if (parent->main && + parent->main->mime_type == + MIME_MESSAGE_RFC822) + break; + parent = parent->parent; + } + + if ((fpos = ftell(fp)) < 0) + perror("ftell"); + else if (fseek(fp, parent->fpos, SEEK_SET) < 0) + perror("fseek"); + else { + headers = textview_scan_header + (textview, fp, charset); + if (fseek(fp, fpos, SEEK_SET) < 0) + perror("fseek"); + } + } + /* skip MIME part headers */ + while (fgets(buf, sizeof(buf), fp) != NULL) + if (buf[0] == '\r' || buf[0] == '\n') break; + } + + /* display attached RFC822 single text message */ + if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) { + if (headers) procheader_header_array_destroy(headers); + if (!mimeinfo->sub) { + textview_clear(textview); + return; + } + headers = textview_scan_header(textview, fp, charset); + mimeinfo = mimeinfo->sub; + is_rfc822_part = TRUE; + } + + textview_set_font(textview, charset); + textview_clear(textview); + + buffer = gtk_text_view_get_buffer(text); + + if (headers) { + textview_show_header(textview, headers); + procheader_header_array_destroy(headers); + + gtk_text_buffer_get_end_iter(buffer, &iter); + textview->body_pos = gtk_text_iter_get_offset(&iter); + if (!mimeinfo->main) + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } else { + gtk_text_buffer_get_end_iter(buffer, &iter); + mark = gtk_text_buffer_get_mark(buffer, "attach-file-pos"); + gtk_text_buffer_move_mark(buffer, mark, &iter); + g_object_set_data(G_OBJECT(mark), "attach-file-count", GINT_TO_POINTER(0)); + } + + if (mimeinfo->mime_type == MIME_MULTIPART || is_rfc822_part) + textview_add_parts(textview, mimeinfo, fp); + else + textview_write_body(textview, mimeinfo, fp, charset); + + textview_set_position(textview, 0); + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_view_scroll_mark_onscreen(text, mark); +} + +enum { + PART_MENU_NONE, + PART_MENU_OPEN, + PART_MENU_OPEN_WITH, + PART_MENU_SAVE_AS, + PART_MENU_PRINT, + PART_MENU_COPY_FILENAME +}; + +static void part_menu_button_position(GtkMenu *menu, gint *x, gint *y, + gboolean *push_in, + gpointer user_data) +{ + GtkWidget *widget; + GtkRequisition requisition; + gint button_xpos, button_ypos; + gint xpos, ypos; + gint width, height; + gint scr_width, scr_height; + + g_return_if_fail(x != NULL && y != NULL); + + widget = GTK_WIDGET(user_data); + gtk_widget_get_child_requisition(GTK_WIDGET(menu), &requisition); + width = requisition.width; + height = requisition.height; + gdk_window_get_origin(widget->window, &button_xpos, &button_ypos); + + xpos = button_xpos; + ypos = button_ypos + widget->requisition.height; + + scr_width = gdk_screen_width(); + scr_height = gdk_screen_height(); + + if (xpos + width > scr_width) + xpos -= (xpos + width) - scr_width; + if (ypos + height > scr_height) + ypos -= widget->requisition.height + height; + if (xpos < 0) + xpos = 0; + if (ypos < 0) + ypos = 0; + + *x = xpos; + *y = ypos; +} + +static gboolean textview_part_widget_entered(GtkWidget *widget, + GdkEventCrossing *event, + gpointer data) +{ + gtk_widget_set_state(widget, GTK_STATE_PRELIGHT); + return FALSE; +} + +static gboolean textview_part_widget_left(GtkWidget *widget, + GdkEventCrossing *event, + gpointer data) +{ + gtk_widget_set_state(widget, GTK_STATE_NORMAL); + return FALSE; +} + +static gboolean textview_part_widget_button_pressed(GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + TextView *textview = (TextView *)data; + GtkWidget *menu; + MimeInfo *mimeinfo; + GList *cur; + + if (!event) + return FALSE; + + menu = textview->popup_menu; + mimeinfo = g_object_get_data(G_OBJECT(widget), "mimeinfo"); + + for (cur = GTK_MENU_SHELL(menu)->children; cur != NULL; cur = cur->next) { + GtkWidget *menuitem = GTK_WIDGET(cur->data); + gint type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID)); + if (type == PART_MENU_PRINT) { + if (mimeinfo->mime_type == MIME_TEXT || + mimeinfo->mime_type == MIME_TEXT_HTML || + mimeinfo->mime_type == MIME_MESSAGE_RFC822) + gtk_widget_set_sensitive(menuitem, TRUE); + else + gtk_widget_set_sensitive(menuitem, FALSE); + } else if (type == PART_MENU_COPY_FILENAME) { + if (mimeinfo->filename || mimeinfo->name) + gtk_widget_set_sensitive(menuitem, TRUE); + else + gtk_widget_set_sensitive(menuitem, FALSE); + } + } + + g_object_set_data(G_OBJECT(menu), "part-widget", widget); + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, part_menu_button_position, widget, event->button, event->time); + + return TRUE; +} + +static gboolean textview_part_widget_exposed(GtkWidget *widget, + GdkEventExpose *event, + gpointer data) +{ + GdkDrawable *drawable; + GdkGC *gc; + gint w, h; + + w = widget->allocation.width - 1; + h = widget->allocation.height - 1; + if (w <= 0 || h <= 0) + return FALSE; + + drawable = GDK_DRAWABLE(widget->window); + gc = widget->style->dark_gc[GTK_WIDGET_STATE(widget)]; + gdk_gc_set_clip_rectangle(gc, &event->area); + gdk_gc_set_line_attributes(gc, 1, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, + GDK_JOIN_MITER); + gdk_draw_line(drawable, gc, 1, 0, w, 0); + gdk_draw_line(drawable, gc, w, 1, w, h); + gdk_draw_line(drawable, gc, w, h, 1, h); + gdk_draw_line(drawable, gc, 0, h, 0, 1); + + return FALSE; +} + +static void textview_part_menu_activated(GtkWidget *widget, gpointer data) +{ + TextView *textview = (TextView *)data; + GtkWidget *menu; + GtkWidget *part_widget; + gint type; + MimeInfo *mimeinfo; + MimeView *mimeview = textview->messageview->mimeview; + const gchar *filename; + GtkClipboard *clipboard; + + menu = gtk_widget_get_parent(widget); + part_widget = g_object_get_data(G_OBJECT(menu), "part-widget"); + mimeinfo = g_object_get_data(G_OBJECT(part_widget), "mimeinfo"); + if (!mimeinfo) + return; + type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), MENU_VAL_ID)); + + switch (type) { + case PART_MENU_OPEN: + mimeview_launch_part(mimeview, mimeinfo); + break; + case PART_MENU_OPEN_WITH: + mimeview_open_part_with(mimeview, mimeinfo); + break; + case PART_MENU_SAVE_AS: + mimeview_save_part_as(mimeview, mimeinfo); + break; + case PART_MENU_PRINT: + mimeview_print_part(mimeview, mimeinfo); + break; + case PART_MENU_COPY_FILENAME: + filename = mimeinfo->filename ? mimeinfo->filename : + mimeinfo->name ? mimeinfo->name : NULL; + if (filename) { + clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY); + gtk_clipboard_set_text(clipboard, filename, -1); + clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + gtk_clipboard_set_text(clipboard, filename, -1); + } + break; + default: + break; + } +} + +static void textview_part_menu_closed(GtkMenuShell *menu_shell, gpointer data) +{ + g_object_set_data(G_OBJECT(menu_shell), "part-widget", NULL); +} + +static void textview_part_menu_create(TextView *textview) +{ + GtkWidget *menu; + GtkWidget *menuitem; + + if (textview->popup_menu) + return; + + menu = gtk_menu_new(); + textview->popup_menu = menu; + + MENUITEM_ADD_FROM_STOCK(menu, menuitem, GTK_STOCK_OPEN, PART_MENU_OPEN); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(textview_part_menu_activated), textview); + MENUITEM_ADD_WITH_MNEMONIC(menu, menuitem, _("Open _with..."), PART_MENU_OPEN_WITH); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(textview_part_menu_activated), textview); + MENUITEM_ADD_WITH_MNEMONIC(menu, menuitem, _("_Save as..."), PART_MENU_SAVE_AS); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(textview_part_menu_activated), textview); + MENUITEM_ADD_FROM_STOCK(menu, menuitem, GTK_STOCK_PRINT, PART_MENU_PRINT); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(textview_part_menu_activated), textview); + + MENUITEM_ADD(menu, menuitem, NULL, 0); + + MENUITEM_ADD_WITH_MNEMONIC(menu, menuitem, _("_Copy file name"), PART_MENU_COPY_FILENAME); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(textview_part_menu_activated), textview); + + g_signal_connect(G_OBJECT(menu), "selection_done", + G_CALLBACK(textview_part_menu_closed), textview); + + gtk_widget_show_all(menu); +} + +static void textview_add_part_widget(TextView *textview, GtkTextIter *iter, + MimeInfo *mimeinfo, const gchar *str) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer; + GtkTextChildAnchor *anchor; + GtkWidget *hbox; + GtkWidget *ebox; + GtkWidget *label; + GtkWidget *arrow; + GtkStyle *style; + GdkColor bg = {0, 0xe000, 0xe500, 0xffff}; + GdkColor fg = {0, 0x8000, 0x9800, 0xffff}; + GdkColor bg2 = {0, 0xc000, 0xc800, 0xffff}; + GdkColor fg2 = {0, 0x6000, 0x8000, 0xffff}; + + buffer = gtk_text_view_get_buffer(text); + anchor = gtk_text_buffer_create_child_anchor(buffer, iter); + ebox = gtk_event_box_new(); + hbox = gtk_hbox_new(FALSE, 4); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 3); + label = gtk_label_new(str); + gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0); + arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT); + gtk_box_pack_start(GTK_BOX(hbox), arrow, FALSE, FALSE, 0); + gtk_container_add(GTK_CONTAINER(ebox), hbox); + gtk_widget_show_all(ebox); + g_signal_connect(G_OBJECT(ebox), "button_press_event", + G_CALLBACK(textview_part_widget_button_pressed), + textview); + g_signal_connect(G_OBJECT(ebox), "enter_notify_event", + G_CALLBACK(textview_part_widget_entered), textview); + g_signal_connect(G_OBJECT(ebox), "leave_notify_event", + G_CALLBACK(textview_part_widget_left), textview); + g_signal_connect_after(G_OBJECT(ebox), "expose_event", + G_CALLBACK(textview_part_widget_exposed), textview); + + style = gtk_widget_get_style(label); + bg = style->bg[GTK_STATE_NORMAL]; + fg = style->fg[GTK_STATE_NORMAL]; + bg2 = style->bg[GTK_STATE_PRELIGHT]; + fg2 = style->fg[GTK_STATE_PRELIGHT]; + + gtk_widget_modify_bg(ebox, GTK_STATE_NORMAL, &bg); + gtk_widget_modify_fg(ebox, GTK_STATE_NORMAL, &fg); + gtk_widget_modify_bg(ebox, GTK_STATE_PRELIGHT, &bg2); + gtk_widget_modify_fg(ebox, GTK_STATE_PRELIGHT, &fg2); + + g_object_set_data(G_OBJECT(ebox), "mimeinfo", mimeinfo); + + gtk_text_view_add_child_at_anchor(text, ebox, anchor); + gtk_text_buffer_insert(buffer, iter, "\n", 1); +} + +static void textview_add_part(TextView *textview, MimeInfo *mimeinfo, FILE *fp) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer; + GtkTextIter iter; + gchar buf[BUFFSIZE]; + const gchar *boundary = NULL; + gint boundary_len = 0; + const gchar *charset; + GPtrArray *headers = NULL; + GtkTextMark *mark; + + g_return_if_fail(mimeinfo != NULL); + g_return_if_fail(fp != NULL); + + buffer = gtk_text_view_get_buffer(text); + gtk_text_buffer_get_end_iter(buffer, &iter); + + if (mimeinfo->mime_type == MIME_MULTIPART) return; + + if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0) { + perror("fseek"); + return; + } + + if (mimeinfo->parent && mimeinfo->parent->boundary) { + boundary = mimeinfo->parent->boundary; + boundary_len = strlen(boundary); + } + + while (fgets(buf, sizeof(buf), fp) != NULL) + if (buf[0] == '\r' || buf[0] == '\n') break; + + charset = textview_get_src_encoding(textview, mimeinfo); + + if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) { + g_snprintf(buf, sizeof(buf), "%s (%s)", + mimeinfo->content_type, + to_human_readable(mimeinfo->content_size)); + debug_print("textview_add_part: adding: %s\n", buf); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + textview_add_part_widget(textview, &iter, mimeinfo, buf); + gtk_text_buffer_get_end_iter(buffer, &iter); + headers = textview_scan_header(textview, fp, charset); + if (headers) { + textview_show_header(textview, headers); + procheader_header_array_destroy(headers); + } else { + mark = gtk_text_buffer_get_mark(buffer, "attach-file-pos"); + gtk_text_buffer_move_mark(buffer, mark, &iter); + g_object_set_data(G_OBJECT(mark), "attach-file-count", GINT_TO_POINTER(0)); + } + return; + } + +#if USE_GPGME + if (mimeinfo->parent && mimeinfo->sigstatus) { + textview_add_sig_part(textview, mimeinfo); + return; + } +#endif + + if (mimeinfo->filename || mimeinfo->name) + g_snprintf(buf, sizeof(buf), "%s %s (%s)", + mimeinfo->filename ? mimeinfo->filename : + mimeinfo->name, + mimeinfo->content_type, + to_human_readable(mimeinfo->content_size)); + else + g_snprintf(buf, sizeof(buf), "%s (%s)", + mimeinfo->content_type, + to_human_readable(mimeinfo->content_size)); + + debug_print("textview_add_part: adding: %s\n", buf); + + if (mimeinfo->mime_type != MIME_TEXT && + mimeinfo->mime_type != MIME_TEXT_HTML) { + if (mimeinfo->mime_type == MIME_IMAGE && + prefs_common.inline_image) { + GdkPixbuf *pixbuf; + GError *error = NULL; + gchar *filename; + RemoteURI *uri; + gchar *uri_str; + + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + textview_add_part_widget(textview, &iter, mimeinfo, buf); + + filename = procmime_get_tmp_file_name(mimeinfo); + if (procmime_get_part_fp(filename, fp, mimeinfo) < 0) { + g_warning("Can't get the image file."); + g_free(filename); + return; + } + + pixbuf = gdk_pixbuf_new_from_file(filename, &error); + if (error != NULL) { + g_warning("%s\n", error->message); + g_error_free(error); + } + if (!pixbuf) { + g_warning("Can't load the image."); + g_free(filename); + return; + } + + { + GdkPixbuf *rotated; + + rotated = imageview_get_rotated_pixbuf(pixbuf); + g_object_unref(pixbuf); + pixbuf = rotated; + } + if (prefs_common.resize_image) { + GdkPixbuf *scaled; + + scaled = imageview_get_resized_pixbuf + (pixbuf, textview->text, 8); + g_object_unref(pixbuf); + pixbuf = scaled; + } + + uri_str = g_filename_to_uri(filename, NULL, NULL); + if (uri_str) { + uri = g_new(RemoteURI, 1); + uri->uri = uri_str; + uri->filename = + procmime_get_part_file_name(mimeinfo); + uri->start = gtk_text_iter_get_offset(&iter); + uri->end = uri->start + 1; + textview->uri_list = + g_slist_append(textview->uri_list, uri); + } + gtk_text_buffer_insert_pixbuf(buffer, &iter, pixbuf); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + + g_object_unref(pixbuf); + g_free(filename); + } else if (prefs_common.show_attached_files_first) { + gint count; + gint prev_pos, new_pos; + + mark = gtk_text_buffer_get_mark(buffer, "attach-file-pos"); + + gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark); + prev_pos = gtk_text_iter_get_offset(&iter); + count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(mark), "attach-file-count")); + if (count == 0) { + textview_insert_pad(textview, &iter, 4); + gtk_text_buffer_move_mark(buffer, mark, &iter); + } + textview_add_part_widget(textview, &iter, mimeinfo, buf); + gtk_text_buffer_move_mark(buffer, mark, &iter); + new_pos = gtk_text_iter_get_offset(&iter); + textview_uri_list_update_offsets(textview, new_pos, new_pos - prev_pos); + g_object_set_data(G_OBJECT(mark), "attach-file-count", GINT_TO_POINTER(count + 1)); + gtk_text_buffer_get_end_iter(buffer, &iter); + } else { + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + textview_add_part_widget(textview, &iter, mimeinfo, buf); + } + } else { + /* text part */ + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + if (mimeinfo->mime_type == MIME_TEXT_HTML || + (!mimeinfo->main && mimeinfo->parent && + mimeinfo->parent->children != mimeinfo)) + textview_add_part_widget(textview, &iter, mimeinfo, buf); + textview_write_body(textview, mimeinfo, fp, charset); + } +} + +#if USE_GPGME +static void textview_add_sig_part(TextView *textview, MimeInfo *mimeinfo) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer; + GtkTextIter iter; + gchar buf[BUFFSIZE]; + const gchar *color; + const gchar *sigstatus; + const gchar *sigstatus_full; + const gchar *type; + + if (mimeinfo) { + sigstatus = mimeinfo->sigstatus; + sigstatus_full = mimeinfo->sigstatus_full; + type = mimeinfo->content_type; + } else if (textview->messageview->msginfo->encinfo) { + sigstatus = textview->messageview->msginfo->encinfo->sigstatus; + sigstatus_full = + textview->messageview->msginfo->encinfo->sigstatus_full; + type = "signature"; + } else + return; + + if (!sigstatus) + return; + + g_snprintf(buf, sizeof(buf), "\n[%s (%s)]\n", type, sigstatus); + + if (!strcmp(sigstatus, _("Good signature"))) + color = "good-signature"; + else if (!strcmp(sigstatus, _("Valid signature (untrusted key)"))) + color = "untrusted-signature"; + else if (!strcmp(sigstatus, _("BAD signature"))) + color = "bad-signature"; + else + color = "nocheck-signature"; + + buffer = gtk_text_view_get_buffer(text); + gtk_text_buffer_get_end_iter(buffer, &iter); + gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, buf, -1, + color, "mimepart", NULL); + if (sigstatus_full) + gtk_text_buffer_insert_with_tags_by_name + (buffer, &iter, sigstatus_full, -1, "mimepart", NULL); +} +#endif + +static void textview_add_parts(TextView *textview, MimeInfo *mimeinfo, FILE *fp) +{ + gint level; + + g_return_if_fail(mimeinfo != NULL); + g_return_if_fail(fp != NULL); + + level = mimeinfo->level; + + for (;;) { + if (mimeinfo->mime_type == MIME_MULTIPART && + mimeinfo->content_type && + !g_ascii_strcasecmp(mimeinfo->content_type, + "multipart/alternative")) { + MimeInfo *preferred_part = mimeinfo->children; + MimeInfo *child; + + if (prefs_common.alt_prefer_html) { + for (child = mimeinfo->children; child != NULL; child = child->next) { + if (child->mime_type == MIME_TEXT_HTML) { + preferred_part = child; + break; + } + } + } + + if (preferred_part) { + textview_add_part(textview, preferred_part, fp); + mimeinfo = preferred_part; + while (mimeinfo->next) + mimeinfo = mimeinfo->next; + } + } else { + textview_add_part(textview, mimeinfo, fp); + } + + mimeinfo = procmime_mimeinfo_next(mimeinfo); + + if (!mimeinfo || mimeinfo->level <= level) + break; + } +} + +static void textview_write_error(TextView *textview, const gchar *msg) +{ + GtkTextBuffer *buffer; + GtkTextIter iter; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text)); + gtk_text_buffer_get_end_iter(buffer, &iter); + gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, msg, -1, + "error", NULL); +} + +void textview_show_error(TextView *textview) +{ + textview_set_font(textview, NULL); + textview_clear(textview); + textview_write_error(textview, _("This message can't be displayed.\n")); +} + +static void textview_write_body(TextView *textview, MimeInfo *mimeinfo, + FILE *fp, const gchar *charset) +{ + FILE *tmpfp; + gchar buf[BUFFSIZE]; + CodeConverter *conv; + + conv = conv_code_converter_new(charset, NULL); + + tmpfp = procmime_decode_content(NULL, fp, mimeinfo); + if (tmpfp) { + if (mimeinfo->mime_type == MIME_TEXT_HTML && + prefs_common.render_html) + textview_show_html(textview, tmpfp, conv); + else + while (fgets(buf, sizeof(buf), tmpfp) != NULL) + textview_write_line(textview, buf, conv); + fclose(tmpfp); + } else { + textview_write_error + (textview, + _("The body text couldn't be displayed because " + "writing to temporary file failed.\n")); + } + + conv_code_converter_destroy(conv); +} + +static void textview_show_html(TextView *textview, FILE *fp, + CodeConverter *conv) +{ + HTMLParser *parser; + const gchar *str; + + parser = html_parser_new(fp, conv); + g_return_if_fail(parser != NULL); + + while ((str = html_parse(parser)) != NULL) { + if (parser->href != NULL) + textview_write_link(textview, str, parser->href, NULL); + else + textview_write_line(textview, str, NULL); + } + textview_write_line(textview, "\n", NULL); + + html_parser_destroy(parser); +} + +/* get_uri_part() - retrieves a URI starting from scanpos. + Returns TRUE if succesful */ +static gboolean get_uri_part(const gchar *start, const gchar *scanpos, + const gchar **bp, const gchar **ep) +{ + const gchar *ep_; + + g_return_val_if_fail(start != NULL, FALSE); + g_return_val_if_fail(scanpos != NULL, FALSE); + g_return_val_if_fail(bp != NULL, FALSE); + g_return_val_if_fail(ep != NULL, FALSE); + + *bp = scanpos; + + /* find end point of URI */ + for (ep_ = scanpos; *ep_ != '\0'; ep_++) { + if (!g_ascii_isgraph(*ep_) || + !isascii(*(const guchar *)ep_) || + strchr("()<>{}[]\"", *ep_)) + break; + } + + /* no punctuation at end of string */ + + /* FIXME: this stripping of trailing punctuations may bite with other URIs. + * should pass some URI type to this function and decide on that whether + * to perform punctuation stripping */ + +#define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=", ch)) + + for (; ep_ - 1 > scanpos + 1 && IS_REAL_PUNCT(*(ep_ - 1)); ep_--) + ; + +#undef IS_REAL_PUNCT + + *ep = ep_; + + return TRUE; +} + +static gchar *make_uri_string(const gchar *bp, const gchar *ep) +{ + return g_strndup(bp, ep - bp); +} + +static gchar *make_http_uri_string(const gchar *bp, const gchar *ep) +{ + gchar *tmp; + gchar *result; + + tmp = g_strndup(bp, ep - bp); + result = g_strconcat("http://", tmp, NULL); + g_free(tmp); + + return result; +} + +/* valid mail address characters */ +#define IS_RFC822_CHAR(ch) \ + (isascii(ch) && \ + (ch) > 32 && \ + (ch) != 127 && \ + !g_ascii_isspace(ch) && \ + !strchr("(),;<>\"", (ch))) + +/* alphabet and number within 7bit ASCII */ +#define IS_ASCII_ALNUM(ch) (isascii(ch) && g_ascii_isalnum(ch)) + +/* get_email_part() - retrieves an email address. Returns TRUE if succesful */ +static gboolean get_email_part(const gchar *start, const gchar *scanpos, + const gchar **bp, const gchar **ep) +{ + /* more complex than the uri part because we need to scan back and forward starting from + * the scan position. */ + gboolean result = FALSE; + const gchar *bp_; + const gchar *ep_; + + g_return_val_if_fail(start != NULL, FALSE); + g_return_val_if_fail(scanpos != NULL, FALSE); + g_return_val_if_fail(bp != NULL, FALSE); + g_return_val_if_fail(ep != NULL, FALSE); + + /* scan start of address */ + for (bp_ = scanpos - 1; + bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--) + ; + + /* TODO: should start with an alnum? */ + bp_++; + for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++) + ; + + if (bp_ != scanpos) { + /* scan end of address */ + for (ep_ = scanpos + 1; + *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++) + ; + + /* TODO: really should terminate with an alnum? */ + for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_); + --ep_) + ; + ep_++; + + if (ep_ > scanpos + 1) { + *ep = ep_; + *bp = bp_; + result = TRUE; + } + } + + return result; +} + +#undef IS_ASCII_ALNUM +#undef IS_RFC822_CHAR + +static gchar *make_email_string(const gchar *bp, const gchar *ep) +{ + /* returns a mailto: URI; mailto: is also used to detect the + * uri type later on in the button_pressed signal handler */ + gchar *tmp, *enc; + gchar *result; + + tmp = g_strndup(bp, ep - bp); + enc = uriencode_for_mailto(tmp); + result = g_strconcat("mailto:", enc, NULL); + g_free(enc); + g_free(tmp); + + return result; +} + +#define ADD_TXT_POS(bp_, ep_, pti_) \ +{ \ + struct txtpos *last; \ + \ + last = g_new(struct txtpos, 1); \ + last->bp = (bp_); \ + last->ep = (ep_); \ + last->pti = (pti_); \ + txtpos_list = g_slist_append(txtpos_list, last); \ +} + +/* textview_make_clickable_parts() - colorizes clickable parts */ +static void textview_make_clickable_parts(TextView *textview, + const gchar *fg_tag, + const gchar *uri_tag, + const gchar *linebuf) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer; + GtkTextIter iter; + + /* parse table - in order of priority */ + struct table { + const gchar *needle; /* token */ + + /* token search function */ + gchar *(*search) (const gchar *haystack, + const gchar *needle); + /* part parsing function */ + gboolean (*parse) (const gchar *start, + const gchar *scanpos, + const gchar **bp_, + const gchar **ep_); + /* part to URI function */ + gchar *(*build_uri) (const gchar *bp, + const gchar *ep); + }; + + static struct table parser[] = { + {"http://", strcasestr, get_uri_part, make_uri_string}, + {"https://", strcasestr, get_uri_part, make_uri_string}, + {"ftp://", strcasestr, get_uri_part, make_uri_string}, + {"www.", strcasestr, get_uri_part, make_http_uri_string}, + {"mailto:", strcasestr, get_uri_part, make_uri_string}, + {"@", strcasestr, get_email_part, make_email_string} + }; + const gint PARSE_ELEMS = sizeof parser / sizeof parser[0]; + + /* flags for search optimization */ + gboolean do_search[] = {TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}; + + gint n; + const gchar *walk, *bp, *ep; + + struct txtpos { + const gchar *bp, *ep; /* text position */ + gint pti; /* index in parse table */ + }; + GSList *txtpos_list = NULL; + + buffer = gtk_text_view_get_buffer(text); + gtk_text_buffer_get_end_iter(buffer, &iter); + + /* parse for clickable parts, and build a list of begin and + end positions */ + for (walk = linebuf, n = 0;;) { + gint last_index = PARSE_ELEMS; + const gchar *scanpos = NULL; + + /* FIXME: this looks phony. scanning for anything in the + parse table */ + for (n = 0; n < PARSE_ELEMS; n++) { + const gchar *tmp; + + if (do_search[n]) { + tmp = parser[n].search(walk, parser[n].needle); + if (tmp) { + if (scanpos == NULL || tmp < scanpos) { + scanpos = tmp; + last_index = n; + } + } else + do_search[n] = FALSE; + } + } + + if (scanpos) { + /* check if URI can be parsed */ + if (parser[last_index].parse(walk, scanpos, &bp, &ep) + && (ep - bp - 1) > strlen(parser[last_index].needle)) { + ADD_TXT_POS(bp, ep, last_index); + walk = ep; + } else + walk = scanpos + + strlen(parser[last_index].needle); + } else + break; + } + + /* colorize this line */ + if (txtpos_list) { + const gchar *normal_text = linebuf; + GSList *cur; + + /* insert URIs */ + for (cur = txtpos_list; cur != NULL; cur = cur->next) { + struct txtpos *pos = (struct txtpos *)cur->data; + RemoteURI *uri; + + uri = g_new(RemoteURI, 1); + if (pos->bp - normal_text > 0) + gtk_text_buffer_insert_with_tags_by_name + (buffer, &iter, + normal_text, + pos->bp - normal_text, + fg_tag, NULL); + uri->uri = parser[pos->pti].build_uri(pos->bp, pos->ep); + uri->filename = NULL; + uri->start = gtk_text_iter_get_offset(&iter); + gtk_text_buffer_insert_with_tags_by_name + (buffer, &iter, pos->bp, pos->ep - pos->bp, + uri_tag, fg_tag, NULL); + uri->end = gtk_text_iter_get_offset(&iter); + textview->uri_list = + g_slist_append(textview->uri_list, uri); + normal_text = pos->ep; + + g_free(pos); + } + + if (*normal_text) + gtkut_text_buffer_insert_with_tag_by_name + (buffer, &iter, normal_text, -1, fg_tag); + + g_slist_free(txtpos_list); + } else { + gtkut_text_buffer_insert_with_tag_by_name + (buffer, &iter, linebuf, -1, fg_tag); + } +} + +#undef ADD_TXT_POS + +static void textview_write_line(TextView *textview, const gchar *str, + CodeConverter *conv) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer; + GtkTextIter iter; + gchar *buf; + gchar *fg_color = NULL; + gint quotelevel = -1; + gchar quote_tag_str[10]; + + buffer = gtk_text_view_get_buffer(text); + gtk_text_buffer_get_end_iter(buffer, &iter); + + if (conv) { + buf = conv_convert(conv, str); + if (!buf) + buf = conv_utf8todisp(str, NULL); + } else + buf = g_strdup(str); + + strcrchomp(buf); + //if (prefs_common.conv_mb_alnum) conv_mb_alnum(buf); + + /* change color of quotation + >, foo>, _> ... ok, , foo bar>, foo-> ... ng + Up to 3 levels of quotations are detected, and each + level is colored using a different color. */ + if (prefs_common.enable_color && strchr(buf, '>')) { + quotelevel = get_quote_level(buf); + + /* set up the correct foreground color */ + if (quotelevel > 2) { + /* recycle colors */ + if (prefs_common.recycle_quote_colors) + quotelevel %= 3; + else + quotelevel = 2; + } + } + + if (quotelevel != -1) { + g_snprintf(quote_tag_str, sizeof(quote_tag_str), + "quote%d", quotelevel); + fg_color = quote_tag_str; + } + + if (prefs_common.enable_color) + textview_make_clickable_parts(textview, fg_color, "link", buf); + else + textview_make_clickable_parts(textview, fg_color, NULL, buf); + + g_free(buf); +} + +static void textview_write_link(TextView *textview, const gchar *str, + const gchar *uri, CodeConverter *conv) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer; + GtkTextIter iter; + gchar *buf; + gchar *bufp; + RemoteURI *r_uri; + + if (!str || *str == '\0') + return; + if (!uri) + return; + + buffer = gtk_text_view_get_buffer(text); + gtk_text_buffer_get_end_iter(buffer, &iter); + + if (conv) { + buf = conv_convert(conv, str); + if (!buf) + buf = conv_utf8todisp(str, NULL); + } else + buf = g_strdup(str); + + if (g_utf8_validate(buf, -1, NULL) == FALSE) { + g_free(buf); + return; + } + + strcrchomp(buf); + + for (bufp = buf; *bufp != '\0'; bufp = g_utf8_next_char(bufp)) { + gunichar ch; + + ch = g_utf8_get_char(bufp); + if (!g_unichar_isspace(ch)) + break; + } + if (bufp > buf) + gtk_text_buffer_insert(buffer, &iter, buf, bufp - buf); + + if (gtk_text_iter_ends_tag(&iter, textview->link_tag)) + gtk_text_buffer_insert(buffer, &iter, " ", 1); + + r_uri = g_new(RemoteURI, 1); + r_uri->uri = g_strstrip(g_strdup(uri)); + r_uri->filename = NULL; + r_uri->start = gtk_text_iter_get_offset(&iter); + gtk_text_buffer_insert_with_tags_by_name + (buffer, &iter, bufp, -1, "link", NULL); + r_uri->end = gtk_text_iter_get_offset(&iter); + textview->uri_list = g_slist_append(textview->uri_list, r_uri); + + g_free(buf); +} + +void textview_clear(TextView *textview) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer; + + buffer = gtk_text_view_get_buffer(text); + gtk_text_buffer_set_text(buffer, "", -1); + + /* workaround for the assertion failure in + gtk_text_view_validate_onscreen() */ + text->vadjustment->value = 0.0; + + STATUSBAR_POP(textview); + textview_uri_list_remove_all(textview->uri_list); + textview->uri_list = NULL; + + textview->body_pos = 0; +} + +void textview_destroy(TextView *textview) +{ + GtkTextBuffer *buffer; + GtkClipboard *clipboard; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text)); + clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY); + gtk_text_buffer_remove_selection_clipboard(buffer, clipboard); + + gtk_widget_destroy(textview->popup_menu); + + textview_uri_list_remove_all(textview->uri_list); + textview->uri_list = NULL; + + g_free(textview); +} + +void textview_set_all_headers(TextView *textview, gboolean all_headers) +{ + textview->show_all_headers = all_headers; +} + +void textview_set_font(TextView *textview, const gchar *codeset) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + + if (prefs_common.textfont) { + PangoFontDescription *font_desc; + font_desc = pango_font_description_from_string + (prefs_common.textfont); + if (font_desc) { + gtk_widget_modify_font(textview->text, font_desc); + pango_font_description_free(font_desc); + } + } + + gtk_text_view_set_pixels_above_lines(text, prefs_common.line_space - prefs_common.line_space / 2); + gtk_text_view_set_pixels_below_lines(text, prefs_common.line_space / 2); + gtk_text_view_set_pixels_inside_wrap(text, prefs_common.line_space); +} + +void textview_set_position(TextView *textview, gint pos) +{ + GtkTextBuffer *buffer; + GtkTextIter iter; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text)); + gtk_text_buffer_get_iter_at_offset(buffer, &iter, pos); + gtk_text_buffer_place_cursor(buffer, &iter); +} + +static GPtrArray *textview_scan_header(TextView *textview, FILE *fp, + const gchar *encoding) +{ + g_return_val_if_fail(fp != NULL, NULL); + + if (textview->show_all_headers) + return procheader_get_header_array_asis(fp, encoding); + + if (!prefs_common.display_header) { + gchar buf[BUFFSIZE]; + + while (fgets(buf, sizeof(buf), fp) != NULL) + if (buf[0] == '\r' || buf[0] == '\n') break; + return NULL; + } + + return procheader_get_header_array_for_display(fp, encoding); +} + +static void textview_size_allocate_cb(GtkWidget *widget, + GtkAllocation *allocation, gpointer data) +{ + GtkWidget *child = GTK_WIDGET(data); + gint new_width; + + /* g_print("textview_size_allocate_cb: (%d, %d)\n", allocation->width, allocation->height); */ + new_width = allocation->width - 14; + if (new_width < -1) + new_width = -1; + gtk_widget_set_size_request(child, new_width, 16); +} + +void textview_hline_destroy_cb(GtkObject *object, gpointer data) +{ + GtkWidget *text = GTK_WIDGET(data); + + g_signal_handlers_disconnect_by_func(text, textview_size_allocate_cb, + object); +} + +static void textview_show_header(TextView *textview, GPtrArray *headers) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer; + GtkTextIter iter; + Header *header; + gint i; + GtkTextMark *mark; + + g_return_if_fail(headers != NULL); + + buffer = gtk_text_view_get_buffer(text); + gtk_text_buffer_get_end_iter(buffer, &iter); + + for (i = 0; i < headers->len; i++) { + header = g_ptr_array_index(headers, i); + g_return_if_fail(header->name != NULL); + + gtk_text_buffer_insert_with_tags_by_name + (buffer, &iter, header->name, -1, + "header_title", "header", NULL); + gtk_text_buffer_insert_with_tags_by_name + (buffer, &iter, ":", 1, + "header_title", "header", NULL); + + if (!g_ascii_strcasecmp(header->name, "Subject") || + !g_ascii_strcasecmp(header->name, "From") || + !g_ascii_strcasecmp(header->name, "To") || + !g_ascii_strcasecmp(header->name, "Cc")) + unfold_line(header->body); + + if (prefs_common.enable_color && + (!strncmp(header->name, "X-Mailer", 8) || + !strncmp(header->name, "X-Newsreader", 12)) && + strstr(header->body, "Sylpheed") != NULL) { + gtk_text_buffer_insert_with_tags_by_name + (buffer, &iter, header->body, -1, + "header", "emphasis", NULL); + } else if (prefs_common.enable_color) { + textview_make_clickable_parts + (textview, "header", "link", header->body); + } else { + textview_make_clickable_parts + (textview, "header", NULL, header->body); + } + gtk_text_buffer_get_end_iter(buffer, &iter); + gtk_text_buffer_insert_with_tags_by_name + (buffer, &iter, "\n", 1, "header", NULL); + } + + mark = gtk_text_buffer_get_mark(buffer, "attach-file-pos"); + gtk_text_buffer_move_mark(buffer, mark, &iter); + g_object_set_data(G_OBJECT(mark), "attach-file-count", GINT_TO_POINTER(0)); + + textview_insert_border(textview, &iter, 0); +} + +static void textview_insert_border(TextView *textview, GtkTextIter *iter, + gint padding) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer; + GtkTextChildAnchor *anchor; + GtkWidget *vbox; + GtkWidget *pad; + GtkWidget *hline; + + buffer = gtk_text_view_get_buffer(text); + + anchor = gtk_text_buffer_create_child_anchor(buffer, iter); + vbox = gtk_vbox_new(FALSE, 0); + if (padding > 0) { + pad = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), pad, FALSE, FALSE, 0); + gtk_widget_set_size_request(pad, -1, padding); + } + hline = gtk_hseparator_new(); + gtk_box_pack_start(GTK_BOX(vbox), hline, FALSE, FALSE, 0); + gtk_widget_show_all(vbox); + gtk_widget_set_size_request(hline, 320, 16); + gtk_text_view_add_child_at_anchor(text, vbox, anchor); + g_signal_connect(G_OBJECT(hline), "destroy", + G_CALLBACK(textview_hline_destroy_cb), text); + g_signal_connect(G_OBJECT(text), "size-allocate", + G_CALLBACK(textview_size_allocate_cb), hline); +} + +static void textview_insert_pad(TextView *textview, GtkTextIter *iter, + gint padding) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer; + GtkTextChildAnchor *anchor; + GtkWidget *vbox; + + buffer = gtk_text_view_get_buffer(text); + + anchor = gtk_text_buffer_create_child_anchor(buffer, iter); + vbox = gtk_vbox_new(FALSE, 0); + gtk_widget_set_size_request(vbox, -1, padding); + gtk_widget_show_all(vbox); + gtk_text_view_add_child_at_anchor(text, vbox, anchor); + + gtk_text_buffer_insert(buffer, iter, "\n", 1); +} + +gboolean textview_search_string(TextView *textview, const gchar *str, + gboolean case_sens) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer; + GtkTextIter iter, match_pos; + GtkTextMark *mark; + gint len; + + g_return_val_if_fail(str != NULL, FALSE); + + buffer = gtk_text_view_get_buffer(text); + + len = g_utf8_strlen(str, -1); + g_return_val_if_fail(len >= 0, FALSE); + + gtk_text_buffer_get_selection_bounds(buffer, NULL, &iter); + + if (gtkut_text_buffer_find(buffer, &iter, str, case_sens, + &match_pos)) { + GtkTextIter end = match_pos; + + gtk_text_iter_forward_chars(&end, len); + /* place "insert" at the last character */ + gtk_text_buffer_select_range(buffer, &end, &match_pos); + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_view_scroll_to_mark(text, mark, 0.0, FALSE, 0.0, 0.0); + return TRUE; + } + + return FALSE; +} + +gboolean textview_search_string_backward(TextView *textview, const gchar *str, + gboolean case_sens) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer; + GtkTextIter iter, match_pos; + GtkTextMark *mark; + gint len; + + g_return_val_if_fail(str != NULL, FALSE); + + buffer = gtk_text_view_get_buffer(text); + + len = g_utf8_strlen(str, -1); + g_return_val_if_fail(len >= 0, FALSE); + + gtk_text_buffer_get_selection_bounds(buffer, &iter, NULL); + + if (gtkut_text_buffer_find_backward(buffer, &iter, str, case_sens, + &match_pos)) { + GtkTextIter end = match_pos; + + gtk_text_iter_forward_chars(&end, len); + gtk_text_buffer_select_range(buffer, &match_pos, &end); + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_view_scroll_to_mark(text, mark, 0.0, FALSE, 0.0, 0.0); + return TRUE; + } + + return FALSE; +} + +void textview_scroll_one_line(TextView *textview, gboolean up) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkAdjustment *vadj = text->vadjustment; + gfloat upper; + + if (prefs_common.enable_smooth_scroll) { + textview_smooth_scroll_one_line(textview, up); + return; + } + + if (!up) { + upper = vadj->upper - vadj->page_size; + if (vadj->value < upper) { + vadj->value += vadj->step_increment; + vadj->value = MIN(vadj->value, upper); + g_signal_emit_by_name(G_OBJECT(vadj), + "value_changed", 0); + } + } else { + if (vadj->value > 0.0) { + vadj->value -= vadj->step_increment; + vadj->value = MAX(vadj->value, 0.0); + g_signal_emit_by_name(G_OBJECT(vadj), + "value_changed", 0); + } + } +} + +gboolean textview_scroll_page(TextView *textview, gboolean up) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkAdjustment *vadj = text->vadjustment; + gfloat upper; + gfloat page_incr; + + if (prefs_common.enable_smooth_scroll) + return textview_smooth_scroll_page(textview, up); + + if (prefs_common.scroll_halfpage) + page_incr = vadj->page_increment / 2; + else + page_incr = vadj->page_increment; + + if (!up) { + upper = vadj->upper - vadj->page_size; + if (vadj->value < upper) { + vadj->value += page_incr; + vadj->value = MIN(vadj->value, upper); + g_signal_emit_by_name(G_OBJECT(vadj), + "value_changed", 0); + } else + return FALSE; + } else { + if (vadj->value > 0.0) { + vadj->value -= page_incr; + vadj->value = MAX(vadj->value, 0.0); + g_signal_emit_by_name(G_OBJECT(vadj), + "value_changed", 0); + } else + return FALSE; + } + + return TRUE; +} + +static void textview_smooth_scroll_do(TextView *textview, + gfloat old_value, gfloat last_value, + gint step) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkAdjustment *vadj = text->vadjustment; + gint change_value; + gboolean up; + gint i; + + if (old_value < last_value) { + change_value = last_value - old_value; + up = FALSE; + } else { + change_value = old_value - last_value; + up = TRUE; + } + + /* gdk_key_repeat_disable(); */ + + g_signal_handlers_block_by_func(vadj, textview_adj_value_changed, + textview); + + for (i = step; i <= change_value; i += step) { + vadj->value = old_value + (up ? -i : i); + g_signal_emit_by_name(G_OBJECT(vadj), "value_changed", 0); + } + + g_signal_handlers_unblock_by_func(vadj, textview_adj_value_changed, + textview); + + vadj->value = last_value; + g_signal_emit_by_name(G_OBJECT(vadj), "value_changed", 0); + + /* gdk_key_repeat_restore(); */ + + gtk_widget_queue_draw(GTK_WIDGET(text)); +} + +static void textview_smooth_scroll_one_line(TextView *textview, gboolean up) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkAdjustment *vadj = text->vadjustment; + gfloat upper; + gfloat old_value; + gfloat last_value; + + if (!up) { + upper = vadj->upper - vadj->page_size; + if (vadj->value < upper) { + old_value = vadj->value; + last_value = vadj->value + vadj->step_increment; + last_value = MIN(last_value, upper); + + textview_smooth_scroll_do(textview, old_value, + last_value, + prefs_common.scroll_step); + } + } else { + if (vadj->value > 0.0) { + old_value = vadj->value; + last_value = vadj->value - vadj->step_increment; + last_value = MAX(last_value, 0.0); + + textview_smooth_scroll_do(textview, old_value, + last_value, + prefs_common.scroll_step); + } + } +} + +static gboolean textview_smooth_scroll_page(TextView *textview, gboolean up) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkAdjustment *vadj = text->vadjustment; + gfloat upper; + gfloat page_incr; + gfloat old_value; + gfloat last_value; + + if (prefs_common.scroll_halfpage) + page_incr = vadj->page_increment / 2; + else + page_incr = vadj->page_increment; + + if (!up) { + upper = vadj->upper - vadj->page_size; + if (vadj->value < upper) { + old_value = vadj->value; + last_value = vadj->value + page_incr; + last_value = MIN(last_value, upper); + + textview_smooth_scroll_do(textview, old_value, + last_value, + prefs_common.scroll_step); + } else + return FALSE; + } else { + if (vadj->value > 0.0) { + old_value = vadj->value; + last_value = vadj->value - page_incr; + last_value = MAX(last_value, 0.0); + + textview_smooth_scroll_do(textview, old_value, + last_value, + prefs_common.scroll_step); + } else + return FALSE; + } + + return TRUE; +} + +#define KEY_PRESS_EVENT_STOP() \ + g_signal_stop_emission_by_name(G_OBJECT(widget), "key-press-event"); + +static gboolean textview_key_pressed(GtkWidget *widget, GdkEventKey *event, + TextView *textview) +{ + SummaryView *summaryview = NULL; + MessageView *messageview = textview->messageview; + + if (!event) return FALSE; + if (messageview->mainwin) + summaryview = messageview->mainwin->summaryview; + + switch (event->keyval) { + case GDK_Tab: + case GDK_Home: + case GDK_Left: + case GDK_Up: + case GDK_Right: + case GDK_Down: + case GDK_Page_Up: + case GDK_Page_Down: + case GDK_End: + case GDK_Control_L: + case GDK_Control_R: + case GDK_KP_Tab: + case GDK_KP_Home: + case GDK_KP_Left: + case GDK_KP_Up: + case GDK_KP_Right: + case GDK_KP_Down: + case GDK_KP_Page_Up: + case GDK_KP_Page_Down: + case GDK_KP_End: + break; + case GDK_space: + case GDK_KP_Space: + if (summaryview) + summary_pass_key_press_event(summaryview, event); + else + textview_scroll_page + (textview, + (event->state & + (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0); + break; + case GDK_BackSpace: + textview_scroll_page(textview, TRUE); + break; + case GDK_Return: + case GDK_KP_Enter: + textview_scroll_one_line + (textview, (event->state & + (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0); + break; + case GDK_Delete: + case GDK_KP_Delete: + if (summaryview) + summary_pass_key_press_event(summaryview, event); + break; + case GDK_Escape: + if (summaryview && textview == messageview->textview) + gtk_widget_grab_focus(summaryview->treeview); + else if (messageview->type == MVIEW_MIME && + textview == messageview->mimeview->textview) + gtk_widget_grab_focus(messageview->mimeview->treeview); + break; + case GDK_n: + case GDK_N: + case GDK_p: + case GDK_P: + case GDK_y: + case GDK_t: + case GDK_l: + if (messageview->type == MVIEW_MIME && + textview == messageview->mimeview->textview) { + KEY_PRESS_EVENT_STOP(); + mimeview_pass_key_press_event(messageview->mimeview, + event); + break; + } + /* fall through */ + default: + if (summaryview && + event->window != messageview->mainwin->window->window) { + GdkEventKey tmpev = *event; + + tmpev.window = messageview->mainwin->window->window; + KEY_PRESS_EVENT_STOP(); + gtk_widget_event(messageview->mainwin->window, + (GdkEvent *)&tmpev); + } + break; + } + + return FALSE; +} + +static gboolean textview_get_link_tag_bounds(TextView *textview, + GtkTextIter *iter, + GtkTextIter *start, + GtkTextIter *end) +{ + GSList *tags, *cur; + gboolean on_link = FALSE; + + tags = gtk_text_iter_get_tags(iter); + *start = *end = *iter; + + for (cur = tags; cur != NULL; cur = cur->next) { + GtkTextTag *tag = cur->data; + gchar *tag_name; + + g_object_get(G_OBJECT(tag), "name", &tag_name, NULL); + if (tag_name && !strcmp(tag_name, "link")) { + if (!gtk_text_iter_begins_tag(start, tag)) + gtk_text_iter_backward_to_tag_toggle + (start, tag); + if (!gtk_text_iter_ends_tag(end, tag)) + gtk_text_iter_forward_to_tag_toggle(end, tag); + on_link = TRUE; + g_free(tag_name); + break; + } + if (tag_name) + g_free(tag_name); + } + + if (tags) + g_slist_free(tags); + + return on_link; +} + +static RemoteURI *textview_get_uri(TextView *textview, GtkTextIter *start, + GtkTextIter *end) +{ + gint start_pos, end_pos; + GSList *cur; + RemoteURI *uri = NULL; + + start_pos = gtk_text_iter_get_offset(start); + end_pos = gtk_text_iter_get_offset(end); + + for (cur = textview->uri_list; cur != NULL; cur = cur->next) { + RemoteURI *uri_ = (RemoteURI *)cur->data; + + if (start_pos == uri_->start && end_pos == uri_->end) { + debug_print("uri found: (%d, %d): %s\n", + start_pos, end_pos, uri_->uri); + uri = uri_; + break; + } + } + + return uri; +} + +static gboolean textview_event_after(GtkWidget *widget, GdkEvent *event, + TextView *textview) +{ + GdkEventButton *bevent = (GdkEventButton *)event; + GtkTextBuffer *buffer; + GtkTextIter iter, start, end; + gint x, y; + gboolean on_link; + RemoteURI *uri; + + if (event->type != GDK_BUTTON_RELEASE) + return FALSE; + if (bevent->button != 1 && bevent->button != 2) + return FALSE; + + /* don't process child widget's event */ + if (gtk_text_view_get_window(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT) != bevent->window) + return FALSE; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); + + /* don't follow a link if the user has selected something */ + gtk_text_buffer_get_selection_bounds(buffer, &start, &end); + if (!gtk_text_iter_equal(&start, &end)) + return FALSE; + + gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget), + GTK_TEXT_WINDOW_TEXT, + bevent->x, bevent->y, &x, &y); + gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &iter, x, y); + on_link = textview_get_link_tag_bounds(textview, &iter, &start, &end); + if (!on_link) + return FALSE; + + uri = textview_get_uri(textview, &start, &end); + if (!uri || !uri->uri) + return FALSE; + + if (!g_ascii_strncasecmp(uri->uri, "mailto:", 7)) { + PrefsAccount *ac = NULL; + MsgInfo *msginfo = textview->messageview->msginfo; + + if (msginfo && msginfo->folder) + ac = account_find_from_item(msginfo->folder); + if (ac && ac->protocol == A_NNTP) + ac = NULL; + compose_new(ac, msginfo ? msginfo->folder : NULL, + uri->uri + 7, NULL); + } else if (uri->uri[0] == '#') { + /* don't open in-page link */ + } else if (textview_uri_security_check(textview, uri) == TRUE) + open_uri(uri->uri, prefs_common.uri_cmd); + + return FALSE; +} + +static void textview_show_uri(TextView *textview, GtkTextIter *start, + GtkTextIter *end) +{ + RemoteURI *uri; + + STATUSBAR_POP(textview); + uri = textview_get_uri(textview, start, end); + if (uri) { + STATUSBAR_PUSH(textview, uri->uri); + } +} + +static void textview_set_cursor(TextView *textview, GtkTextView *text, + gint x, gint y) +{ + GtkTextBuffer *buffer; + GtkTextIter iter; + GtkTextIter start, end; + GtkTextMark *start_mark, *end_mark; + gboolean on_link = FALSE; + + buffer = gtk_text_view_get_buffer(text); + gtk_text_view_get_iter_at_location(text, &iter, x, y); + on_link = textview_get_link_tag_bounds(textview, &iter, &start, &end); + + start_mark = gtk_text_buffer_get_mark(buffer, "hover-link-start"); + end_mark = gtk_text_buffer_get_mark(buffer, "hover-link-end"); + if (start_mark) { + GtkTextIter prev_start, prev_end; + + gtk_text_buffer_get_iter_at_mark(buffer, &prev_start, + start_mark); + gtk_text_buffer_get_iter_at_mark(buffer, &prev_end, end_mark); + if (on_link) { + if (gtk_text_iter_equal(&prev_start, &start)) + return; + } + + gtk_text_buffer_get_iter_at_mark(buffer, &prev_start, + start_mark); + gtk_text_buffer_get_iter_at_mark(buffer, &prev_end, end_mark); + gtk_text_buffer_remove_tag_by_name(buffer, "hover-link", + &prev_start, &prev_end); + gtk_text_buffer_delete_mark(buffer, start_mark); + gtk_text_buffer_delete_mark(buffer, end_mark); + } else { + if (!on_link) + return; + } + + if (on_link) { + gtk_text_buffer_create_mark + (buffer, "hover-link-start", &start, FALSE); + gtk_text_buffer_create_mark + (buffer, "hover-link-end", &end, FALSE); + gtk_text_buffer_apply_tag_by_name + (buffer, "hover-link", &start, &end); + gdk_window_set_cursor + (gtk_text_view_get_window(text, GTK_TEXT_WINDOW_TEXT), + hand_cursor); + textview_show_uri(textview, &start, &end); + } else { + gdk_window_set_cursor + (gtk_text_view_get_window(text, GTK_TEXT_WINDOW_TEXT), + regular_cursor); + STATUSBAR_POP(textview); + } +} + +static gboolean textview_motion_notify(GtkWidget *widget, + GdkEventMotion *event, + TextView *textview) +{ + gint x, y; + + if (gtk_text_view_get_window(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT) != event->window) + return FALSE; + gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget), + GTK_TEXT_WINDOW_WIDGET, + event->x, event->y, &x, &y); + textview_set_cursor(textview, GTK_TEXT_VIEW(widget), x, y); + gdk_window_get_pointer(widget->window, NULL, NULL, NULL); + + return FALSE; +} + +static gboolean textview_leave_notify(GtkWidget *widget, + GdkEventCrossing *event, + TextView *textview) +{ + textview_set_cursor(textview, GTK_TEXT_VIEW(widget), 0, 0); + + return FALSE; +} + +static gboolean textview_visibility_notify(GtkWidget *widget, + GdkEventVisibility *event, + TextView *textview) +{ + gint wx, wy, bx, by; + GdkWindow *window; + + window = gtk_text_view_get_window(GTK_TEXT_VIEW(widget), + GTK_TEXT_WINDOW_TEXT); + + /* check if the event occurred for the text window part */ + if (window != event->window) + return FALSE; + + gdk_window_get_pointer(widget->window, &wx, &wy, NULL); + gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget), + GTK_TEXT_WINDOW_WIDGET, + wx, wy, &bx, &by); + textview_set_cursor(textview, GTK_TEXT_VIEW(widget), bx, by); + + return FALSE; +} + +#define ADD_MENU(label, func) \ +{ \ + menuitem = gtk_menu_item_new_with_mnemonic(label); \ + g_signal_connect(menuitem, "activate", G_CALLBACK(func), uri); \ + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); \ + g_object_set_data(G_OBJECT(menuitem), "textview", textview); \ + gtk_widget_show(menuitem); \ +} + +#define ADD_MENU_SEPARATOR() \ +{ \ + menuitem = gtk_menu_item_new(); \ + gtk_widget_set_sensitive(menuitem, FALSE); \ + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); \ + gtk_widget_show(menuitem); \ +} + +static void textview_populate_popup(GtkWidget *widget, GtkMenu *menu, + TextView *textview) +{ + gint px, py, x, y; + GtkWidget *separator, *menuitem; + GtkTextBuffer *buffer; + GtkTextIter iter, start, end; + gboolean on_link; + RemoteURI *uri; + GdkPixbuf *pixbuf; + gchar *selected_text; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); + + gdk_window_get_pointer(widget->window, &px, &py, NULL); + gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget), + GTK_TEXT_WINDOW_WIDGET, + px, py, &x, &y); + gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &iter, x, y); + if ((pixbuf = gtk_text_iter_get_pixbuf(&iter)) != NULL) { + start = end = iter; + gtk_text_iter_forward_char(&end); + uri = textview_get_uri(textview, &start, &end); + + separator = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator); + gtk_widget_show(separator); + + ADD_MENU(_("Sa_ve this image as..."), + textview_popup_menu_activate_image_cb); + } + + selected_text = gtkut_text_view_get_selection(GTK_TEXT_VIEW(widget)); + + uri = NULL; + on_link = textview_get_link_tag_bounds(textview, &iter, &start, &end); + if (!on_link) + goto finish; + + uri = textview_get_uri(textview, &start, &end); + if (!uri || !uri->uri) + goto finish; + + separator = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator); + gtk_widget_show(separator); + + if (!g_ascii_strncasecmp(uri->uri, "mailto:", 7)) { + ADD_MENU(_("Compose _new message"), + textview_popup_menu_activate_open_uri_cb); + ADD_MENU(_("R_eply to this address"), + textview_popup_menu_activate_reply_cb); + ADD_MENU_SEPARATOR(); + ADD_MENU(_("Add to address _book..."), + textview_popup_menu_activate_add_address_cb); + ADD_MENU(_("Copy this add_ress"), + textview_popup_menu_activate_copy_cb); + } else { + ADD_MENU(_("_Open with Web browser"), + textview_popup_menu_activate_open_uri_cb); + ADD_MENU(_("Copy this _link"), + textview_popup_menu_activate_copy_cb); + } + +finish: + syl_plugin_signal_emit("textview-menu-popup", menu, + GTK_TEXT_VIEW(widget), + uri ? uri->uri : NULL, + selected_text, + textview->messageview->msginfo); + g_free(selected_text); +} + +static void textview_popup_menu_activate_open_uri_cb(GtkMenuItem *menuitem, + gpointer data) +{ + RemoteURI *uri = (RemoteURI *)data; + TextView *textview; + + g_return_if_fail(uri != NULL); + + if (!uri->uri) + return; + + textview = g_object_get_data(G_OBJECT(menuitem), "textview"); + g_return_if_fail(textview != NULL); + + if (!g_ascii_strncasecmp(uri->uri, "mailto:", 7)) { + PrefsAccount *ac = NULL; + MsgInfo *msginfo = textview->messageview->msginfo; + + if (msginfo && msginfo->folder) + ac = account_find_from_item(msginfo->folder); + if (ac && ac->protocol == A_NNTP) + ac = NULL; + compose_new(ac, msginfo ? msginfo->folder : NULL, + uri->uri + 7, NULL); + } else if (uri->uri[0] == '#') { + /* don't open in-page link */ + } else if (textview_uri_security_check(textview, uri) == TRUE) + open_uri(uri->uri, prefs_common.uri_cmd); +} + +static void textview_popup_menu_activate_reply_cb(GtkMenuItem *menuitem, + gpointer data) +{ + RemoteURI *uri = (RemoteURI *)data; + TextView *textview; + + g_return_if_fail(uri != NULL); + + if (!uri->uri) + return; + + textview = g_object_get_data(G_OBJECT(menuitem), "textview"); + g_return_if_fail(textview != NULL); + + if (!g_ascii_strncasecmp(uri->uri, "mailto:", 7)) { + MsgInfo *msginfo = textview->messageview->msginfo; + ComposeMode mode = COMPOSE_REPLY; + gchar *text; + GList *list; + Compose *compose; + + g_return_if_fail(msginfo != NULL); + + if (prefs_common.reply_with_quote) + mode |= COMPOSE_WITH_QUOTE; + + text = gtkut_text_view_get_selection + (GTK_TEXT_VIEW(textview->text)); + if (text && *text == '\0') { + g_free(text); + text = NULL; + } + + compose_reply(msginfo, msginfo->folder, mode, text); + list = compose_get_compose_list(); + list = g_list_last(list); + if (list && list->data) { + compose = (Compose *)list->data; + compose_block_modified(compose); + compose_entry_set(compose, uri->uri + 7, + COMPOSE_ENTRY_TO); + compose_unblock_modified(compose); + } + g_free(text); + } +} + +static void textview_popup_menu_activate_add_address_cb(GtkMenuItem *menuitem, + gpointer data) +{ + RemoteURI *uri = (RemoteURI *)data; + gchar *addr; + + g_return_if_fail(uri != NULL); + + if (!uri->uri) + return; + + if (!g_ascii_strncasecmp(uri->uri, "mailto:", 7)) { + addr = g_malloc(strlen(uri->uri + 7) + 1); + decode_uri(addr, uri->uri + 7); + } else + addr = g_strdup(uri->uri); + + addressbook_add_contact(addr, addr, NULL); + + g_free(addr); +} + +static void textview_popup_menu_activate_copy_cb(GtkMenuItem *menuitem, + gpointer data) +{ + RemoteURI *uri = (RemoteURI *)data; + gchar *uri_string; + GtkClipboard *clipboard; + + g_return_if_fail(uri != NULL); + + if (!uri->uri) + return; + + if (!g_ascii_strncasecmp(uri->uri, "mailto:", 7)) { + uri_string = g_malloc(strlen(uri->uri + 7) + 1); + decode_uri(uri_string, uri->uri + 7); + } else + uri_string = g_strdup(uri->uri); + + clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY); + gtk_clipboard_set_text(clipboard, uri_string, -1); + clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + gtk_clipboard_set_text(clipboard, uri_string, -1); + + g_free(uri_string); +} + +static void textview_popup_menu_activate_image_cb(GtkMenuItem *menuitem, + gpointer data) +{ + RemoteURI *uri = (RemoteURI *)data; + gchar *src; + gchar *dest; + gchar *filename; + + g_return_if_fail(uri != NULL); + + if (!uri->uri) + return; + + src = g_filename_from_uri(uri->uri, NULL, NULL); + g_return_if_fail(src != NULL); + + filename = conv_filename_to_utf8(uri->filename); + dest = filesel_save_as(filename); + if (dest) { + copy_file(src, dest, FALSE); + g_free(dest); + } + g_free(filename); + g_free(src); +} + +static void textview_adj_value_changed(GtkAdjustment *adj, gpointer data) +{ + TextView *textview = (TextView *)data; + GtkTextBuffer *buffer; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text)); + if (gtk_text_buffer_get_selection_bounds(buffer, NULL, NULL)) + return; + gtk_text_view_place_cursor_onscreen(GTK_TEXT_VIEW(textview->text)); +} + +static gboolean textview_uri_security_check(TextView *textview, RemoteURI *uri) +{ + GtkTextBuffer *buffer; + GtkTextIter start_iter, end_iter; + gchar *visible_str; + gboolean retval = TRUE; + + if (is_uri_string(uri->uri) == FALSE) + return TRUE; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text)); + gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, uri->start); + gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, uri->end); + visible_str = gtk_text_buffer_get_text(buffer, &start_iter, &end_iter, + FALSE); + if (visible_str == NULL) + return TRUE; + + if (strcmp(visible_str, uri->uri) != 0 && is_uri_string(visible_str)) { + gchar *uri_path; + gchar *visible_uri_path; + + uri_path = get_uri_path(uri->uri); + visible_uri_path = get_uri_path(visible_str); + if (path_cmp(uri_path, visible_uri_path) != 0) + retval = FALSE; + } + + if (retval == FALSE) { + gchar *msg; + AlertValue aval; + + msg = g_strdup_printf(_("The real URL (%s) is different from\n" + "the apparent URL (%s).\n" + "\n" + "Open it anyway?"), + uri->uri, visible_str); + aval = alertpanel_full(_("Fake URL warning"), msg, + ALERT_WARNING, G_ALERTDEFAULT, FALSE, + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + g_free(msg); + if (aval == G_ALERTDEFAULT) + retval = TRUE; + } + + g_free(visible_str); + + return retval; +} + +static void textview_uri_list_remove_all(GSList *uri_list) +{ + GSList *cur; + + for (cur = uri_list; cur != NULL; cur = cur->next) { + RemoteURI *uri = (RemoteURI *)cur->data; + + if (uri) { + g_free(uri->uri); + g_free(uri->filename); + g_free(uri); + } + } + + g_slist_free(uri_list); +} + +static void textview_uri_list_update_offsets(TextView *textview, gint start, gint add) +{ + GSList *cur; + + debug_print("textview_uri_list_update_offsets: from %d: add %d\n", start, add); + + for (cur = textview->uri_list; cur != NULL; cur = cur->next) { + RemoteURI *uri = (RemoteURI *)cur->data; + + if (uri->start >= start) { + uri->start += add; + uri->end += add; + } + } +} diff --git a/src/textview.h b/src/textview.h new file mode 100644 index 0000000..b697173 --- /dev/null +++ b/src/textview.h @@ -0,0 +1,93 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2011 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __TEXTVIEW_H__ +#define __TEXTVIEW_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +typedef struct _TextView TextView; + +#include "messageview.h" +#include "procmime.h" + +struct _TextView +{ + GtkWidget *vbox; + GtkWidget *scrolledwin; + GtkWidget *text; + + GtkWidget *popup_menu; + GtkItemFactory *popup_factory; /* not used */ + + GtkTextTag *quote0_tag; + GtkTextTag *quote1_tag; + GtkTextTag *quote2_tag; + GtkTextTag *link_tag; + GtkTextTag *hover_link_tag; + + GSList *uri_list; + gint body_pos; + + gboolean show_all_headers; + + MessageView *messageview; +}; + +TextView *textview_create (void); +void textview_init (TextView *textview); +void textview_reflect_prefs (TextView *textview); + +void textview_show_message (TextView *textview, + MimeInfo *mimeinfo, + const gchar *file); +void textview_show_part (TextView *textview, + MimeInfo *mimeinfo, + FILE *fp); +void textview_show_error (TextView *textview); + +void textview_clear (TextView *textview); +void textview_destroy (TextView *textview); + +void textview_set_all_headers (TextView *textview, + gboolean all_headers); +void textview_set_font (TextView *textview, + const gchar *codeset); +void textview_set_position (TextView *textview, + gint pos); + +void textview_scroll_one_line (TextView *textview, + gboolean up); +gboolean textview_scroll_page (TextView *textview, + gboolean up); + +gboolean textview_search_string (TextView *textview, + const gchar *str, + gboolean case_sens); +gboolean textview_search_string_backward (TextView *textview, + const gchar *str, + gboolean case_sens); + +#endif /* __TEXTVIEW_H__ */ diff --git a/src/trayicon.c b/src/trayicon.c new file mode 100644 index 0000000..ef56dc1 --- /dev/null +++ b/src/trayicon.c @@ -0,0 +1,434 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "eggtrayicon.h" +#include "trayicon.h" +#include "mainwindow.h" +#include "utils.h" +#include "gtkutils.h" +#include "stock_pixmap.h" +#include "menu.h" +#include "main.h" +#include "inc.h" +#include "compose.h" +#include "prefs_common.h" + +#if GTK_CHECK_VERSION(2, 10, 0) || defined(GDK_WINDOWING_X11) + +#if GTK_CHECK_VERSION(2, 10, 0) +#include +#endif + +#ifdef G_OS_WIN32 +#define TRAYICON_IMAGE STOCK_PIXMAP_SYLPHEED_SMALL +#define TRAYICON_NEW_IMAGE STOCK_PIXMAP_SYLPHEED_NEWMAIL_SMALL +#else +#define TRAYICON_IMAGE STOCK_PIXMAP_SYLPHEED +#define TRAYICON_NEW_IMAGE STOCK_PIXMAP_SYLPHEED_NEWMAIL +#endif + +#define TRAYICON_NOTIFY_PERIOD 10000 + +static TrayIcon trayicon; +static GtkWidget *trayicon_menu; +static gboolean on_notify = FALSE; +static gboolean default_tooltip = FALSE; + +#if GTK_CHECK_VERSION(2, 10, 0) + +static void trayicon_activated (GtkStatusIcon *status_icon, + gpointer data); +static void trayicon_popup_menu_cb (GtkStatusIcon *status_icon, + guint button, + guint activate_time, + gpointer data); + +#else + +static GtkWidget *trayicon_img; +static GtkWidget *eventbox; +static GtkTooltips *trayicon_tip; + +static void trayicon_button_pressed (GtkWidget *widget, + GdkEventButton *event, + gpointer data); +static void trayicon_destroy_cb (GtkWidget *widget, + gpointer data); + +#endif + +static void trayicon_present (GtkWidget *widget, + gpointer data); +static void trayicon_inc (GtkWidget *widget, + gpointer data); +static void trayicon_inc_all (GtkWidget *widget, + gpointer data); +static void trayicon_send (GtkWidget *widget, + gpointer data); +static void trayicon_compose (GtkWidget *widget, + gpointer data); +static void trayicon_app_exit (GtkWidget *widget, + gpointer data); + +TrayIcon *trayicon_create(MainWindow *mainwin) +{ + GtkWidget *menuitem; + +#if GTK_CHECK_VERSION(2, 10, 0) + GdkPixbuf *pixbuf; + + stock_pixbuf_gdk(NULL, TRAYICON_IMAGE, &pixbuf); + trayicon.status_icon = gtk_status_icon_new_from_pixbuf(pixbuf); + + g_signal_connect(G_OBJECT(trayicon.status_icon), "activate", + G_CALLBACK(trayicon_activated), mainwin); + g_signal_connect(G_OBJECT(trayicon.status_icon), "popup-menu", + G_CALLBACK(trayicon_popup_menu_cb), mainwin); +#else + trayicon.widget = GTK_WIDGET(egg_tray_icon_new("Sylpheed")); + g_signal_connect(G_OBJECT(trayicon.widget), "destroy", + G_CALLBACK(trayicon_destroy_cb), mainwin); + + eventbox = gtk_event_box_new(); + gtk_widget_show(eventbox); + gtk_container_add(GTK_CONTAINER(trayicon.widget), eventbox); + g_signal_connect(G_OBJECT(eventbox), "button_press_event", + G_CALLBACK(trayicon_button_pressed), mainwin); + trayicon_img = stock_pixbuf_widget_scale(NULL, TRAYICON_IMAGE, 24, 24); + gtk_widget_show(trayicon_img); + gtk_container_add(GTK_CONTAINER(eventbox), trayicon_img); + + trayicon_tip = gtk_tooltips_new(); +#endif + on_notify = FALSE; + default_tooltip = FALSE; + trayicon_set_tooltip(NULL); + + if (!trayicon_menu) { + trayicon_menu = gtk_menu_new(); + gtk_widget_show(trayicon_menu); + MENUITEM_ADD_WITH_MNEMONIC(trayicon_menu, menuitem, + _("_Display Sylpheed"), 0); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(trayicon_present), mainwin); + MENUITEM_ADD(trayicon_menu, menuitem, NULL, 0); + MENUITEM_ADD_WITH_MNEMONIC(trayicon_menu, menuitem, + _("Get from _current account"), 0); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(trayicon_inc), mainwin); + MENUITEM_ADD_WITH_MNEMONIC(trayicon_menu, menuitem, + _("Get from _all accounts"), 0); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(trayicon_inc_all), mainwin); + MENUITEM_ADD_WITH_MNEMONIC(trayicon_menu, menuitem, + _("_Send queued messages"), 0); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(trayicon_send), mainwin); + + MENUITEM_ADD(trayicon_menu, menuitem, NULL, 0); + MENUITEM_ADD_WITH_MNEMONIC(trayicon_menu, menuitem, + _("Compose _new message"), 0); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(trayicon_compose), mainwin); + + MENUITEM_ADD(trayicon_menu, menuitem, NULL, 0); + MENUITEM_ADD_WITH_MNEMONIC(trayicon_menu, menuitem, + _("E_xit"), 0); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(trayicon_app_exit), mainwin); + } + + return &trayicon; +} + +#if GTK_CHECK_VERSION(2, 10, 0) + +void trayicon_show(TrayIcon *tray_icon) +{ + gtk_status_icon_set_visible(tray_icon->status_icon, TRUE); +}; + +void trayicon_hide(TrayIcon *tray_icon) +{ + gtk_status_icon_set_visible(tray_icon->status_icon, FALSE); +} + +void trayicon_destroy(TrayIcon *tray_icon) +{ + g_object_unref(tray_icon->status_icon); + tray_icon->status_icon = NULL; +} + +void trayicon_set_tooltip(const gchar *text) +{ + if (text) { + default_tooltip = FALSE; + gtk_status_icon_set_tooltip(trayicon.status_icon, text); + } else if (!default_tooltip) { + default_tooltip = TRUE; + gtk_status_icon_set_tooltip(trayicon.status_icon, + _("Sylpheed")); + } +} + +static guint notify_tag = 0; + +gboolean notify_timeout_cb(gpointer data) +{ + gdk_threads_enter(); + gtk_status_icon_set_blinking(trayicon.status_icon, FALSE); + notify_tag = 0; + gdk_threads_leave(); + + return FALSE; +} + +void trayicon_set_notify(gboolean enabled) +{ + if (enabled && !on_notify) { + trayicon_set_stock_icon(TRAYICON_NEW_IMAGE); + on_notify = TRUE; + } else if (!enabled && on_notify) { + trayicon_set_stock_icon(TRAYICON_IMAGE); + on_notify = FALSE; + } + + if (enabled && notify_tag == 0) { + gtk_status_icon_set_blinking(trayicon.status_icon, enabled); + notify_tag = g_timeout_add(TRAYICON_NOTIFY_PERIOD, notify_timeout_cb, NULL); + } else if (!enabled && notify_tag > 0) { + g_source_remove(notify_tag); + notify_timeout_cb(NULL); + } +} + +void trayicon_set_stock_icon(StockPixmap icon) +{ + GdkPixbuf *pixbuf; + + stock_pixbuf_gdk(NULL, icon, &pixbuf); + gtk_status_icon_set_from_pixbuf(trayicon.status_icon, pixbuf); +} + +static void trayicon_activated(GtkStatusIcon *status_icon, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + +#ifdef G_OS_WIN32 + if (prefs_common.toggle_window_on_trayicon_click && + !mainwin->window_hidden && !mainwin->window_obscured) + gtk_window_iconify(GTK_WINDOW(mainwin->window)); + else + main_window_popup(mainwin); +#else + if (prefs_common.toggle_window_on_trayicon_click && + gtk_window_is_active(GTK_WINDOW(mainwin->window))) + gtk_window_iconify(GTK_WINDOW(mainwin->window)); + else + main_window_popup(mainwin); +#endif +} + +static void trayicon_popup_menu_cb(GtkStatusIcon *status_icon, guint button, + guint activate_time, gpointer data) +{ + gtk_menu_popup(GTK_MENU(trayicon_menu), NULL, NULL, NULL, NULL, + button, activate_time); +} + +#else /* GTK_CHECK_VERSION(2, 10, 0) */ + +void trayicon_show(TrayIcon *tray_icon) +{ + gtk_widget_show(tray_icon->widget); +}; + +void trayicon_hide(TrayIcon *tray_icon) +{ + gtk_widget_destroy(tray_icon->widget); + tray_icon->widget = NULL; +} + +void trayicon_destroy(TrayIcon *tray_icon) +{ + g_signal_handlers_disconnect_by_func(G_OBJECT(tray_icon->widget), + G_CALLBACK(trayicon_destroy_cb), + main_window_get()); + gtk_widget_destroy(tray_icon->widget); + tray_icon->widget = NULL; +} + +void trayicon_set_tooltip(const gchar *text) +{ + if (text) { + default_tooltip = FALSE; + gtk_tooltips_set_tip(trayicon_tip, trayicon.widget, text, + NULL); + } else if (!default_tooltip) { + default_tooltip = TRUE; + gtk_tooltips_set_tip(trayicon_tip, trayicon.widget, + _("Sylpheed"), NULL); + } +} + +void trayicon_set_notify(gboolean enabled) +{ + if (enabled && !on_notify) { + trayicon_set_stock_icon(TRAYICON_NEW_IMAGE); + on_notify = TRUE; + } else if (!enabled && on_notify) { + trayicon_set_stock_icon(TRAYICON_IMAGE); + on_notify = FALSE; + } +} + +void trayicon_set_stock_icon(StockPixmap icon) +{ + GdkPixbuf *pixbuf; + GdkPixbuf *scaled_pixbuf; + + stock_pixbuf_gdk(NULL, icon, &pixbuf); + scaled_pixbuf = gdk_pixbuf_scale_simple(pixbuf, 24, 24, + GDK_INTERP_HYPER); + gtk_image_set_from_pixbuf(GTK_IMAGE(trayicon_img), scaled_pixbuf); + g_object_unref(scaled_pixbuf); +} + +static void trayicon_button_pressed(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + if (!event) + return; + + if (event->button == 1) { + if (prefs_common.toggle_window_on_trayicon_click && + gtk_window_is_active(GTK_WINDOW(mainwin->window))) + gtk_window_iconify(GTK_WINDOW(mainwin->window)); + else + main_window_popup(mainwin); + } else if (event->button == 3) { + gtk_menu_popup(GTK_MENU(trayicon_menu), NULL, NULL, NULL, NULL, + event->button, event->time); + } +} + +static gboolean trayicon_restore(gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + gdk_threads_enter(); + mainwin->tray_icon = trayicon_create(mainwin); + gdk_threads_leave(); + return FALSE; +} + +static void trayicon_destroy_cb(GtkWidget *widget, gpointer data) +{ + g_idle_add(trayicon_restore, data); +} + +#endif /* GTK_CHECK_VERSION(2, 10, 0) */ + +static void trayicon_present(GtkWidget *widget, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + main_window_popup(mainwin); +} + +static void trayicon_inc(GtkWidget *widget, gpointer data) +{ + if (!inc_is_active() && !gtkut_window_modal_exist()) + inc_mail((MainWindow *)data); +} + +static void trayicon_inc_all(GtkWidget *widget, gpointer data) +{ + if (!inc_is_active() && !gtkut_window_modal_exist()) + inc_all_account_mail((MainWindow *)data, FALSE); +} + +static void trayicon_send(GtkWidget *widget, gpointer data) +{ + if (!gtkut_window_modal_exist()) + main_window_send_queue((MainWindow *)data); +} + +static void trayicon_compose(GtkWidget *widget, gpointer data) +{ + if (!gtkut_window_modal_exist()) + compose_new(NULL, NULL, NULL, NULL); +} + +static void trayicon_app_exit(GtkWidget *widget, gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + if (mainwin->lock_count == 0 && !gtkut_window_modal_exist()) + app_will_exit(FALSE); +} + +#else /* GTK_CHECK_VERSION(2, 10, 0) || defined(GDK_WINDOWING_X11) */ + +TrayIcon *trayicon_create(MainWindow *mainwin) +{ + return NULL; +} + +void trayicon_show(TrayIcon *tray_icon) +{ +} + +void trayicon_hide(TrayIcon *tray_icon) +{ +} + +void trayicon_destroy(TrayIcon *tray_icon) +{ +} + +void trayicon_set_tooltip(const gchar *text) +{ +} + +void trayicon_set_notify(gboolean enabled) +{ +} + +void trayicon_set_stock_icon(StockPixmap icon) +{ +} + +#endif /* GTK_CHECK_VERSION(2, 10, 0) || defined(GDK_WINDOWING_X11) */ diff --git a/src/trayicon.h b/src/trayicon.h new file mode 100644 index 0000000..eea46af --- /dev/null +++ b/src/trayicon.h @@ -0,0 +1,53 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2006 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __TRAYICON_H__ +#define __TRAYICON_H__ + +#include +#include +#include + +#if GTK_CHECK_VERSION(2, 10, 0) +# include +#endif + +typedef struct _TrayIcon TrayIcon; + +#include "mainwindow.h" +#include "stock_pixmap.h" + +struct _TrayIcon +{ +#if GTK_CHECK_VERSION(2, 10, 0) + GtkStatusIcon *status_icon; +#else + GtkWidget *widget; +#endif +}; + +TrayIcon *trayicon_create (MainWindow *mainwin); +void trayicon_show (TrayIcon *tray_icon); +void trayicon_hide (TrayIcon *tray_icon); +void trayicon_destroy (TrayIcon *tray_icon); +void trayicon_set_tooltip (const gchar *text); +void trayicon_set_notify (gboolean enabled); +void trayicon_set_stock_icon (StockPixmap icon); + +#endif /* __TRAYICON_H__ */ diff --git a/src/undo.c b/src/undo.c new file mode 100644 index 0000000..122afc6 --- /dev/null +++ b/src/undo.c @@ -0,0 +1,673 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2011 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* code ported from gedit */ +/* This is for my patient girlfirend Regina */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "undo.h" +#include "utils.h" +#include "prefs_common.h" + +typedef struct _UndoInfo UndoInfo; + +struct _UndoInfo +{ + UndoAction action; + gchar *text; + gint start_pos; + gint end_pos; + gfloat window_position; + gint mergeable; +}; + +/* #define UNDO_DEBUG 1 */ +#ifdef UNDO_DEBUG +# define undo_debug debug_print +#else +# define undo_debug 1 ? (void)0 : debug_print +#endif + +static void undo_free_list (GList **list_pointer); +static void undo_check_size (UndoMain *undostruct); +static gint undo_merge (GList *list, + guint start_pos, + guint end_pos, + gint action, + const gchar *text); +static void undo_add (const gchar *text, + gint start_pos, + gint end_pos, + UndoAction action, + UndoMain *undostruct); +static gint undo_get_selection (GtkTextView *textview, + guint *start, + guint *end); +static void undo_insert_text_cb (GtkTextBuffer *textbuf, + GtkTextIter *iter, + gchar *new_text, + gint new_text_length, + UndoMain *undostruct); +static void undo_delete_text_cb (GtkTextBuffer *textbuf, + GtkTextIter *start, + GtkTextIter *end, + UndoMain *undostruct); + +static void undo_paste_clipboard_cb (GtkTextView *textview, + UndoMain *undostruct); + +void undo_undo (UndoMain *undostruct); +void undo_redo (UndoMain *undostruct); + + +UndoMain *undo_init(GtkWidget *text) +{ + UndoMain *undostruct; + GtkTextView *textview = GTK_TEXT_VIEW(text); + GtkTextBuffer *textbuf; + + g_return_val_if_fail(text != NULL, NULL); + + textbuf = gtk_text_view_get_buffer(textview); + + undostruct = g_new(UndoMain, 1); + undostruct->textview = textview; + undostruct->undo = NULL; + undostruct->redo = NULL; + undostruct->paste = 0; + undostruct->undo_state = FALSE; + undostruct->redo_state = FALSE; + + g_signal_connect(G_OBJECT(textbuf), "insert-text", + G_CALLBACK(undo_insert_text_cb), undostruct); + g_signal_connect(G_OBJECT(textbuf), "delete-range", + G_CALLBACK(undo_delete_text_cb), undostruct); + g_signal_connect(G_OBJECT(textview), "paste-clipboard", + G_CALLBACK(undo_paste_clipboard_cb), undostruct); + + return undostruct; +} + +void undo_destroy (UndoMain *undostruct) +{ + undo_free_list(&undostruct->undo); + undo_free_list(&undostruct->redo); + g_free(undostruct); +} + +static UndoInfo *undo_object_new(gchar *text, gint start_pos, gint end_pos, + UndoAction action, gfloat window_position) +{ + UndoInfo *undoinfo; + undoinfo = g_new (UndoInfo, 1); + undoinfo->text = text; + undoinfo->start_pos = start_pos; + undoinfo->end_pos = end_pos; + undoinfo->action = action; + undoinfo->window_position = window_position; + return undoinfo; +} + +static void undo_object_free(UndoInfo *undo) +{ + g_free (undo->text); + g_free (undo); +} + +/** + * undo_free_list: + * @list_pointer: list to be freed + * + * frees and undo structure list + **/ +static void undo_free_list(GList **list_pointer) +{ + UndoInfo *undo; + GList *cur, *list = *list_pointer; + + if (list == NULL) return; + + for (cur = list; cur != NULL; cur = cur->next) { + undo = (UndoInfo *)cur->data; + undo_object_free(undo); + } + + g_list_free(list); + *list_pointer = NULL; +} + +void undo_set_change_state_func(UndoMain *undostruct, UndoChangeStateFunc func, + gpointer data) +{ + g_return_if_fail(undostruct != NULL); + + undostruct->change_state_func = func; + undostruct->change_state_data = data; +} + +/** + * undo_check_size: + * @compose: document to check + * + * Checks that the size of compose->undo does not excede settings->undo_levels and + * frees any undo level above sett->undo_level. + * + **/ +static void undo_check_size(UndoMain *undostruct) +{ + UndoInfo *last_undo; + guint length; + + if (prefs_common.undolevels < 1) return; + + /* No need to check for the redo list size since the undo + list gets freed on any call to compose_undo_add */ + length = g_list_length(undostruct->undo); + if (length >= prefs_common.undolevels && prefs_common.undolevels > 0) { + last_undo = (UndoInfo *)g_list_last(undostruct->undo)->data; + undostruct->undo = g_list_remove(undostruct->undo, last_undo); + undo_object_free(last_undo); + } +} + +/** + * undo_merge: + * @last_undo: + * @start_pos: + * @end_pos: + * @action: + * + * This function tries to merge the undo object at the top of + * the stack with a new set of data. So when we undo for example + * typing, we can undo the whole word and not each letter by itself + * + * Return Value: TRUE is merge was sucessful, FALSE otherwise + **/ +static gint undo_merge(GList *list, guint start_pos, guint end_pos, + gint action, const gchar *text) +{ + gchar *temp_string; + UndoInfo *last_undo; + + undo_debug("undo_merge: %d: %d: text: %s\n", action, start_pos, text); + + /* This are the cases in which we will NOT merge : + 1. if (last_undo->mergeable == FALSE) + [mergeable = FALSE when the size of the undo data was not 1. + or if the data was size = 1 but = '\n' or if the undo object + has been "undone" already ] + 2. The size of text is not 1 + 3. If the new merging data is a '\n' + 4. If the last char of the undo_last data is a space/tab + and the new char is not a space/tab ( so that we undo + words and not chars ) + 5. If the type (action) of undo is different from the last one + Chema */ + + if (list == NULL) return FALSE; + + last_undo = list->data; + + if (!last_undo->mergeable) return FALSE; + + if (end_pos - start_pos != 1 || + text[0] == '\n' || + action != last_undo->action || + action == UNDO_ACTION_REPLACE_INSERT || + action == UNDO_ACTION_REPLACE_DELETE) { + last_undo->mergeable = FALSE; + return FALSE; + } + + if (action == UNDO_ACTION_DELETE) { + gboolean checkit = TRUE; + + if (last_undo->start_pos != end_pos && + last_undo->start_pos != start_pos) { + last_undo->mergeable = FALSE; + return FALSE; + } else if (last_undo->start_pos == start_pos) { + /* Deleted with the delete key */ + undo_debug("undo_merge: deleted with the delete key\n"); + if (text[0] != ' ' && text[0] != '\t' && + (last_undo->text[last_undo->end_pos - last_undo->start_pos - 1] == ' ' || + last_undo->text[last_undo->end_pos - last_undo->start_pos - 1] == '\t')) + checkit = FALSE; + else { + temp_string = g_strdup_printf("%s%s", last_undo->text, text); + last_undo->end_pos++; + g_free(last_undo->text); + last_undo->text = temp_string; + } + } else { + /* Deleted with the backspace key */ + undo_debug("undo_merge: deleted with the backspace key\n"); + if (text[0] != ' ' && text[0] != '\t' && + (last_undo->text[0] == ' ' || + last_undo->text[0] == '\t')) + checkit = FALSE; + else { + temp_string = g_strdup_printf("%s%s", text, last_undo->text); + last_undo->start_pos = start_pos; + g_free(last_undo->text); + last_undo->text = temp_string; + } + } + + if (!checkit) { + undo_debug("undo_merge: checkit = FALSE\n"); + last_undo->mergeable = FALSE; + return FALSE; + } + } else if (action == UNDO_ACTION_INSERT) { + if (last_undo->end_pos != start_pos) { + last_undo->mergeable = FALSE; + return FALSE; + } else { + temp_string = g_strdup_printf("%s%s", last_undo->text, text); + g_free(last_undo->text); + last_undo->end_pos = end_pos; + last_undo->text = temp_string; + } + } else + undo_debug("Unknown action [%i] inside undo merge encountered", action); + + undo_debug("undo_merge: merged: %d: text: %s\n", last_undo->start_pos, last_undo->text); + return TRUE; +} + +/** + * undo_add: + * @text: + * @start_pos: + * @end_pos: + * @action: either UNDO_ACTION_INSERT or UNDO_ACTION_DELETE + * + * Adds text to the undo stack. It also performs test to limit the number + * of undo levels and deltes the redo list + **/ + +static void undo_add(const gchar *text, + gint start_pos, gint end_pos, + UndoAction action, UndoMain *undostruct) +{ + UndoInfo *undoinfo; + GtkAdjustment *vadj; + + g_return_if_fail(text != NULL); + g_return_if_fail(end_pos >= start_pos); + + undo_debug("undo_add: %d: %d: %s\n", action, start_pos, text); + + undo_free_list(&undostruct->redo); + + /* Set the redo sensitivity */ + undostruct->change_state_func(undostruct, + UNDO_STATE_UNCHANGED, UNDO_STATE_FALSE, + undostruct->change_state_data); + + if (undostruct->paste != 0) { + if (action == UNDO_ACTION_INSERT) + action = UNDO_ACTION_REPLACE_INSERT; + else + action = UNDO_ACTION_REPLACE_DELETE; + undostruct->paste = undostruct->paste + 1; + if (undostruct->paste == 3) + undostruct->paste = 0; + } + + if (undo_merge(undostruct->undo, start_pos, end_pos, action, text)) + return; + + undo_check_size(undostruct); + + vadj = GTK_ADJUSTMENT(GTK_TEXT_VIEW(undostruct->textview)->vadjustment); + undoinfo = undo_object_new(g_strdup(text), start_pos, end_pos, action, + vadj->value); + + if (end_pos - start_pos != 1 || text[0] == '\n') + undoinfo->mergeable = FALSE; + else + undoinfo->mergeable = TRUE; + + undostruct->undo = g_list_prepend(undostruct->undo, undoinfo); + + undostruct->change_state_func(undostruct, + UNDO_STATE_TRUE, UNDO_STATE_UNCHANGED, + undostruct->change_state_data); +} + +/** + * undo_undo: + * @w: not used + * @data: not used + * + * Executes an undo request on the current document + **/ +void undo_undo(UndoMain *undostruct) +{ + UndoInfo *undoinfo; + GtkTextView *textview; + GtkTextBuffer *buffer; + GtkTextIter iter, start_iter, end_iter; + GtkTextMark *mark; + + g_return_if_fail(undostruct != NULL); + + if (undostruct->undo == NULL) return; + + /* The undo data we need is always at the top op the + stack. So, therefore, the first one */ + undoinfo = (UndoInfo *)undostruct->undo->data; + g_return_if_fail(undoinfo != NULL); + undoinfo->mergeable = FALSE; + undostruct->redo = g_list_prepend(undostruct->redo, undoinfo); + undostruct->undo = g_list_remove(undostruct->undo, undoinfo); + + textview = undostruct->textview; + buffer = gtk_text_view_get_buffer(textview); + + undo_block(undostruct); + + /* Check if there is a selection active */ + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark); + gtk_text_buffer_place_cursor(buffer, &iter); + + /* Move the view (scrollbars) to the correct position */ + gtk_adjustment_set_value(GTK_ADJUSTMENT(textview->vadjustment), + undoinfo->window_position); + + switch (undoinfo->action) { + case UNDO_ACTION_DELETE: + gtk_text_buffer_get_iter_at_offset + (buffer, &iter, undoinfo->start_pos); + gtk_text_buffer_insert(buffer, &iter, undoinfo->text, -1); + undo_debug("undo: UNDO_ACTION_DELETE: %d: %s\n", + undoinfo->start_pos, undoinfo->text); + break; + case UNDO_ACTION_INSERT: + gtk_text_buffer_get_iter_at_offset + (buffer, &start_iter, undoinfo->start_pos); + gtk_text_buffer_get_iter_at_offset + (buffer, &end_iter, undoinfo->end_pos); + gtk_text_buffer_delete(buffer, &start_iter, &end_iter); + undo_debug("undo: UNDO_ACTION_INSERT: %d: delete %d chars\n", + undoinfo->start_pos, + undoinfo->end_pos - undoinfo->start_pos); + break; + case UNDO_ACTION_REPLACE_INSERT: + gtk_text_buffer_get_iter_at_offset + (buffer, &start_iter, undoinfo->start_pos); + gtk_text_buffer_get_iter_at_offset + (buffer, &end_iter, undoinfo->end_pos); + undo_debug("undo: UNDO_ACTION_REPLACE: %d: %s\n", + undoinfo->start_pos, undoinfo->text); + /* "pull" another data structure from the list */ + undoinfo = (UndoInfo *)undostruct->undo->data; + g_return_if_fail(undoinfo != NULL); + undostruct->redo = g_list_prepend(undostruct->redo, undoinfo); + undostruct->undo = g_list_remove(undostruct->undo, undoinfo); + g_return_if_fail(undoinfo->action == UNDO_ACTION_REPLACE_DELETE); + gtk_text_buffer_insert(buffer, &start_iter, undoinfo->text, -1); + undo_debug("undo: UNDO_ACTION_REPLACE: %d: %s\n", + undoinfo->start_pos, undoinfo->text); + break; + case UNDO_ACTION_REPLACE_DELETE: + g_warning("undo: this should not happen: UNDO_REPLACE_DELETE"); + break; + default: + g_assert_not_reached(); + break; + } + + gtk_widget_queue_draw(GTK_WIDGET(textview)); + + undostruct->change_state_func(undostruct, + UNDO_STATE_UNCHANGED, UNDO_STATE_TRUE, + undostruct->change_state_data); + + if (undostruct->undo == NULL) + undostruct->change_state_func(undostruct, + UNDO_STATE_FALSE, + UNDO_STATE_UNCHANGED, + undostruct->change_state_data); + + undo_unblock(undostruct); +} + +/** + * undo_redo: + * @w: not used + * @data: not used + * + * executes a redo request on the current document + **/ +void undo_redo(UndoMain *undostruct) +{ + UndoInfo *redoinfo; + GtkTextView *textview; + GtkTextBuffer *buffer; + GtkTextIter iter, start_iter, end_iter; + GtkTextMark *mark; + + g_return_if_fail(undostruct != NULL); + + if (undostruct->redo == NULL) return; + + redoinfo = (UndoInfo *)undostruct->redo->data; + g_return_if_fail (redoinfo != NULL); + undostruct->undo = g_list_prepend(undostruct->undo, redoinfo); + undostruct->redo = g_list_remove(undostruct->redo, redoinfo); + + textview = undostruct->textview; + buffer = gtk_text_view_get_buffer(textview); + + undo_block(undostruct); + + /* Check if there is a selection active */ + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark); + gtk_text_buffer_place_cursor(buffer, &iter); + + /* Move the view to the right position. */ + gtk_adjustment_set_value(textview->vadjustment, + redoinfo->window_position); + + switch (redoinfo->action) { + case UNDO_ACTION_INSERT: + gtk_text_buffer_get_iter_at_offset + (buffer, &iter, redoinfo->start_pos); + gtk_text_buffer_insert(buffer, &iter, redoinfo->text, -1); + undo_debug("redo: UNDO_ACTION_DELETE: %d: %s\n", + redoinfo->start_pos, redoinfo->text); + break; + case UNDO_ACTION_DELETE: + gtk_text_buffer_get_iter_at_offset + (buffer, &start_iter, redoinfo->start_pos); + gtk_text_buffer_get_iter_at_offset + (buffer, &end_iter, redoinfo->end_pos); + gtk_text_buffer_delete(buffer, &start_iter, &end_iter); + undo_debug("redo: UNDO_ACTION_INSERT: %d: delete %d chars\n", + redoinfo->start_pos, + redoinfo->end_pos - redoinfo->start_pos); + break; + case UNDO_ACTION_REPLACE_DELETE: + gtk_text_buffer_get_iter_at_offset + (buffer, &start_iter, redoinfo->start_pos); + gtk_text_buffer_get_iter_at_offset + (buffer, &end_iter, redoinfo->end_pos); + gtk_text_buffer_delete(buffer, &start_iter, &end_iter); + undo_debug("redo: UNDO_ACTION_REPLACE: %d: %s\n", + redoinfo->start_pos, redoinfo->text); + /* "pull" another data structure from the list */ + redoinfo = (UndoInfo *)undostruct->redo->data; + g_return_if_fail(redoinfo != NULL); + undostruct->undo = g_list_prepend(undostruct->undo, redoinfo); + undostruct->redo = g_list_remove(undostruct->redo, redoinfo); + g_return_if_fail(redoinfo->action == UNDO_ACTION_REPLACE_INSERT); + gtk_text_buffer_insert(buffer, &start_iter, redoinfo->text, -1); + undo_debug("redo: UNDO_ACTION_REPLACE: %d: %s\n", + redoinfo->start_pos, redoinfo->text); + break; + case UNDO_ACTION_REPLACE_INSERT: + g_warning("redo: this should not happen: UNDO_REPLACE_INSERT"); + break; + default: + g_assert_not_reached(); + break; + } + + gtk_widget_queue_draw(GTK_WIDGET(textview)); + + undostruct->change_state_func(undostruct, + UNDO_STATE_TRUE, UNDO_STATE_UNCHANGED, + undostruct->change_state_data); + + if (undostruct->redo == NULL) + undostruct->change_state_func(undostruct, + UNDO_STATE_UNCHANGED, + UNDO_STATE_FALSE, + undostruct->change_state_data); + + undo_unblock(undostruct); +} + +void undo_block(UndoMain *undostruct) +{ + GtkTextBuffer *buffer; + + g_return_if_fail(GTK_IS_TEXT_VIEW(undostruct->textview)); + + buffer = gtk_text_view_get_buffer(undostruct->textview); + g_signal_handlers_block_by_func + (buffer, undo_insert_text_cb, undostruct); + g_signal_handlers_block_by_func + (buffer, undo_delete_text_cb, undostruct); + g_signal_handlers_block_by_func + (buffer, undo_paste_clipboard_cb, undostruct); +} + +void undo_unblock(UndoMain *undostruct) +{ + GtkTextBuffer *buffer; + + g_return_if_fail(GTK_IS_TEXT_VIEW(undostruct->textview)); + + buffer = gtk_text_view_get_buffer(undostruct->textview); + g_signal_handlers_unblock_by_func + (buffer, undo_insert_text_cb, undostruct); + g_signal_handlers_unblock_by_func + (buffer, undo_delete_text_cb, undostruct); + g_signal_handlers_unblock_by_func + (buffer, undo_paste_clipboard_cb, undostruct); +} + +void undo_insert_text_cb(GtkTextBuffer *textbuf, GtkTextIter *iter, + gchar *new_text, gint new_text_length, + UndoMain *undostruct) +{ + gchar *text_to_insert; + gint pos; + + if (prefs_common.undolevels <= 0) return; + + pos = gtk_text_iter_get_offset(iter); + + text_to_insert = g_strndup(new_text, new_text_length); + undo_add(text_to_insert, pos, pos + g_utf8_strlen(text_to_insert, -1), + UNDO_ACTION_INSERT, undostruct); + g_free(text_to_insert); +} + +void undo_delete_text_cb(GtkTextBuffer *textbuf, GtkTextIter *start, + GtkTextIter *end, UndoMain *undostruct) +{ + gchar *text_to_delete; + gint start_pos, end_pos; + + if (prefs_common.undolevels <= 0) return; + + text_to_delete = gtk_text_buffer_get_text(textbuf, start, end, FALSE); + if (!text_to_delete || !*text_to_delete) return; + + start_pos = gtk_text_iter_get_offset(start); + end_pos = gtk_text_iter_get_offset(end); + + undo_add(text_to_delete, start_pos, end_pos, UNDO_ACTION_DELETE, + undostruct); + g_free(text_to_delete); +} + +void undo_paste_clipboard_cb(GtkTextView *textview, UndoMain *undostruct) +{ + undo_debug("before Paste: %d\n", undostruct->paste); + if (prefs_common.undolevels > 0) + if (undo_get_selection(textview, NULL, NULL)) + undostruct->paste = TRUE; + undo_debug("after Paste: %d\n", undostruct->paste); +} + +/** + * undo_get_selection: + * @text: Text to get the selection from + * @start: return here the start position of the selection + * @end: return here the end position of the selection + * + * Gets the current selection for View + * + * Return Value: TRUE if there is a selection active, FALSE if not + **/ +static gint undo_get_selection(GtkTextView *textview, guint *start, guint *end) +{ + GtkTextBuffer *buffer; + GtkTextIter start_iter, end_iter; + guint start_pos, end_pos; + + buffer = gtk_text_view_get_buffer(textview); + gtk_text_buffer_get_selection_bounds(buffer, &start_iter, &end_iter); + + start_pos = gtk_text_iter_get_offset(&start_iter); + end_pos = gtk_text_iter_get_offset(&end_iter); + + /* The user can select from end to start too. If so, swap it*/ + if (end_pos < start_pos) { + guint swap_pos; + swap_pos = end_pos; + end_pos = start_pos; + start_pos = swap_pos; + } + + if (start != NULL) + *start = start_pos; + + if (end != NULL) + *end = end_pos; + + if ((start_pos > 0 || end_pos > 0) && (start_pos != end_pos)) + return TRUE; + else + return FALSE; +} diff --git a/src/undo.h b/src/undo.h new file mode 100644 index 0000000..1376d1e --- /dev/null +++ b/src/undo.h @@ -0,0 +1,80 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2001 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* code ported from gedit */ + +#ifndef __UNDO_H__ +#define __UNDO_H__ + +#include +#include +#include + +typedef enum +{ + UNDO_ACTION_INSERT, + UNDO_ACTION_DELETE, + UNDO_ACTION_REPLACE_INSERT, + UNDO_ACTION_REPLACE_DELETE, +} UndoAction; + +typedef enum +{ + UNDO_STATE_TRUE, + UNDO_STATE_FALSE, + UNDO_STATE_UNCHANGED, + UNDO_STATE_REFRESH, +} UndoState; + +typedef struct _UndoMain UndoMain; + +typedef void (*UndoChangeStateFunc) (UndoMain *undostruct, + gint undo_state, + gint redo_state, + gpointer data); + +struct _UndoMain +{ + GtkTextView *textview; + + GList *undo; + GList *redo; + + UndoChangeStateFunc change_state_func; + gpointer change_state_data; + + gboolean undo_state : 1; + gboolean redo_state : 1; + + gint paste; +}; + +UndoMain *undo_init (GtkWidget *text); +void undo_destroy (UndoMain *undostruct); + +void undo_set_change_state_func (UndoMain *undostruct, + UndoChangeStateFunc func, + gpointer data); + +void undo_undo (UndoMain *undostruct); +void undo_redo (UndoMain *undostruct); +void undo_block (UndoMain *undostruct); +void undo_unblock (UndoMain *undostruct); + +#endif /* __UNDO_H__ */ diff --git a/src/update_check.c b/src/update_check.c new file mode 100644 index 0000000..d677ebb --- /dev/null +++ b/src/update_check.c @@ -0,0 +1,826 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2015 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef USE_UPDATE_CHECK + +#include "defs.h" + +#include +#include + +#include +#include + +#include +#include + +#include "update_check.h" +#include "manage_window.h" +#include "inc.h" +#include "gtkutils.h" +#include "alertpanel.h" +#include "prefs_common.h" +#include "socket.h" +#include "utils.h" +#include "version.h" +#include "plugin.h" + +static gchar *check_url = NULL; +static gchar *download_url = NULL; +static gchar *jump_url = NULL; +#ifdef USE_UPDATE_CHECK_PLUGIN +static gchar *check_plugin_url = NULL; +static gchar *jump_plugin_url = NULL; +#endif /* USE_UPDATE_CHECK_PLUGIN */ + +static gint compare_version(gint lmajor, gint lminor, gint lmicro, + gint rmajor, gint rminor, gint rmicro) +{ + debug_print("comparing %d.%d.%d <> %d.%d.%d\n", + lmajor, lminor, lmicro, + rmajor, rminor, rmicro); + + if (lmajor > rmajor) + return 1; + if (lmajor < rmajor) + return -1; + if (lminor > rminor) + return 1; + if (lminor < rminor) + return -1; + if (lmicro > rmicro) + return 1; + if (lmicro < rmicro) + return -1; + + return 0; +} + +static gboolean compare_sylpheed_version(gint major, gint minor, gint micro, + const gchar *extra, gboolean remote_is_release, + gboolean cur_ver_is_release) +{ + debug_print("comparing %d.%d.%d.%s (%s) <> " VERSION " (%s)\n", + major, minor, micro, extra ? extra : "", + remote_is_release ? "release" : "devel", + cur_ver_is_release ? "release" : "devel"); + + switch (compare_version(major, minor, micro, + MAJOR_VERSION, MINOR_VERSION, MICRO_VERSION)) { + case 1: + return TRUE; + case -1: + return FALSE; + default: + break; + } + + /* compare extra version + 3.0.0.a (rel) > 3.0.0 (rel) + 3.0.0 (rel) > 3.0.0.betaX (dev) + 3.0.0.a (rel) > 3.0.0.betaX (dev) + 3.0.0.rc (dev) > 3.0.0.betaX (dev) */ + if (remote_is_release && !cur_ver_is_release) + return TRUE; + if (!remote_is_release && cur_ver_is_release) + return FALSE; + + if (extra) { + if (strcmp(extra, EXTRA_VERSION) > 0) + return TRUE; + } + + return FALSE; +} + +static void parse_version_string(const gchar *ver, gint *major, gint *minor, + gint *micro, gchar **extra) +{ + gchar **vers; + + vers = g_strsplit(ver, ".", -1); + if (vers[0]) { + *major = atoi(vers[0]); + if (vers[1]) { + *minor = atoi(vers[1]); + if (vers[2]) { + *micro = atoi(vers[2]); + if (vers[3] && extra) { + *extra = g_strdup(vers[3]); + } + } + } + } + g_strfreev(vers); +} + +#ifdef G_OS_WIN32 +static gboolean spawn_update_manager(void) +{ + gchar *src = NULL, *dest = NULL, *quoted_uri = NULL; + gchar *cmdline[] = {NULL, "/uri", NULL, NULL}; + GError *error = NULL; + gboolean ret = FALSE; + + src = g_strconcat(get_startup_dir(), G_DIR_SEPARATOR_S, "update-manager.exe", NULL); + if (!is_file_exist(src) || get_file_size(src) <= 0) { + g_warning("update-manager.exe not found."); + goto finish; + } + dest = g_strconcat(g_get_tmp_dir(), G_DIR_SEPARATOR_S, "sylpheed-update-manager.exe", NULL); + if (copy_file(src, dest, FALSE) < 0) { + g_warning("Couldn't copy update-manager.exe"); + goto finish; + } + quoted_uri = g_strdup_printf("'%s'", download_url); + cmdline[0] = dest; + cmdline[2] = quoted_uri; + if (g_spawn_async + (NULL, cmdline, NULL, + G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, + NULL, NULL, NULL, &error) == FALSE) { + g_warning("Couldn't execute update-manager.exe"); + if (error) { + g_warning("g_spawn_async: %s", error->message); + g_error_free(error); + } + } else { + ret = TRUE; + } +finish: + g_free(src); + g_free(dest); + g_free(quoted_uri); + return ret; +} + +#ifdef USE_UPDATE_CHECK_PLUGIN +static gchar *plugin_updater_ini = NULL; + +void update_check_spawn_plugin_updater(void) +{ + gchar *exe = NULL, *quoted_ini = NULL; + gchar *cmdline[] = {NULL, "/ini", NULL, NULL}; + GError *error = NULL; + gboolean ret = FALSE; + + if (!plugin_updater_ini) + return; + if (!is_file_exist(plugin_updater_ini)) { + g_warning("Not found %s", plugin_updater_ini); + goto finish; + } + exe = g_strconcat(get_startup_dir(), G_DIR_SEPARATOR_S, "plugin-updater.exe", NULL); + if (!is_file_exist(exe) || get_file_size(exe) <= 0) { + g_warning("Not found plugin-updater.exe"); + goto finish; + } + + quoted_ini = g_strdup_printf("'%s'", plugin_updater_ini); + cmdline[0] = exe; + cmdline[2] = quoted_ini; + if (g_spawn_sync + (NULL, cmdline, NULL, + G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, + NULL, NULL, NULL, NULL, NULL, &error) == FALSE) { + g_warning("Couldn't execute plugin-updater.exe"); + debug_print("Couldn't execute plugin-updater.exe\n"); + if (error) { + g_warning("g_spawn_async: %s", error->message); + debug_print("g_spawn_async: %s\n", error->message); + g_error_free(error); + } + } else { + ret = TRUE; + } +finish: + g_free(exe); + g_free(quoted_ini); + g_free(plugin_updater_ini); + plugin_updater_ini = NULL; +} +#endif /* USE_UPDATE_CHECK_PLUGIN */ +#endif /* G_OS_WIN32 */ + +#ifdef G_OS_WIN32 +static void set_default_download_url(void) +{ + gchar buf[1024]; + const gchar *os; + +#ifdef G_OS_WIN32 + os = "win"; +#else + if (strstr(TARGET_ALIAS, "linux")) + os = "linux"; + else + os = "other"; +#endif + +#ifdef DEVEL_VERSION + g_snprintf(buf, sizeof(buf), "%s?ver=%s&os=%s&dev=t", + DOWNLOAD_URI, VERSION, os); +#else + g_snprintf(buf, sizeof(buf), "%s?ver=%s&os=%s", + DOWNLOAD_URI, VERSION, os); +#endif + update_check_set_download_url(buf); +} +#endif + +static void update_dialog(const gchar *new_ver, const gchar *disp_ver, + gboolean manual) +{ + gchar buf[1024]; + AlertValue val; + + if (!jump_url) + update_check_set_jump_url(HOMEPAGE_URI); + + if (new_ver) { + if (disp_ver) + g_snprintf(buf, sizeof(buf), "%s\n\n%s\n(%s -> %s)", + _("A newer version of Sylpheed has been found.\n" + "Upgrade now?"), + disp_ver, VERSION, new_ver); + else + g_snprintf(buf, sizeof(buf), "%s\n\n%s -> %s", + _("A newer version of Sylpheed has been found.\n" + "Upgrade now?"), + VERSION, new_ver); + } else + g_snprintf(buf, sizeof(buf), "%s", + _("A newer version of Sylpheed has been found.\n" + "Upgrade now?")); + + val = alertpanel_full(_("New version found"), buf, + ALERT_QUESTION, + G_ALERTDEFAULT, + manual ? FALSE : TRUE, + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if ((val & G_ALERT_VALUE_MASK) == G_ALERTDEFAULT) { +#ifdef G_OS_WIN32 + if (!download_url) + set_default_download_url(); + if (!spawn_update_manager()) + open_uri(jump_url, prefs_common.uri_cmd); +#else + open_uri(jump_url, prefs_common.uri_cmd); +#endif + } + if (val & G_ALERTDISABLE) { + prefs_common.auto_update_check = FALSE; + } +} + +static gint child_stdout; + +static gchar *read_child_stdout_and_close(gint fd) +{ + GIOChannel *ch; + gchar *str = NULL; + gsize len = 0; + + ch = g_io_channel_unix_new(fd); + g_io_channel_read_to_end(ch, &str, &len, NULL); + g_io_channel_shutdown(ch, TRUE, NULL); + g_io_channel_unref(ch); + + return str; +} + +static void close_child_stdout(gint fd) +{ +#ifdef G_OS_WIN32 + GIOChannel *ch; + + ch = g_io_channel_win32_new_fd(fd); + g_io_channel_shutdown(ch, TRUE, NULL); + g_io_channel_unref(ch); +#else + fd_close(fd); +#endif +} + +static void update_check_cb(GPid pid, gint status, gpointer data) +{ + gchar **lines; + gchar *key, *val, *p; + gchar *new_ver = NULL; + gint i; +#ifdef DEVEL_VERSION + gboolean cur_ver_is_release = FALSE; +#else + gboolean cur_ver_is_release = TRUE; +#endif + gboolean result = FALSE; + gboolean got_version = FALSE; + gboolean rel_result = FALSE; + gboolean dev_result = FALSE; + gboolean show_dialog_always = GPOINTER_TO_INT(data); + gchar *str; + gchar *disp_rel_ver = NULL; + gchar *disp_dev_ver = NULL; + + debug_print("update_check_cb\n"); + + if (!child_stdout) { + g_spawn_close_pid(pid); + return; + } + + str = read_child_stdout_and_close(child_stdout); + child_stdout = 0; + g_spawn_close_pid(pid); + + if (!str) { + return; + } + + lines = g_strsplit(str, "\n", -1); + + for (i = 0; lines[i] != NULL; i++) { + gint major = 0, minor = 0, micro = 0; + gchar *extra = NULL; + + debug_print("update_check: %s\n", lines[i]); + p = strchr(lines[i], '='); + if (!p) continue; + key = g_strndup(lines[i], p - lines[i]); + val = p + 1; + + if (!disp_rel_ver && !strcmp(key, "DISP_RELEASE")) { + disp_rel_ver = g_strdup(val); + } else if (!cur_ver_is_release && !disp_dev_ver && + !strcmp(key, "DISP_DEVEL")) { + disp_dev_ver = g_strdup(val); + } + + if (!result) { + if (!strcmp(key, "RELEASE")) { + parse_version_string(val, &major, &minor, µ, + &extra); + result = compare_sylpheed_version(major, minor, micro, extra, + TRUE, cur_ver_is_release); + rel_result = result; + } else if (!cur_ver_is_release && !strcmp(key, "DEVEL")) { + parse_version_string(val, &major, &minor, µ, + &extra); + result = compare_sylpheed_version(major, minor, micro, extra, + FALSE, cur_ver_is_release); + dev_result = result; + } + + if (major + minor + micro != 0) + got_version = TRUE; + + if (result) { + new_ver = g_strdup_printf("%d.%d.%d%s", major, minor, micro, extra ? extra : ""); + debug_print("update_check: new ver: %s (%s)\n", new_ver, rel_result ? "release version" : "devel version"); + } + } + + g_free(extra); + g_free(key); + } + + g_strfreev(lines); + g_free(str); + + gdk_threads_enter(); + + if (!gtkut_window_modal_exist() && !inc_is_active()) { + if (result) { + if (rel_result) + update_dialog(new_ver, disp_rel_ver, show_dialog_always); + else + update_dialog(new_ver, disp_dev_ver, show_dialog_always); + } else if (show_dialog_always) { + if (got_version) + alertpanel_message(_("Information"), + _("Sylpheed is already the latest version."), + ALERT_NOTICE); + else + alertpanel_error(_("Couldn't get the version information.")); + } + } else { + debug_print("update_check_cb: modal dialog exists or incorporation is active. Disabling update dialog.\n"); + } + + g_free(disp_rel_ver); + g_free(disp_dev_ver); + g_free(new_ver); + + gdk_threads_leave(); +} + +static void spawn_curl(gchar *url, GChildWatchFunc func, gpointer data) +{ + gchar *cmdline[8] = {"curl", "--silent", "--max-time", "10"}; + gint argc = 4; + GPid pid; + GError *error = NULL; + + if (child_stdout > 0) { + debug_print("update check is in progress\n"); + return; + } + + child_stdout = 0; + + debug_print("spawn_curl: getting from %s\n", url); + + cmdline[argc++] = url; + if (prefs_common.use_http_proxy && prefs_common.http_proxy_host && + prefs_common.http_proxy_host[0] != '\0') { + cmdline[argc++] = "--proxy"; + cmdline[argc++] = prefs_common.http_proxy_host; + } + cmdline[argc++] = NULL; + + if (g_spawn_async_with_pipes + (NULL, cmdline, NULL, + G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, + NULL, NULL, &pid, + NULL, &child_stdout, NULL, &error) == FALSE) { + g_warning("Couldn't execute curl"); + if (error) { + g_warning("g_spawn_async_with_pipes: %s", + error->message); + g_error_free(error); + } + return; + } + if (pid == 0) { + g_warning("Couldn't get PID of child process"); + if (child_stdout) { + close_child_stdout(child_stdout); + child_stdout = 0; + } + return; + } + + g_child_watch_add(pid, func, data); +} + +#ifdef USE_UPDATE_CHECK_PLUGIN +struct download_plugin_info { + const gchar *filename; + const SylPluginInfo* info; + gchar *url; + gint major, minor, micro; +}; + +static void download_plugin_info_free(struct download_plugin_info *pinfo) +{ + if (!pinfo) + return; + g_free(pinfo->url); + g_free(pinfo); +} + +static GHashTable *get_plugin_version_table(void) +{ + GSList *list, *cur; + SylPluginInfo *info; + GModule *module; + struct download_plugin_info *pinfo; + GHashTable *plugin_version_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)download_plugin_info_free); + + list = syl_plugin_get_module_list(); + for (cur = list; cur != NULL; cur = cur->next) { + module = (GModule *)cur->data; + + info = syl_plugin_get_info(module); + if (info) { + pinfo = g_new0(struct download_plugin_info, 1); + pinfo->filename = g_module_name(module); + pinfo->info = info; + g_hash_table_insert(plugin_version_table, info->name, pinfo); + } else { + debug_print("info not found: %s\n", g_module_name(module)); + } + } + + return plugin_version_table; +} + +#ifdef G_OS_WIN32 +static gboolean write_plugin_updater_ini(GSList *list) +{ + guint num, h = 12; + struct download_plugin_info *pinfo; + GString *ini = g_string_new("[Settings]\n"); + GSList *cur; + gchar *basename, *p; + gboolean ret = TRUE; + + num = g_slist_length(list); + g_string_append_printf(ini, "NumFields=%d\n", num+1); + + num = 0; + g_string_append_printf(ini, "\n[Field %d]\n", ++num); + g_string_append_printf(ini, "Type=GroupBox\n"); + g_string_append_printf(ini, "Left=0\n"); + g_string_append_printf(ini, "Right=-1\n"); + g_string_append_printf(ini, "Top=0\n"); + g_string_append_printf(ini, "Bottom=-5\n"); + g_string_append_printf(ini, "Text=\" Select update plugins \"\n"); + + for (cur = list; cur != NULL; cur = cur->next) { + pinfo = cur->data; + g_string_append_printf(ini, "\n[Field %d]\n", ++num); + g_string_append_printf(ini, "Type=checkbox\n"); + g_string_append_printf(ini, "Text=%s %s -> %d.%d.%d\n", + pinfo->info->name, pinfo->info->version, + pinfo->major, pinfo->minor, pinfo->micro); + g_string_append_printf(ini, "Left=10\n"); + g_string_append_printf(ini, "Right=-10\n"); + g_string_append_printf(ini, "Top=%u\n", (h+=5)); + g_string_append_printf(ini, "Bottom=%u\n", (h+=8)); + g_string_append_printf(ini, "State=1\n"); + g_string_append_printf(ini, "URL=%s\n", pinfo->url); + g_string_append_printf(ini, "name=%s\n", pinfo->info->name); + + basename = g_path_get_basename(pinfo->filename); + p = strrchr(basename, '.'); + if (p) *p = '\0'; /* cut ".dll" */ + g_string_append_printf(ini, "basename=%s\n", basename); + g_free(basename); + } + + debug_print("write_plugin_updater_ini:\n%s\n", ini->str); + + plugin_updater_ini = g_strconcat(g_get_tmp_dir(), G_DIR_SEPARATOR_S, "sylpheed-plugin-updater.ini", NULL); + if (str_write_to_file(ini->str, plugin_updater_ini) < 0) { + g_free(plugin_updater_ini); + plugin_updater_ini = NULL; + ret = FALSE; + } + + g_string_free(ini, TRUE); + return ret; +} +#endif /* G_OS_WIN32 */ + +static void update_plugin_dialog(GString *text, GSList *list) +{ + AlertValue val; + + if (!jump_plugin_url) + update_check_set_jump_plugin_url(PLUGIN_HOMEPAGE_URI); + + val = alertpanel_full(_("New version found"), text->str, + ALERT_QUESTION, + G_ALERTDEFAULT, + FALSE, + GTK_STOCK_YES, GTK_STOCK_NO, NULL); + if ((val & G_ALERT_VALUE_MASK) == G_ALERTDEFAULT) { +#ifdef G_OS_WIN32 + if (write_plugin_updater_ini(list)) + app_will_restart(TRUE); +#else + open_uri(jump_plugin_url, prefs_common.uri_cmd); +#endif + } +} + +static void update_check_plugin_cb(GPid pid, gint status, gpointer data) +{ + gchar **lines; + gchar *key, *val, *p; + gchar *cur_ver; + gint i; + gboolean show_dialog_always = GPOINTER_TO_INT(data); + gchar *str; + GHashTable *plugin_version_table = NULL; + struct download_plugin_info *pinfo = NULL; + gboolean result = FALSE; + gboolean got_version = FALSE; + GString *text = NULL; + GSList *list = NULL; + + debug_print("update_check_plugin_cb\n"); + + if (!child_stdout) { + g_spawn_close_pid(pid); + return; + } + + str = read_child_stdout_and_close(child_stdout); + child_stdout = 0; + g_spawn_close_pid(pid); + + if (!str) { + return; + } + + lines = g_strsplit(str, "\n", -1); + plugin_version_table = get_plugin_version_table(); + text = g_string_new(_("Newer version of plug-ins have been found.\n" + "Upgrade now?\n")); + + for (i = 0; lines[i] != NULL; i++) { + gint new_major = 0, new_minor = 0, new_micro = 0; + gint cur_major = 0, cur_minor = 0, cur_micro = 0; + debug_print("update_check_plugin: %s\n", lines[i]); + p = strchr(lines[i], '='); + if (!p) continue; + key = g_strndup(lines[i], p - lines[i]); + val = p + 1; + + parse_version_string(val, &new_major, &new_minor, &new_micro, NULL); + if (new_major + new_minor + new_micro != 0) { + got_version = TRUE; + } + + pinfo = g_hash_table_lookup(plugin_version_table, key); + if (pinfo && (cur_ver = pinfo->info->version)) { + parse_version_string(cur_ver, &cur_major, &cur_minor, &cur_micro, NULL); + if (0 < compare_version(new_major, new_minor, new_micro, + cur_major, cur_minor, cur_micro)) { + g_string_append_printf(text, "\n - %s %d.%d.%d -> %d.%d.%d", key, + cur_major, cur_minor, cur_micro, + new_major, new_minor, new_micro); + + result = TRUE; + + debug_print("val = %s\n", val); + p = strchr(val, ','); + if (p) { + struct download_plugin_info *pinfo2 = g_new0(struct download_plugin_info, 1); + pinfo2->filename = pinfo->filename; + pinfo2->info = pinfo->info; + pinfo2->url = g_strdup(p + 1); /* skip ',' */ + pinfo2->major = new_major; + pinfo2->minor = new_minor; + pinfo2->micro = new_micro; + list = g_slist_append(list, pinfo2); + } + } + } + + g_free(key); + } + + g_hash_table_destroy(plugin_version_table); + g_strfreev(lines); + g_free(str); + + debug_print("%s\n", text->str); + + gdk_threads_enter(); + + if (!gtkut_window_modal_exist() && !inc_is_active()) { + if (result) { + update_plugin_dialog(text, list); + list = NULL; + } else if (show_dialog_always) { + if (got_version) + alertpanel_message(_("Information"), + _("All Sylpheed plug-ins are already the latest version."), + ALERT_NOTICE); + else + alertpanel_error(_("Couldn't get the version information of plug-ins.")); + } + } else { + debug_print("update_check_plugin_cb: modal dialog exists or incorporation is active. Disabling update dialog.\n"); + } + + g_string_free(text, TRUE); + g_slist_foreach(list, (GFunc)download_plugin_info_free, NULL); + g_slist_free(list); + + gdk_threads_leave(); +} + +void update_check_plugin(gboolean show_dialog_always) +{ + gchar buf[1024]; + + if (!check_plugin_url) { +#ifdef G_OS_WIN32 + g_snprintf(buf, sizeof(buf), "%s?ver=%s&os=win", PLUGIN_VERSION_URI, VERSION); +#else + if (strstr(TARGET_ALIAS, "linux")) + g_snprintf(buf, sizeof(buf), "%s?ver=%s&os=linux", PLUGIN_VERSION_URI, VERSION); + else + g_snprintf(buf, sizeof(buf), "%s?ver=%s&os=other", PLUGIN_VERSION_URI, VERSION); +#endif + update_check_set_check_plugin_url(buf); + } + spawn_curl(check_plugin_url, update_check_plugin_cb, GINT_TO_POINTER(show_dialog_always)); +} +#endif /* USE_UPDATE_CHECK_PLUGIN */ + +void update_check(gboolean show_dialog_always) +{ + gchar buf[1024]; + + if (!check_url) { +#ifdef G_OS_WIN32 + g_snprintf(buf, sizeof(buf), "%s?ver=%s&os=win", VERSION_URI, VERSION); +#else + if (strstr(TARGET_ALIAS, "linux")) + g_snprintf(buf, sizeof(buf), "%s?ver=%s&os=linux", VERSION_URI, VERSION); + else + g_snprintf(buf, sizeof(buf), "%s?ver=%s&os=other", VERSION_URI, VERSION); +#endif + update_check_set_check_url(buf); + } + spawn_curl(check_url, update_check_cb, GINT_TO_POINTER(show_dialog_always)); +} + +void update_check_set_check_url(const gchar *url) +{ + if (check_url) + g_free(check_url); + check_url = g_strdup(url); + + if (url) + debug_print("update_check_set_check_url: check URL was set to: %s\n", url); + else + debug_print("update_check_set_check_url: check URL was unset.\n"); +} + +const gchar *update_check_get_check_url(void) +{ + return check_url; +} + +void update_check_set_download_url(const gchar *url) +{ + if (download_url) + g_free(download_url); + download_url = g_strdup(url); + + if (url) + debug_print("update_check_set_download_url: download URL was set to: %s\n", url); + else + debug_print("update_check_set_download_url: download URL was unset.\n"); +} + +const gchar *update_check_get_download_url(void) +{ + return download_url; +} + +void update_check_set_jump_url(const gchar *url) +{ + if (jump_url) + g_free(jump_url); + jump_url = g_strdup(url); +} + +const gchar *update_check_get_jump_url(void) +{ + return jump_url; +} + +#ifdef USE_UPDATE_CHECK_PLUGIN +void update_check_set_check_plugin_url(const gchar *url) +{ + if (check_plugin_url) + g_free(check_plugin_url); + check_plugin_url = g_strdup(url); +} + +const gchar *update_check_get_check_plugin_url(void) +{ + return check_plugin_url; +} + +void update_check_set_jump_plugin_url(const gchar *url) +{ + if (jump_plugin_url) + g_free(jump_plugin_url); + jump_plugin_url = g_strdup(url); +} + +const gchar *update_check_get_jump_plugin_url(void) +{ + return jump_plugin_url; +} +#endif /* USE_UPDATE_CHECK_PLUGIN */ + +#endif /* USE_UPDATE_CHECK */ diff --git a/src/update_check.h b/src/update_check.h new file mode 100644 index 0000000..ac4c477 --- /dev/null +++ b/src/update_check.h @@ -0,0 +1,55 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2010 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __UPDATE_CHECK_H__ +#define __UPDATE_CHECK_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef USE_UPDATE_CHECK + +#include + +void update_check (gboolean show_dialog_always); +void update_check_plugin (gboolean show_dialog_always); +#ifdef G_OS_WIN32 +#ifdef USE_UPDATE_CHECK_PLUGIN +void update_check_spawn_plugin_updater(void); +#endif +#endif + +void update_check_set_check_url (const gchar *url); +const gchar *update_check_get_check_url (void); +void update_check_set_download_url (const gchar *url); +const gchar *update_check_get_download_url(void); +void update_check_set_jump_url (const gchar *url); +const gchar *update_check_get_jump_url (void); + +#ifdef USE_UPDATE_CHECK_PLUGIN +void update_check_set_check_plugin_url (const gchar *url); +const gchar *update_check_get_check_plugin_url(void); +void update_check_set_jump_plugin_url (const gchar *url); +const gchar *update_check_get_jump_plugin_url(void); +#endif /* USE_UPDATE_CHECK_PLUGIN */ + +#endif /* USE_UPDATE_CHECK */ + +#endif /* __UPDATE_CHECK_H__ */ diff --git a/src/vcard.c b/src/vcard.c new file mode 100644 index 0000000..dd7219a --- /dev/null +++ b/src/vcard.c @@ -0,0 +1,777 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Functions necessary to access vCard files. vCard files are used + * by GnomeCard for addressbook, and Netscape for sending business + * card information. Refer to RFC2426 for more information. + */ + +#include +#include +#include + +#include "mgutils.h" +#include "vcard.h" +#include "addritem.h" +#include "addrcache.h" +#include "utils.h" + +#define GNOMECARD_DIR ".gnome" +#define GNOMECARD_FILE "GnomeCard" +#define GNOMECARD_SECTION "[file]" +#define GNOMECARD_PARAM "open" + +#define VCARD_TEST_LINES 200 + +/* +* Create new cardfile object. +*/ +VCardFile *vcard_create() { + VCardFile *cardFile; + cardFile = g_new0( VCardFile, 1 ); + cardFile->name = NULL; + cardFile->path = NULL; + cardFile->file = NULL; + cardFile->bufptr = cardFile->buffer; + cardFile->addressCache = addrcache_create(); + cardFile->retVal = MGU_SUCCESS; + cardFile->accessFlag = FALSE; + return cardFile; +} + +/* +* Properties... +*/ +void vcard_set_name( VCardFile* cardFile, const gchar *value ) { + g_return_if_fail( cardFile != NULL ); + cardFile->name = mgu_replace_string( cardFile->name, value ); + g_strstrip( cardFile->name ); +} +void vcard_set_file( VCardFile* cardFile, const gchar *value ) { + g_return_if_fail( cardFile != NULL ); + addrcache_refresh( cardFile->addressCache ); + cardFile->path = mgu_replace_string( cardFile->path, value ); + g_strstrip( cardFile->path ); +} +void vcard_set_accessed( VCardFile *cardFile, const gboolean value ) { + g_return_if_fail( cardFile != NULL ); + cardFile->accessFlag = value; +} + +/* +* Test whether file was modified since last access. +* Return: TRUE if file was modified. +*/ +gboolean vcard_get_modified( VCardFile *vcardFile ) { + g_return_val_if_fail( vcardFile != NULL, FALSE ); + return addrcache_check_file( vcardFile->addressCache, vcardFile->path ); +} +gboolean vcard_get_accessed( VCardFile *vcardFile ) { + g_return_val_if_fail( vcardFile != NULL, FALSE ); + return addrcache_check_file( vcardFile->addressCache, vcardFile->path ); +} + +/* +* Test whether file was read. +* Return: TRUE if file was read. +*/ +gboolean vcard_get_read_flag( VCardFile *vcardFile ) { + g_return_val_if_fail( vcardFile != NULL, FALSE ); + return vcardFile->addressCache->dataRead; +} + +/* +* Return status code from last file operation. +* Return: Status code. +*/ +gint vcard_get_status( VCardFile *cardFile ) { + g_return_val_if_fail( cardFile != NULL, -1 ); + return cardFile->retVal; +} + +ItemFolder *vcard_get_root_folder( VCardFile *cardFile ) { + g_return_val_if_fail( cardFile != NULL, NULL ); + return addrcache_get_root_folder( cardFile->addressCache ); +} +gchar *vcard_get_name( VCardFile *cardFile ) { + g_return_val_if_fail( cardFile != NULL, NULL ); + return cardFile->name; +} + +/* +* Refresh internal variables to force a file read. +*/ +void vcard_force_refresh( VCardFile *cardFile ) { + addrcache_refresh( cardFile->addressCache ); +} + +/* +* Create new cardfile object for specified file. +*/ +VCardFile *vcard_create_path( const gchar *path ) { + VCardFile *cardFile; + cardFile = vcard_create(); + vcard_set_file(cardFile, path); + return cardFile; +} + +/* +* Free up cardfile object by releasing internal memory. +*/ +void vcard_free( VCardFile *cardFile ) { + g_return_if_fail( cardFile != NULL ); + + /* Close file */ + if( cardFile->file ) fclose( cardFile->file ); + + /* Free internal stuff */ + g_free( cardFile->name ); + g_free( cardFile->path ); + + /* Clear cache */ + addrcache_clear( cardFile->addressCache ); + addrcache_free( cardFile->addressCache ); + + /* Clear pointers */ + cardFile->file = NULL; + cardFile->name = NULL; + cardFile->path = NULL; + cardFile->addressCache = NULL; + cardFile->retVal = MGU_SUCCESS; + cardFile->accessFlag = FALSE; + + /* Now release file object */ + g_free( cardFile ); + +} + +/* +* Display object to specified stream. +*/ +void vcard_print_file( VCardFile *cardFile, FILE *stream ) { + g_return_if_fail( cardFile != NULL ); + + fprintf( stream, "VCardFile:\n" ); + fprintf( stream, " name: '%s'\n", cardFile->name ); + fprintf( stream, "file spec: '%s'\n", cardFile->path ); + fprintf( stream, " ret val: %d\n", cardFile->retVal ); + addrcache_print( cardFile->addressCache, stream ); + addritem_print_item_folder( cardFile->addressCache->rootFolder, stream ); +} + +/* +* Open file for read. +* return: TRUE if file opened successfully. +*/ +static gint vcard_open_file( VCardFile* cardFile ) { + g_return_val_if_fail( cardFile != NULL, -1 ); + + /* fprintf( stdout, "Opening file\n" ); */ + cardFile->addressCache->dataRead = FALSE; + if( cardFile->path ) { + cardFile->file = g_fopen( cardFile->path, "rb" ); + if( ! cardFile->file ) { + /* fprintf( stderr, "can't open %s\n", cardFile->path ); */ + cardFile->retVal = MGU_OPEN_FILE; + return cardFile->retVal; + } + } + else { + /* fprintf( stderr, "file not specified\n" ); */ + cardFile->retVal = MGU_NO_FILE; + return cardFile->retVal; + } + + /* Setup a buffer area */ + cardFile->buffer[0] = '\0'; + cardFile->bufptr = cardFile->buffer; + cardFile->retVal = MGU_SUCCESS; + return cardFile->retVal; +} + +/* +* Close file. +*/ +static void vcard_close_file( VCardFile *cardFile ) { + g_return_if_fail( cardFile != NULL ); + if( cardFile->file ) fclose( cardFile->file ); + cardFile->file = NULL; +} + +/* +* Read line of text from file. +* Return: ptr to buffer where line starts. +*/ +static gchar *vcard_read_line( VCardFile *cardFile ) { + while( *cardFile->bufptr == '\n' || *cardFile->bufptr == '\0' ) { + if( fgets( cardFile->buffer, VCARDBUFSIZE, cardFile->file ) == NULL ) + return NULL; + g_strstrip( cardFile->buffer ); + cardFile->bufptr = cardFile->buffer; + } + return cardFile->bufptr; +} + +/* +* Read line of text from file. +* Return: ptr to buffer where line starts. +*/ +static gchar *vcard_get_line( VCardFile *cardFile ) { + gchar buf[ VCARDBUFSIZE ]; + gchar *start, *end; + gint len; + + if (vcard_read_line( cardFile ) == NULL ) { + buf[0] = '\0'; + return NULL; + } + + /* Copy into private buffer */ + start = cardFile->bufptr; + len = strlen( start ); + end = start + len; + strncpy( buf, start, len ); + buf[ len ] = '\0'; + g_strstrip(buf); + cardFile->bufptr = end + 1; + + /* Return a copy of buffer */ + return g_strdup( buf ); +} + +/* +* Free linked lists of character strings. +*/ +static void vcard_free_lists( GSList *listName, GSList *listAddr, GSList *listRem, GSList* listID ) { + mgu_free_list( listName ); + mgu_free_list( listAddr ); + mgu_free_list( listRem ); + mgu_free_list( listID ); +} + +/* +* Read quoted-printable text, which may span several lines into one long string. +* Param: cardFile - object. +* Param: tagvalue - will be placed into the linked list. +*/ +static gchar *vcard_read_qp( VCardFile *cardFile, gchar *tagvalue ) { + GSList *listQP = NULL; + gint len = 0; + gchar *line = tagvalue; + + while( line ) { + listQP = g_slist_append( listQP, line ); + len = strlen( line ) - 1; + if( len > 0 ) { + if( line[ len ] != '=' ) break; + line[ len ] = '\0'; + } + line = vcard_get_line( cardFile ); + } + + /* Coalesce linked list into one long buffer. */ + line = mgu_list_coalesce( listQP ); + + /* Clean up */ + mgu_free_list( listQP ); + listQP = NULL; + return line; +} + +/* +* Parse tag name from line buffer. +* Return: Buffer containing the tag name, or NULL if no delimiter char found. +*/ +static gchar *vcard_get_tagname( gchar* line, gchar dlm ) { + gint len = 0; + gchar *tag = NULL; + gchar *lptr = line; + + while( *lptr++ ) { + if( *lptr == dlm ) { + len = lptr - line; + tag = g_strndup( line, len+1 ); + tag[ len ] = '\0'; + g_strdown( tag ); + return tag; + } + } + return tag; +} + +/* +* Parse tag value from line buffer. +* Return: Buffer containing the tag value. Empty string is returned if +* no delimiter char found. +*/ +static gchar *vcard_get_tagvalue( gchar* line, gchar dlm ) { + gchar *value = NULL; + gchar *start = NULL; + gchar *lptr; + gint len = 0; + + for( lptr = line; *lptr; lptr++ ) { + if( *lptr == dlm ) { + if( ! start ) + start = lptr + 1; + } + } + if( start ) { + len = lptr - start; + value = g_strndup( start, len+1 ); + } + else { + /* Ensure that we get an empty string */ + value = g_strndup( "", 1 ); + } + value[ len ] = '\0'; + return value; +} + +#if 0 +/* +* Dump linked lists of character strings (for debug). +*/ +static void vcard_dump_lists( GSList *listName, GSList *listAddr, GSList *listRem, GSList *listID, FILE *stream ) { + fprintf( stream, "dump name\n" ); + fprintf( stream, "------------\n" ); + mgu_print_list( listName, stdout ); + fprintf( stream, "dump address\n" ); + fprintf( stream, "------------\n" ); + mgu_print_list( listAddr, stdout ); + fprintf( stream, "dump remarks\n" ); + fprintf( stdout, "------------\n" ); + mgu_print_list( listRem, stdout ); + fprintf( stream, "dump id\n" ); + fprintf( stdout, "------------\n" ); + mgu_print_list( listID, stdout ); +} +#endif + +/* +* Build an address list entry and append to list of address items. +*/ +static void vcard_build_items( VCardFile *cardFile, GSList *listName, GSList *listAddr, GSList *listRem, + GSList *listID ) +{ + GSList *nodeName = listName; + GSList *nodeID = listID; + gchar *str; + while( nodeName ) { + GSList *nodeAddress = listAddr; + GSList *nodeRemarks = listRem; + ItemPerson *person = addritem_create_item_person(); + addritem_person_set_common_name( person, nodeName->data ); + while( nodeAddress ) { + str = nodeAddress->data; + if( *str != '\0' ) { + ItemEMail *email = addritem_create_item_email(); + addritem_email_set_address( email, str ); + if( nodeRemarks ) { + str = nodeRemarks->data; + if( str ) { + if( g_ascii_strcasecmp( str, "internet" ) != 0 ) { + if( *str != '\0' ) addritem_email_set_remarks( email, str ); + } + } + } + addrcache_id_email( cardFile->addressCache, email ); + addrcache_person_add_email( cardFile->addressCache, person, email ); + } + nodeAddress = g_slist_next( nodeAddress ); + nodeRemarks = g_slist_next( nodeRemarks ); + } + if( person->listEMail ) { + addrcache_id_person( cardFile->addressCache, person ); + addrcache_add_person( cardFile->addressCache, person ); + } + else { + addritem_free_item_person( person ); + } + if( nodeID ) { + str = nodeID->data; + addritem_person_set_external_id( person, str ); + } + nodeName = g_slist_next( nodeName ); + nodeID = g_slist_next( nodeID ); + } +} + +/* Unescape characters in quoted-printable string. */ +static void vcard_unescape_qp( gchar *value ) { + gchar *ptr, *src, *dest; + gint d, v = 0; + gchar ch; + gboolean gotch; + ptr = value; + while( *ptr ) { + gotch = FALSE; + if( *ptr == '=' ) { + v = 0; + ch = *(ptr + 1); + if( ch ) { + if( ch > '0' && ch < '8' ) v = ch - '0'; + } + d = -1; + ch = *(ptr + 2); + if( ch ) { + if( ch > '\x60' ) ch -= '\x20'; + d = ch - '0'; + if( d > 9 ) d -= 7; + if( d > -1 && d < 16 ) { + v = ( 16 * v ) + d; + gotch = TRUE; + } + } + } + if( gotch ) { + /* Replace = with char and move down in buffer */ + *ptr = v; + src = ptr + 3; + dest = ptr + 1; + while( *src ) { + *dest++ = *src++; + } + *dest = '\0'; + } + ptr++; + } +} + +/* +* Read file data into root folder. +* Note that one vCard can have multiple E-Mail addresses (MAIL tags); +* these are broken out into separate address items. An address item +* is generated for the person identified by FN tag and each EMAIL tag. +* If a sub-type is included in the EMAIL entry, this will be used as +* the Remarks member. Also note that it is possible for one vCard +* entry to have multiple FN tags; this might not make sense. However, +* it will generate duplicate address entries for each person listed. +*/ +static void vcard_read_file( VCardFile *cardFile ) { + gchar *tagtemp = NULL, *tagname = NULL, *tagvalue = NULL, *tagtype = NULL; + GSList *listName = NULL, *listAddress = NULL, *listRemarks = NULL, *listID = NULL; + /* GSList *listQP = NULL; */ + + for( ;; ) { + gchar *line; + + line = vcard_get_line( cardFile ); + if( line == NULL ) break; + + /* fprintf( stdout, "%s\n", line ); */ + + /* Parse line */ + tagtemp = vcard_get_tagname( line, VCARD_SEP_TAG ); + if( tagtemp == NULL ) { + g_free( line ); + continue; + } + + /* fprintf( stdout, "\ttemp: %s\n", tagtemp ); */ + + tagvalue = vcard_get_tagvalue( line, VCARD_SEP_TAG ); + if( tagvalue == NULL ) { + g_free( tagtemp ); + g_free( line ); + continue; + } + + tagname = vcard_get_tagname( tagtemp, VCARD_SEP_TYPE ); + tagtype = vcard_get_tagvalue( tagtemp, VCARD_SEP_TYPE ); + if( tagname == NULL ) { + tagname = tagtemp; + tagtemp = NULL; + } + + /* fprintf( stdout, "\tname: %s\n", tagname ); */ + /* fprintf( stdout, "\ttype: %s\n", tagtype ); */ + /* fprintf( stdout, "\tvalue: %s\n", tagvalue ); */ + + if( g_ascii_strcasecmp( tagtype, VCARD_TYPE_QP ) == 0 ) { + /* Quoted-Printable: could span multiple lines */ + tagvalue = vcard_read_qp( cardFile, tagvalue ); + vcard_unescape_qp( tagvalue ); + /* fprintf( stdout, "QUOTED-PRINTABLE !!! final\n>%s<\n", tagvalue ); */ + } + + if( g_ascii_strcasecmp( tagname, VCARD_TAG_START ) == 0 && + g_ascii_strcasecmp( tagvalue, VCARD_NAME ) == 0 ) { + /* fprintf( stdout, "start card\n" ); */ + vcard_free_lists( listName, listAddress, listRemarks, listID ); + listName = listAddress = listRemarks = listID = NULL; + } + if( g_ascii_strcasecmp( tagname, VCARD_TAG_FULLNAME ) == 0 ) { + /* fprintf( stdout, "- full name: %s\n", tagvalue ); */ + listName = g_slist_append( listName, g_strdup( tagvalue ) ); + } + if( g_ascii_strcasecmp( tagname, VCARD_TAG_EMAIL ) == 0 ) { + /* fprintf( stdout, "- address: %s\n", tagvalue ); */ + listAddress = g_slist_append( listAddress, g_strdup( tagvalue ) ); + listRemarks = g_slist_append( listRemarks, g_strdup( tagtype ) ); + } + if( g_ascii_strcasecmp( tagname, VCARD_TAG_UID ) == 0 ) { + /* fprintf( stdout, "- id: %s\n", tagvalue ); */ + listID = g_slist_append( listID, g_strdup( tagvalue ) ); + } + if( g_ascii_strcasecmp( tagname, VCARD_TAG_END ) == 0 && + g_ascii_strcasecmp( tagvalue, VCARD_NAME ) == 0 ) { + /* vCard is complete */ + /* fprintf( stdout, "end card\n--\n" ); */ + /* vcard_dump_lists( listName, listAddress, listRemarks, listID, stdout ); */ + vcard_build_items( cardFile, listName, listAddress, listRemarks, listID ); + vcard_free_lists( listName, listAddress, listRemarks, listID ); + listName = listAddress = listRemarks = listID = NULL; + } + + g_free( tagname ); + g_free( tagtype ); + g_free( tagvalue ); + g_free( tagtemp ); + g_free( line ); + } + + /* Free lists */ + vcard_free_lists( listName, listAddress, listRemarks, listID ); + listName = listAddress = listRemarks = listID = NULL; +} + +/* ============================================================================================ */ +/* +* Read file into list. Main entry point +* Return: TRUE if file read successfully. +*/ +/* ============================================================================================ */ +gint vcard_read_data( VCardFile *cardFile ) { + g_return_val_if_fail( cardFile != NULL, -1 ); + + cardFile->retVal = MGU_SUCCESS; + cardFile->accessFlag = FALSE; + if( addrcache_check_file( cardFile->addressCache, cardFile->path ) ) { + addrcache_clear( cardFile->addressCache ); + vcard_open_file( cardFile ); + if( cardFile->retVal == MGU_SUCCESS ) { + /* Read data into the list */ + vcard_read_file( cardFile ); + vcard_close_file( cardFile ); + + /* Mark cache */ + addrcache_mark_file( cardFile->addressCache, cardFile->path ); + cardFile->addressCache->modified = FALSE; + cardFile->addressCache->dataRead = TRUE; + } + } + return cardFile->retVal; +} + +/* +* Return link list of persons. +*/ +GList *vcard_get_list_person( VCardFile *cardFile ) { + g_return_val_if_fail( cardFile != NULL, NULL ); + return addrcache_get_list_person( cardFile->addressCache ); +} + +/* +* Return link list of folders. This is always NULL since there are +* no folders in GnomeCard. +* Return: NULL. +*/ +GList *vcard_get_list_folder( VCardFile *cardFile ) { + g_return_val_if_fail( cardFile != NULL, NULL ); + return NULL; +} + +/* +* Return link list of all persons. Note that the list contains references +* to items. Do *NOT* attempt to use the addrbook_free_xxx() functions... +* this will destroy the addressbook data! +* Return: List of items, or NULL if none. +*/ +GList *vcard_get_all_persons( VCardFile *cardFile ) { + g_return_val_if_fail( cardFile != NULL, NULL ); + return addrcache_get_all_persons( cardFile->addressCache ); +} + +/* +* Validate that all parameters specified. +* Return: TRUE if data is good. +*/ +gboolean vcard_validate( const VCardFile *cardFile ) { + gboolean retVal; + + g_return_val_if_fail( cardFile != NULL, FALSE ); + + retVal = TRUE; + if( cardFile->path ) { + if( strlen( cardFile->path ) < 1 ) retVal = FALSE; + } + else { + retVal = FALSE; + } + if( cardFile->name ) { + if( strlen( cardFile->name ) < 1 ) retVal = FALSE; + } + else { + retVal = FALSE; + } + return retVal; +} + +#define WORK_BUFLEN 1024 + +/* +* Attempt to find a valid GnomeCard file. +* Return: Filename, or home directory if not found. Filename should +* be g_free() when done. +*/ +gchar *vcard_find_gnomecard( void ) { + const gchar *homedir; + gchar buf[ WORK_BUFLEN ]; + gchar str[ WORK_BUFLEN ]; + gchar *fileSpec; + gint len, lenlbl, i; + FILE *fp; + + homedir = get_home_dir(); + if( ! homedir ) return NULL; + + strcpy( str, homedir ); + len = strlen( str ); + if( len > 0 ) { + if( str[ len-1 ] != G_DIR_SEPARATOR ) { + str[ len ] = G_DIR_SEPARATOR; + str[ ++len ] = '\0'; + } + } + strcat( str, GNOMECARD_DIR ); + strcat( str, G_DIR_SEPARATOR_S ); + strcat( str, GNOMECARD_FILE ); + + fileSpec = NULL; + if( ( fp = g_fopen( str, "rb" ) ) != NULL ) { + /* Read configuration file */ + lenlbl = strlen( GNOMECARD_SECTION ); + while( fgets( buf, sizeof( buf ), fp ) != NULL ) { + if( 0 == g_ascii_strncasecmp( buf, GNOMECARD_SECTION, lenlbl ) ) { + break; + } + } + + while( fgets( buf, sizeof( buf ), fp ) != NULL ) { + g_strchomp( buf ); + if( buf[0] == '[' ) break; + for( i = 0; i < lenlbl; i++ ) { + if( buf[i] == '=' ) { + if( 0 == g_ascii_strncasecmp( buf, GNOMECARD_PARAM, i ) ) { + fileSpec = g_strdup( buf + i + 1 ); + g_strstrip( fileSpec ); + } + } + } + } + fclose( fp ); + } + + if( fileSpec == NULL ) { + /* Use the home directory */ + str[ len ] = '\0'; + fileSpec = g_strdup( str ); + } + + return fileSpec; +} + +/* +* Attempt to read file, testing for valid vCard format. +* Return: TRUE if file appears to be valid format. +*/ +gint vcard_test_read_file( const gchar *fileSpec ) { + gboolean haveStart; + gchar *tagtemp = NULL, *tagname = NULL, *tagvalue = NULL, *tagtype = NULL, *line; + VCardFile *cardFile; + gint retVal, lines; + + if( ! fileSpec ) return MGU_NO_FILE; + + cardFile = vcard_create_path( fileSpec ); + cardFile->retVal = MGU_SUCCESS; + vcard_open_file( cardFile ); + if( cardFile->retVal == MGU_SUCCESS ) { + cardFile->retVal = MGU_BAD_FORMAT; + haveStart = FALSE; + lines = VCARD_TEST_LINES; + while( lines > 0 ) { + lines--; + if( ( line = vcard_get_line( cardFile ) ) == NULL ) break; + + /* Parse line */ + tagtemp = vcard_get_tagname( line, VCARD_SEP_TAG ); + if( tagtemp == NULL ) { + g_free( line ); + continue; + } + + tagvalue = vcard_get_tagvalue( line, VCARD_SEP_TAG ); + if( tagvalue == NULL ) { + g_free( tagtemp ); + g_free( line ); + continue; + } + + tagname = vcard_get_tagname( tagtemp, VCARD_SEP_TYPE ); + tagtype = vcard_get_tagvalue( tagtemp, VCARD_SEP_TYPE ); + if( tagname == NULL ) { + tagname = tagtemp; + tagtemp = NULL; + } + + if( g_ascii_strcasecmp( tagtype, VCARD_TYPE_QP ) == 0 ) { + /* Quoted-Printable: could span multiple lines */ + tagvalue = vcard_read_qp( cardFile, tagvalue ); + vcard_unescape_qp( tagvalue ); + } + if( g_ascii_strcasecmp( tagname, VCARD_TAG_START ) == 0 && + g_ascii_strcasecmp( tagvalue, VCARD_NAME ) == 0 ) { + haveStart = TRUE; + } + if( g_ascii_strcasecmp( tagname, VCARD_TAG_END ) == 0 && + g_ascii_strcasecmp( tagvalue, VCARD_NAME ) == 0 ) { + /* vCard is complete */ + if( haveStart ) cardFile->retVal = MGU_SUCCESS; + } + + g_free( tagname ); + g_free( tagtype ); + g_free( tagvalue ); + g_free( tagtemp ); + g_free( line ); + } + vcard_close_file( cardFile ); + } + retVal = cardFile->retVal; + vcard_free( cardFile ); + cardFile = NULL; + return retVal; +} + +/* +* End of Source. +*/ diff --git a/src/vcard.h b/src/vcard.h new file mode 100644 index 0000000..889949f --- /dev/null +++ b/src/vcard.h @@ -0,0 +1,105 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2001 Match Grun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Definitions necessary to access vCard files. vCard files are used + * by GnomeCard for addressbook, and Netscape for sending business + * card information. Refer to RFC2426 for more information. + */ + +#ifndef __VCARD_H__ +#define __VCARD_H__ + +#include +#include + +#include "addritem.h" +#include "addrcache.h" + +#define VCARDBUFSIZE 1024 + +#define VCARD_TAG_START "begin" +#define VCARD_TAG_END "end" +#define VCARD_NAME "vcard" + +#define VCARD_TAG_FULLNAME "fn" +#define VCARD_TAG_NAME "n" +#define VCARD_TAG_EMAIL "email" +#define VCARD_TAG_UID "uid" + +#define VCARD_TYPE_QP "quoted-printable" + +#define VCARD_SEP_TAG ':' +#define VCARD_SEP_TYPE ';' + +/* +// Typical vCard entry: +// +// BEGIN:VCARD +// FN:Axle Rose +// N:Rose;Axle;D;Ms;Jnr +// REV:2001-04-22T03:52:05 +// ADR;HOME:;;777 Lexington Avenue;Denver;CO;80299;USA +// ADR;POSTAL:P O Box 777;;;Denver;CO;80298;Usa +// TEL;HOME:303-555-1234 +// EMAIL;AOL:axlerose@aol.com +// EMAIL;INTERNET:axlerose@netscape.net +// TITLE:Janitor +// ORG:The Company +// URL:http://www.axlerose.com +// END:VCARD +*/ + +/* vCard object */ +typedef struct _VCardFile VCardFile; +struct _VCardFile { + gchar *name; + FILE *file; + gchar *path; + gchar buffer[ VCARDBUFSIZE ]; + gchar *bufptr; + AddressCache *addressCache; + gint retVal; + gboolean accessFlag; +}; + +/* Function prototypes */ +VCardFile *vcard_create ( void ); +VCardFile *vcard_create_path ( const gchar *path ); +void vcard_set_name ( VCardFile* cardFile, const gchar *value ); +void vcard_set_file ( VCardFile* cardFile, const gchar *value ); +void vcard_set_modified ( VCardFile *vcardFile, const gboolean value ); +void vcard_set_accessed ( VCardFile *vcardFile, const gboolean value ); +gboolean vcard_get_modified ( VCardFile *vcardFile ); +gboolean vcard_get_accessed ( VCardFile *vcardFile ); +gboolean vcard_get_read_flag ( VCardFile *vcardFile ); +gint vcard_get_status ( VCardFile *cardFile ); +ItemFolder *vcard_get_root_folder ( VCardFile *cardFile ); +gchar *vcard_get_name ( VCardFile *cardFile ); +void vcard_free ( VCardFile *cardFile ); +void vcard_force_refresh ( VCardFile *cardFile ); +gint vcard_read_data ( VCardFile *cardFile ); +GList *vcard_get_list_person ( VCardFile *cardFile ); +GList *vcard_get_list_folder ( VCardFile *cardFile ); +GList *vcard_get_all_persons ( VCardFile *cardFile ); +gboolean vcard_validate ( const VCardFile *cardFile ); +gchar *vcard_find_gnomecard ( void ); +gint vcard_test_read_file ( const gchar *fileSpec ); + +#endif /* __VCARD_H__ */ diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..d4a63dd --- /dev/null +++ b/src/version.h @@ -0,0 +1,33 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2012 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __VERSION_H__ +#define __VERSION_H__ + +#define PACKAGE "sylpheed" +#define VERSION "3.7.0" +#define MAJOR_VERSION 3 +#define MINOR_VERSION 7 +#define MICRO_VERSION 0 +#define EXTRA_VERSION "" +#define PROG_VERSION "Sylpheed " VERSION + +#define BUILD_REVISION 1185 + +#endif /* __VERSION_H__ */ diff --git a/src/version.h.in b/src/version.h.in new file mode 100644 index 0000000..05ee734 --- /dev/null +++ b/src/version.h.in @@ -0,0 +1,33 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2012 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __VERSION_H__ +#define __VERSION_H__ + +#define PACKAGE "@PACKAGE@" +#define VERSION "@VERSION@" +#define MAJOR_VERSION @MAJOR_VERSION@ +#define MINOR_VERSION @MINOR_VERSION@ +#define MICRO_VERSION @MICRO_VERSION@ +#define EXTRA_VERSION "@EXTRA_VERSION@" +#define PROG_VERSION "Sylpheed " VERSION + +#define BUILD_REVISION @BUILD_REVISION@ + +#endif /* __VERSION_H__ */ -- cgit v1.3