diff options
author | hiro <hiro@ee746299-78ed-0310-b773-934348b2243d> | 2009-06-10 08:11:08 +0000 |
---|---|---|
committer | hiro <hiro@ee746299-78ed-0310-b773-934348b2243d> | 2009-06-10 08:11:08 +0000 |
commit | ca06cd994b09cbb500b35af30c8bd75dc3e0e5b7 (patch) | |
tree | 3474db42cb4b1c42007cfb84ec24ca8331b50dba /src | |
parent | 81f0f7394f9c47a30314f2884be6d4bdae302673 (diff) |
merged plugin-test branch into trunk.
git-svn-id: svn://sylpheed.sraoss.jp/sylpheed/trunk@2164 ee746299-78ed-0310-b773-934348b2243d
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 30 | ||||
-rw-r--r-- | src/about.c | 4 | ||||
-rw-r--r-- | src/folderview.c | 15 | ||||
-rw-r--r-- | src/libsylpheed-plugin-0.def | 41 | ||||
-rw-r--r-- | src/main.c | 196 | ||||
-rw-r--r-- | src/mainwindow.c | 24 | ||||
-rw-r--r-- | src/manage_window.c | 5 | ||||
-rw-r--r-- | src/manage_window.h | 1 | ||||
-rw-r--r-- | src/plugin.c | 644 | ||||
-rw-r--r-- | src/plugin.h | 175 | ||||
-rw-r--r-- | src/plugin_manager.c | 227 | ||||
-rw-r--r-- | src/plugin_manager.h | 29 | ||||
-rw-r--r-- | src/prefs_common_dialog.c | 69 | ||||
-rw-r--r-- | src/update_check.c | 255 | ||||
-rw-r--r-- | src/update_check.h | 29 | ||||
-rw-r--r-- | src/version.h.in | 6 |
16 files changed, 1642 insertions, 108 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index bc25307e..bfe67d2d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -89,6 +89,8 @@ sylpheed_SOURCES = \ 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 @@ -100,11 +102,33 @@ BUILT_SOURCES = \ sylpheed-marshal.c \ sylpheed-marshal.h +lib_LTLIBRARIES = libsylpheed-plugin-0.la + +libsylpheed_plugin_0_la_SOURCES = \ + plugin.c + +libsylpheed_plugin_0includedir=$(includedir)/sylpheed +libsylpheed_plugin_0include_HEADERS = \ + plugin.h + +if NATIVE_WIN32 +no_undefined = -no-undefined +export_symbols = -export-symbols libsylpheed-plugin-0.def +endif + +libsylpheed_plugin_0_la_LDFLAGS = \ + -export-dynamic $(no_undefined) $(export_symbols) + +libsylpheed_plugin_0_la_LIBADD = \ + $(GTK_LIBS) \ + ../libsylph/libsylph-0.la + EXTRA_DIST = \ quote_fmt_parse.h \ sylpheed-marshal.list \ version.h.in \ - sylpheed.rc.in + sylpheed.rc.in \ + libsylpheed-plugin-0.def INCLUDES = \ -DG_LOG_DOMAIN=\"Sylpheed\" \ @@ -128,13 +152,15 @@ sylpheed_LDADD = \ $(GPGME_LIBS) \ $(LDAP_LIBS) \ $(LIBICONV) \ - ../libsylph/libsylph.la \ + libsylpheed-plugin-0.la \ + ../libsylph/libsylph-0.la \ $(SYLPHEED_RES) AM_CPPFLAGS = \ -DLOCALEDIR=\""$(localedir)"\" \ -DMANUALDIR=\""$(manualdir)"\" \ -DFAQDIR=\""$(faqdir)"\" \ + -DPLUGINDIR=\""$(plugindir)"\" \ -DTARGET_ALIAS=\""$(target_triplet)"\" \ -DSYSCONFDIR=\""$(sysconfdir)"\" diff --git a/src/about.c b/src/about.c index 6e22e9b2..01e7a24d 100644 --- a/src/about.c +++ b/src/about.c @@ -1,6 +1,6 @@ /* * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2008 Hiroyuki Yamamoto + * 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 @@ -175,7 +175,7 @@ static void about_create(void) gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); label = gtk_label_new - ("Copyright (C) 1999-2008 Hiroyuki Yamamoto <hiro-y@kcn.ne.jp>"); + ("Copyright (C) 1999-2009 Hiroyuki Yamamoto <hiro-y@kcn.ne.jp>"); gtk_label_set_selectable(GTK_LABEL(label), TRUE); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); diff --git a/src/folderview.c b/src/folderview.c index 17272447..1c0aeb7a 100644 --- a/src/folderview.c +++ b/src/folderview.c @@ -69,6 +69,7 @@ #include "inc.h" #include "send_message.h" #include "virtual.h" +#include "plugin.h" enum { @@ -1773,10 +1774,11 @@ static gboolean folderview_menu_popup(FolderView *folderview, SET_VISIBILITY(ifactory, "/Edit search condition...", item->stype == F_VIRTUAL); - #undef SET_SENS #undef SET_VISIBILITY + syl_plugin_signal_emit("folderview-menu-popup", ifactory); + gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL, event->button, event->time); @@ -2282,6 +2284,16 @@ static void folderview_new_folder_cb(FolderView *folderview, guint action, 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')) { @@ -2289,6 +2301,7 @@ static void folderview_new_folder_cb(FolderView *folderview, guint action, G_DIR_SEPARATOR); return; } +#endif name = trim_string(new_folder, 32); AUTORELEASE_STR(name, {g_free(name); return;}); diff --git a/src/libsylpheed-plugin-0.def b/src/libsylpheed-plugin-0.def new file mode 100644 index 00000000..af7f91cd --- /dev/null +++ b/src/libsylpheed-plugin-0.def @@ -0,0 +1,41 @@ +; c:\MinGW\bin\dlltool.exe -z libsylpheed-plugin-0.def --export-all-symbols .libs/plugin.o
+EXPORTS
+ syl_plugin_add_factory_item @ 1
+ syl_plugin_add_menuitem @ 2
+ syl_plugin_add_symbol @ 3
+ syl_plugin_app_will_exit @ 4
+ syl_plugin_folder_sel @ 5
+ syl_plugin_folder_sel_full @ 6
+ syl_plugin_folderview_get @ 7
+ syl_plugin_folderview_get_selected_item @ 8
+ syl_plugin_get_prog_version @ 9
+ syl_plugin_get_type @ 10
+ syl_plugin_inc_lock @ 11
+ syl_plugin_inc_mail @ 12
+ syl_plugin_inc_unlock @ 13
+ syl_plugin_init_lib @ 14
+ syl_plugin_input_dialog @ 15
+ syl_plugin_input_dialog_with_invisible @ 16
+ syl_plugin_load @ 17
+ syl_plugin_load_all @ 18
+ syl_plugin_main_window_get @ 19
+ syl_plugin_main_window_popup @ 20
+ syl_plugin_manage_window_set_transient @ 21
+ syl_plugin_manage_window_signals_connect @ 22
+ syl_plugin_messageview_create_with_new_window @ 23
+ syl_plugin_open_message @ 24
+ syl_plugin_open_message_by_new_window @ 25
+ syl_plugin_signal_connect @ 26
+ syl_plugin_signal_disconnect @ 27
+ syl_plugin_signal_emit @ 28
+ syl_plugin_sumary_select_by_msgnum @ 29
+ syl_plugin_summary_select_by_msginfo @ 30
+ syl_plugin_summary_view_get @ 31
+ syl_plugin_unload_all @ 32
+ syl_plugin_get_module_list @ 33
+ syl_plugin_get_info @ 34
+ syl_plugin_check_version @ 35
+ syl_plugin_menu_set_sensitive @ 36
+ syl_plugin_menu_set_sensitive_all @ 37
+ syl_plugin_menu_set_active @ 38
+ syl_plugin_manage_window_get_focus_window @ 39
@@ -1,6 +1,6 @@ /* * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2008 Hiroyuki Yamamoto + * 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 @@ -75,11 +75,16 @@ #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 "plugin.h" +#include "plugin_manager.h" +#include "foldersel.h" +#include "update_check.h" #if USE_GPGME # include "rfc2015.h" @@ -136,6 +141,7 @@ 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); @@ -174,6 +180,10 @@ static void send_queue (void); exit(val); \ } +static void load_cb(GObject *obj, GModule *module, gpointer data) +{ + g_print("load_cb: %p (%s), %p\n", module, module ? g_module_name(module) : "(null)", data); +} int main(int argc, char *argv[]) { @@ -183,15 +193,11 @@ int main(int argc, char *argv[]) #ifdef G_OS_WIN32 GList *iconlist = NULL; #endif + GObject *syl_app; app_init(); parse_cmd_opt(argc, argv); - sock_init(); -#if USE_SSL - ssl_init(); -#endif - /* check and create (unix domain) socket for remote operation */ lock_socket = prohibit_duplicate_launch(); if (lock_socket < 0) return 0; @@ -205,6 +211,8 @@ int main(int argc, char *argv[]) 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()); @@ -324,7 +332,13 @@ int main(int argc, char *argv[]) inc_autocheck_timer_init(mainwin); + plugin_init(); + + g_signal_emit_by_name(syl_app, "init-done"); + remote_command_exec(); + if (prefs_common.auto_update_check) + update_check(FALSE); gtk_main(); @@ -568,70 +582,9 @@ static gint get_queued_message_num(void) static void app_init(void) { -#ifdef G_OS_WIN32 - gchar *newpath; - const gchar *lang_env; - - /* disable locale variable such as "LANG=1041" */ - -#define DISABLE_DIGIT_LOCALE(envstr) \ -{ \ - lang_env = g_getenv(envstr); \ - if (lang_env && g_ascii_isdigit(lang_env[0])) \ - g_unsetenv(envstr); \ -} - - DISABLE_DIGIT_LOCALE("LC_ALL"); - DISABLE_DIGIT_LOCALE("LANG"); - DISABLE_DIGIT_LOCALE("LC_CTYPE"); - DISABLE_DIGIT_LOCALE("LC_MESSAGES"); - -#undef DISABLE_DIGIT_LOCALE - - g_unsetenv("LANGUAGE"); -#endif - -#ifdef HAVE_LOCALE_H - setlocale(LC_ALL, ""); -#endif + syl_init(); prog_version = PROG_VERSION; - set_startup_dir(); - -#ifdef G_OS_WIN32 - /* include startup directory into %PATH% for GSpawn */ - newpath = g_strconcat(get_startup_dir(), ";", g_getenv("PATH"), NULL); - g_setenv("PATH", newpath, TRUE); - g_free(newpath); -#endif - -#ifdef ENABLE_NLS - if (g_path_is_absolute(LOCALEDIR)) - bindtextdomain(PACKAGE, LOCALEDIR); - else { - gchar *locale_dir; - - locale_dir = g_strconcat(get_startup_dir(), G_DIR_SEPARATOR_S, - LOCALEDIR, NULL); -#ifdef G_OS_WIN32 - { - gchar *locale_dir_; - - locale_dir_ = g_locale_from_utf8(locale_dir, -1, - NULL, NULL, NULL); - if (locale_dir_) { - g_free(locale_dir); - locale_dir = locale_dir_; - } - } -#endif /* G_OS_WIN32 */ - bindtextdomain(PACKAGE, locale_dir); - g_free(locale_dir); - } - - bind_textdomain_codeset(PACKAGE, CS_UTF_8); - textdomain(PACKAGE); -#endif /* ENABLE_NLS */ #ifdef G_OS_WIN32 read_ini_file(); @@ -704,24 +657,7 @@ static void setup_rc_dir(void) } #endif /* !G_OS_WIN32 */ - if (!is_dir_exist(get_rc_dir())) { - if (make_dir_hier(get_rc_dir()) < 0) - exit(1); - } - - MAKE_DIR_IF_NOT_EXIST(get_mail_base_dir()); - - CHDIR_EXIT_IF_FAIL(get_rc_dir(), 1); - - MAKE_DIR_IF_NOT_EXIST(get_imap_cache_dir()); - MAKE_DIR_IF_NOT_EXIST(get_news_cache_dir()); - MAKE_DIR_IF_NOT_EXIST(get_mime_tmp_dir()); - MAKE_DIR_IF_NOT_EXIST(get_tmp_dir()); - MAKE_DIR_IF_NOT_EXIST(UIDL_DIR); - - /* remove temporary files */ - remove_all_files(get_tmp_dir()); - remove_all_files(get_mime_tmp_dir()); + syl_setup_rc_dir(); } void app_will_exit(gboolean force) @@ -760,6 +696,8 @@ void app_will_exit(gboolean force) manage_window_focus_in(mainwin->window, NULL, NULL); } + g_signal_emit_by_name(syl_app_get(), "app-exit"); + inc_autocheck_timer_remove(); if (prefs_common.clean_on_exit) @@ -773,36 +711,26 @@ void app_will_exit(gboolean force) procmsg_remove_all_cached_messages(FOLDER(ac->folder)); } + syl_plugin_unload_all(); + trayicon_destroy(mainwin->tray_icon); /* save all state before exiting */ - folder_write_list(); summary_write_cache(mainwin->summaryview); - main_window_get_size(mainwin); main_window_get_position(mainwin); - prefs_common_write_config(); - filter_write_config(); - account_write_config_all(); + 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 */ - remove_all_files(get_tmp_dir()); - remove_all_files(get_mime_tmp_dir()); - - set_log_handlers(FALSE); - close_log_file(); + /* remove temporary files, close log file, socket cleanup */ + syl_cleanup(); lock_socket_remove(); -#if USE_SSL - ssl_done(); -#endif cleanup_console(); - sock_cleanup(); if (gtk_main_level() > 0) gtk_main_quit(); @@ -1080,6 +1008,72 @@ static void register_system_events(void) } #endif +static void plugin_init(void) +{ + MainWindow *mainwin; + + mainwin = main_window_get(); + + if (syl_plugin_init_lib() != 0) + return; + + syl_plugin_add_symbol("prog_version", prog_version); + syl_plugin_add_symbol("main_window_get", main_window_get); + syl_plugin_add_symbol("main_window_popup", main_window_popup); + syl_plugin_add_symbol("app_will_exit", app_will_exit); + syl_plugin_add_symbol("main_window_menu_factory", + mainwin->menu_factory); + + syl_plugin_add_symbol("folderview", mainwin->folderview); + syl_plugin_add_symbol("folderview_get_selected_item", + folderview_get_selected_item); + syl_plugin_add_symbol("folderview_mail_popup_factory", + mainwin->folderview->mail_factory); + + syl_plugin_add_symbol("summaryview", mainwin->summaryview); + syl_plugin_add_symbol("summary_select_by_msgnum", + summary_select_by_msgnum); + syl_plugin_add_symbol("summary_select_by_msginfo", + summary_select_by_msginfo); + + syl_plugin_add_symbol("messageview_create_with_new_window", + messageview_create_with_new_window); + syl_plugin_add_symbol("messageview_show", messageview_show); + + syl_plugin_add_symbol("foldersel_folder_sel", + foldersel_folder_sel); + syl_plugin_add_symbol("foldersel_folder_sel_full", + foldersel_folder_sel_full); + syl_plugin_add_symbol("input_dialog", input_dialog); + syl_plugin_add_symbol("input_dialog_with_invisible", + input_dialog_with_invisible); + + syl_plugin_add_symbol("manage_window_set_transient", + manage_window_set_transient); + syl_plugin_add_symbol("manage_window_signals_connect", + manage_window_signals_connect); + syl_plugin_add_symbol("manage_window_get_focus_window", + manage_window_get_focus_window); + + syl_plugin_add_symbol("inc_mail", inc_mail); + syl_plugin_add_symbol("inc_lock", inc_lock); + syl_plugin_add_symbol("inc_unlock", inc_unlock); + + syl_plugin_signal_connect("plugin-load", G_CALLBACK(load_cb), NULL); + +#ifdef G_OS_WIN32 + { + gchar *path; + path = g_strconcat(get_startup_dir(), G_DIR_SEPARATOR_S, + "plugins", NULL); + syl_plugin_load_all(path); + g_free(path); + } +#else + syl_plugin_load_all(PLUGINDIR); +#endif +} + static gchar *get_socket_name(void) { static gchar *filename = NULL; diff --git a/src/mainwindow.c b/src/mainwindow.c index e0a43dd8..7d9736c7 100644 --- a/src/mainwindow.c +++ b/src/mainwindow.c @@ -79,6 +79,7 @@ #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" @@ -95,6 +96,7 @@ #include "codeconv.h" #include "about.h" #include "manual.h" +#include "update_check.h" #include "version.h" #define AC_LABEL_WIDTH 240 @@ -495,6 +497,9 @@ static void prefs_filter_open_cb (MainWindow *mainwin, 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, @@ -522,6 +527,9 @@ static void faq_open_cb (MainWindow *mainwin, static void help_cmdline_cb (MainWindow *mainwin, guint action, GtkWidget *widget); +static void update_check_cb (MainWindow *mainwin, + guint action, + GtkWidget *widget); static void scan_tree_func (Folder *folder, FolderItem *item, @@ -857,6 +865,8 @@ static GtkItemFactoryEntry mainwin_entries[] = #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, "<Separator>"}, {N_("/_Configuration/_Preferences for current account..."), NULL, prefs_account_open_cb, 0, NULL}, @@ -879,6 +889,8 @@ static GtkItemFactoryEntry mainwin_entries[] = {N_("/_Help/_FAQ/_Italian"), NULL, faq_open_cb, MANUAL_LANG_IT, NULL}, {N_("/_Help/_Command line options"), NULL, help_cmdline_cb, 0, NULL}, {N_("/_Help/---"), NULL, NULL, 0, "<Separator>"}, + {N_("/_Help/_Update check..."), NULL, update_check_cb, 0, NULL}, + {N_("/_Help/---"), NULL, NULL, 0, "<Separator>"}, {N_("/_Help/_About"), NULL, about_show, 0, NULL} }; @@ -3852,6 +3864,12 @@ static void prefs_template_open_cb(MainWindow *mainwin, guint action, 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) @@ -4027,6 +4045,12 @@ static void help_cmdline_cb(MainWindow *mainwin, guint action, help_command_line_show(); } +static void update_check_cb(MainWindow *mainwin, guint action, + GtkWidget *widget) +{ + update_check(TRUE); +} + static void scan_tree_func(Folder *folder, FolderItem *item, gpointer data) { MainWindow *mainwin = (MainWindow *)data; diff --git a/src/manage_window.c b/src/manage_window.c index fe9f2b86..b21a3b22 100644 --- a/src/manage_window.c +++ b/src/manage_window.c @@ -92,6 +92,11 @@ void manage_window_set_transient(GtkWindow *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 index 64d48617..61369d1e 100644 --- a/src/manage_window.h +++ b/src/manage_window.h @@ -52,6 +52,7 @@ 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); diff --git a/src/plugin.c b/src/plugin.c new file mode 100644 index 00000000..062200ff --- /dev/null +++ b/src/plugin.c @@ -0,0 +1,644 @@ +/* + * 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. + */ + +#include <glib.h> +#include <gmodule.h> +#include <gtk/gtk.h> + +#include "plugin.h" +#include "utils.h" +#include "folder.h" + +G_DEFINE_TYPE(SylPlugin, syl_plugin, G_TYPE_OBJECT); + +enum { + PLUGIN_LOAD, + PLUGIN_UNLOAD, + FOLDERVIEW_MENU_POPUP, + LAST_SIGNAL +}; + +#define syl_plugin_lookup_symbol(name) g_hash_table_lookup(sym_table, name) + +#define SAFE_CALL(func_ptr) { if (func_ptr) func_ptr(); } +#define SAFE_CALL_RET(func_ptr) (func_ptr ? func_ptr() : NULL) +#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_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_ARG4(func_ptr, arg1, arg2, arg3, arg4) \ + { if (func_ptr) func_ptr(arg1, arg2, arg3); } +#define SAFE_CALL_ARG4_RET(func_ptr, arg1, arg2, arg3, arg4) \ + (func_ptr ? func_ptr(arg1, arg2, arg3, arg4) : NULL) + +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); +} + +void syl_plugin_signal_connect(const gchar *name, GCallback callback, + gpointer data) +{ + g_signal_connect(plugin_obj, name, callback, data); +} + +void syl_plugin_signal_disconnect(gpointer func, gpointer data) +{ + g_signal_handlers_disconnect_by_func(plugin_obj, func, data); +} + +void syl_plugin_signal_emit(const gchar *name, ...) +{ + guint signal_id; + + 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(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)) { +#if 0 + g_warning("Version check failed. Skipping: %s", name); + g_module_close(module); + g_free(file); + return -1; +#endif + } + + 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; + + if ((d = g_dir_open(dir, 0, NULL)) == NULL) { + g_warning("failed to open directory: %s", 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; + + 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; + + 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; + } + + if (ver == SYL_PLUGIN_INTERFACE_VERSION) { + 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; +} + +const gchar *syl_plugin_get_prog_version(void) +{ + gpointer sym; + + sym = syl_plugin_lookup_symbol("prog_version"); + return (gchar *)sym; +} + +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); +} + +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, "<Main>", 6) == 0) + ifactory = syl_plugin_lookup_symbol("main_window_menu_factory"); + else if (strncmp(path, "<MailFolder>", 12) == 0) + ifactory = syl_plugin_lookup_symbol("folderview_mail_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 = "<Separator>"; + else + entry.item_type = NULL; + } else { + entry.path = "/---"; + entry.item_type = "<Separator>"; + } + 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 sym; + + sym = syl_plugin_lookup_symbol("folderview"); + return sym; +} + +FolderItem *syl_plugin_folderview_get_selected_item(void) +{ + FolderItem * (*func)(gpointer); + gpointer folderview; + + folderview = syl_plugin_folderview_get(); + if (folderview) { + func = syl_plugin_lookup_symbol("folderview_get_selected_item"); + return SAFE_CALL_ARG1_RET(func, folderview); + } + + return NULL; +} + +gpointer syl_plugin_summary_view_get(void) +{ + gpointer sym; + + sym = syl_plugin_lookup_symbol("summaryview"); + return sym; +} + +void syl_plugin_sumary_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); + } +} + +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); + } +} + +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()); +} + +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); +} diff --git a/src/plugin.h b/src/plugin.h new file mode 100644 index 00000000..6a120731 --- /dev/null +++ b/src/plugin.h @@ -0,0 +1,175 @@ +/* + * 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_H__ +#define __PLUGIN_H__ + +#include <glib.h> +#include <glib-object.h> +#include <gmodule.h> +#include <gtk/gtk.h> + +#include "procmsg.h" +#include "folder.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 0x0100 + +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); +}; + +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); + +/* 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); + +gpointer syl_plugin_main_window_get (void); +void syl_plugin_main_window_popup (gpointer mainwin); + +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); +FolderItem *syl_plugin_folderview_get_selected_item + (void); + +/* SummaryView */ +gpointer syl_plugin_summary_view_get (void); +void syl_plugin_sumary_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); + +/* MessageView */ +gpointer syl_plugin_messageview_create_with_new_window + (void); +void syl_plugin_open_message_by_new_window (MsgInfo *msginfo); + +/* 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); +void syl_plugin_inc_lock (void); +void syl_plugin_inc_unlock (void); + +#endif /* __PLUGIN_H__ */ diff --git a/src/plugin_manager.c b/src/plugin_manager.c new file mode 100644 index 00000000..53195fe9 --- /dev/null +++ b/src/plugin_manager.c @@ -0,0 +1,227 @@ +/* + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> + +#include "plugin.h" +#include "plugin_manager.h" +#include "manage_window.h" +#include "gtkutils.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); +} + +static void plugin_manager_create(void) +{ + GtkWidget *window; + GtkWidget *vbox; + 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, + &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); + + 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); + 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 00000000..c139de2f --- /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_common_dialog.c b/src/prefs_common_dialog.c index eeedc28b..88da4d17 100644 --- a/src/prefs_common_dialog.c +++ b/src/prefs_common_dialog.c @@ -1,6 +1,6 @@ /* * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2008 Hiroyuki Yamamoto + * 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 @@ -233,6 +233,12 @@ static struct Extcmd { GtkWidget *button_extsend; } extcmd; +static struct UpdateCheck { + GtkWidget *checkbtn_autoupdate; + GtkWidget *checkbtn_useproxy; + GtkWidget *entry_proxyhost; +} update_check; + static struct Advanced { GtkWidget *checkbtn_strict_cache_check; @@ -540,6 +546,14 @@ static PrefsUIData ui_data[] = { {"ext_sendmail_cmd", &extcmd.entry_extsend, prefs_set_data_from_entry, prefs_set_entry}, + /* 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}, + /* Advanced */ {"strict_cache_check", &advanced.checkbtn_strict_cache_check, prefs_set_data_from_toggle, prefs_set_toggle}, @@ -567,6 +581,7 @@ static void prefs_privacy_create (void); static void prefs_details_create (void); static GtkWidget *prefs_other_create (void); static GtkWidget *prefs_extcmd_create (void); +static GtkWidget *prefs_update_create (void); static GtkWidget *prefs_advanced_create (void); static void prefs_common_set_encoding_optmenu (GtkOptionMenu *optmenu, @@ -2168,6 +2183,7 @@ static void prefs_details_create(void) GtkWidget *other_wid; GtkWidget *extcmd_wid; + GtkWidget *update_wid; GtkWidget *advanced_wid; vbox1 = gtk_vbox_new (FALSE, VSPACING); @@ -2268,6 +2284,10 @@ static void prefs_details_create(void) extcmd_wid = prefs_extcmd_create(); gtk_box_pack_start(GTK_BOX(vbox_tab), extcmd_wid, FALSE, FALSE, 0); + 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); + 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); @@ -2581,6 +2601,53 @@ static GtkWidget *prefs_extcmd_create(void) return vbox1; } +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; +} + static GtkWidget *prefs_advanced_create(void) { GtkWidget *vbox1; diff --git a/src/update_check.c b/src/update_check.c new file mode 100644 index 00000000..5979ea5f --- /dev/null +++ b/src/update_check.c @@ -0,0 +1,255 @@ +/* + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> + +#include <stdlib.h> +#include <unistd.h> + +#include "update_check.h" +#include "manage_window.h" +#include "gtkutils.h" +#include "alertpanel.h" +#include "prefs_common.h" +#include "socket.h" +#include "utils.h" +#include "version.h" + + +static gboolean compare_version(gint major, gint minor, gint micro, + const gchar *extra) +{ + debug_print("comparing %d.%d.%d.%s <> " VERSION "\n", major, minor, micro, extra ? extra : ""); + + if (major > MAJOR_VERSION) + return TRUE; + if (major < MAJOR_VERSION) + return FALSE; + if (minor > MINOR_VERSION) + return TRUE; + if (minor < MINOR_VERSION) + return FALSE; + if (micro > MICRO_VERSION) + return TRUE; + if (micro < MICRO_VERSION) + 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 = g_strdup(vers[3]); + } + } + } + } + g_strfreev(vers); +} + +static void update_dialog(const gchar *new_ver, gboolean manual) +{ + gchar buf[1024]; + AlertValue val; + + if (new_ver) + g_snprintf(buf, sizeof(buf), "%s\n\n%s -> %s", + _("The newer version of Sylpheed found.\n" + "Upgrade now?"), + VERSION, new_ver); + else + g_snprintf(buf, sizeof(buf), "%s", + _("The newer version of Sylpheed 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) { + open_uri(HOMEPAGE_URI, prefs_common.uri_cmd); + } + if (val & G_ALERTDISABLE) { + prefs_common.auto_update_check = FALSE; + } +} + +static gint child_stdout; + +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 show_dialog_always = (gboolean)data; + gchar buf[BUFFSIZE]; + ssize_t size; + + debug_print("update_check_cb\n"); + + if (!child_stdout) { + g_spawn_close_pid(pid); + return; + } + + size = read(child_stdout, buf, sizeof(buf) - 1); + if (size < 0) { + fd_close(child_stdout); + child_stdout = 0; + g_spawn_close_pid(pid); + return; + } + buf[size] = '\0'; + + fd_close(child_stdout); + child_stdout = 0; + g_spawn_close_pid(pid); + + lines = g_strsplit(buf, "\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 (!strcmp(key, "RELEASE")) { + parse_version_string(val, &major, &minor, µ, + &extra); + result = compare_version(major, minor, micro, extra); + } else if (!cur_ver_is_release && !strcmp(key, "DEVEL")) { + parse_version_string(val, &major, &minor, µ, + &extra); + result = compare_version(major, minor, micro, extra); + } + + 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\n", new_ver); + g_free(extra); + g_free(key); + break; + } + g_free(extra); + g_free(key); + } + + g_strfreev(lines); + + if (result) + update_dialog(new_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.")); + } + g_free(new_ver); +} + +void update_check(gboolean show_dialog_always) +{ + gchar *cmdline[6] = {"curl", "--silent"}; + GPid pid; + GError *error = NULL; + + if (child_stdout > 0) { + debug_print("update check is in progress\n"); + return; + } + + child_stdout = 0; + + debug_print("update_check: getting latest version from http://sylpheed.sraoss.jp/version.txt\n"); + + cmdline[2] = "http://sylpheed.sraoss.jp/version.txt?"; + if (prefs_common.use_http_proxy && prefs_common.http_proxy_host && + prefs_common.http_proxy_host[0] != '\0') { + cmdline[3] = "--proxy"; + cmdline[4] = prefs_common.http_proxy_host; + cmdline[5] = NULL; + } else + cmdline[3] = 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) { + fd_close(child_stdout); + child_stdout = 0; + } + return; + } + + g_child_watch_add(pid, update_check_cb, (gpointer)show_dialog_always); +} diff --git a/src/update_check.h b/src/update_check.h new file mode 100644 index 00000000..532a407a --- /dev/null +++ b/src/update_check.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 __UPDATE_CHECK_H__ +#define __UPDATE_CHECK_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +void update_check(gboolean show_dialog_always); + +#endif /* __PLUGIN_MANAGER_H__ */ diff --git a/src/version.h.in b/src/version.h.in index d7fac0aa..8e7affa6 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,6 +1,6 @@ /* * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2006 Hiroyuki Yamamoto + * 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 @@ -22,6 +22,10 @@ #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 #endif /* __VERSION_H__ */ |