[Commits] Makefile.am 1.3 1.4 callbacks.c 1.13 1.14 contactwindow.c 1.11 1.12 extension-loader.c 1.1 1.2 extension-loader.h 1.1 1.2 extension.c 1.1 NONE extension.h 1.1 1.2 gtk-utils.c 1.4 1.5 mainwindow.c 1.5 1.6 mainwindow.h 1.3 1.4

miras at claws-mail.org miras at claws-mail.org
Sat Dec 10 00:01:23 CET 2011


Update of /home/claws-mail/contacts/src
In directory claws-mail:/tmp/cvs-serv2085/src

Modified Files:
	Makefile.am callbacks.c contactwindow.c extension-loader.c 
	extension-loader.h extension.h gtk-utils.c mainwindow.c 
	mainwindow.h 
Removed Files:
	extension.c 
Log Message:
2011-12-09 [mir]	0.6.0cvs38

	* claws-contacts.pc.in
	* configure.ac
	* extensions/Makefile.am
	* extensions/example/Makefile.am
	* extensions/example/configure.ac
	* extensions/example/config/Makefile.am
	* extensions/example/src/Makefile.am
	* extensions/example/src/example-extension.c
	* extensions/export/ldifexport_extension.c
	* plugins/Makefile.am
	* plugins/example/Makefile.am
	* plugins/example/configure.ac
	* src/Makefile.am
	* src/callbacks.c
	* src/contactwindow.c
	* src/extension-loader.c
	* src/extension-loader.h
	* src/extension.c
	* src/extension.h
	* src/gtk-utils.c
	* src/mainwindow.c
	* src/mainwindow.h
	    Fix a number of bugs
	    Refined the build system
	    Added a plugable extension framework 

--- extension.c DELETED ---

Index: mainwindow.h
===================================================================
RCS file: /home/claws-mail/contacts/src/mainwindow.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- mainwindow.h	30 Nov 2011 23:35:12 -0000	1.3
+++ mainwindow.h	9 Dec 2011 23:01:21 -0000	1.4
@@ -59,7 +59,11 @@
     GtkWidget*          window;
     GtkWidget*          left_scroll;
     GtkWidget*          right_scroll;
+    GtkAccelGroup*      accel;
     GtkWidget*          menu;
+    GtkWidget*          tools_menu;
+    GtkWidget*          help_menu;
+    GtkWidget*          file_menu;
     GtkWidget*          toolbar;
     GtkWidget*          abook_list;
     GtkWidget*          contact_list;
@@ -80,6 +84,7 @@
     guint               source_id;
     gchar*              default_book;
     gboolean            compose_mode;
+    gboolean            use_extensions;
 } MainWindow;
 
 enum {

Index: contactwindow.c
===================================================================
RCS file: /home/claws-mail/contacts/src/contactwindow.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -d -r1.11 -r1.12
--- contactwindow.c	30 Nov 2011 16:37:59 -0000	1.11
+++ contactwindow.c	9 Dec 2011 23:01:21 -0000	1.12
@@ -47,6 +47,7 @@
 #include "gtk-utils.h"
 #include "mainwindow.h"
 #include "callbacks.h"
+#include "extension-loader.h"
 
 #define CONTACT_WINDOW_WIDTH 480
 #define CONTACT_WINDOW_HEIGHT 400
@@ -67,7 +68,7 @@
 } ContactEntryData;
 
 typedef struct {
-	MainWindow* main;
+	MainWindow*			main;
 	AddressBook* 		abook;
 	Plugin*				plugin;
 	Contact*			contact;
@@ -151,10 +152,16 @@
     GtkTreeSelection* row;
 
 	if (contact) {
-		if (contact_is_new)
+		if (contact_is_new) {
 			cw->plugin->set_contact(cw->abook, contact, &err);
-		else
+			if (cw->main->use_extensions) {
+				debug_print("AFTER_INIT_CONTACT_HOOK\n");
+				run_hook_callbacks(EXTENSION_AFTER_INIT_CONTACT_HOOK, contact);
+			}
+		}
+		else {
 			err = contact_write_to_backend(cw->plugin, cw->abook, cw->old, contact);
+		}
 		if (err) {
 			show_message(cw->window, GTK_UTIL_MESSAGE_WARNING, "%s", err);
 			g_free(err);
@@ -447,19 +454,35 @@
 	gchar* error = NULL;
 	
 	if (strcmp(label, GTK_STOCK_OK) == 0) {
+		if (cw->main->use_extensions) {
+			debug_print("BEFORE_ADD_CONTACT_HOOK\n");
+			run_hook_callbacks(EXTENSION_BEFORE_ADD_CONTACT_HOOK, cw->contact);
+		}
 		save_contact(cw, &error);
 		if (error) {
 			show_message(cw->window, GTK_UTIL_MESSAGE_ERROR, "%s", error);
 			g_free(error);
 		}
+		if (cw->main->use_extensions) {
+			debug_print("AFTER_ADD_CONTACT_HOOK\n");
+			run_hook_callbacks(EXTENSION_AFTER_ADD_CONTACT_HOOK, cw->contact);
+		}
 		quit_cb(widget, data);
 	}
 	else if (strcmp(label, GTK_STOCK_APPLY) == 0) {
+		if (cw->main->use_extensions) {
+			debug_print("AFTER_ADD_CONTACT_HOOK\n");
+			run_hook_callbacks(EXTENSION_AFTER_ADD_CONTACT_HOOK, cw->contact);
+		}
 		save_contact(cw, &error);
 		if (error) {
 			show_message(cw->window, GTK_UTIL_MESSAGE_ERROR, "%s", error);
 			g_free(error);
 		}
+		if (cw->main->use_extensions) {
+			debug_print("AFTER_ADD_CONTACT_HOOK\n");
+			run_hook_callbacks(EXTENSION_AFTER_ADD_CONTACT_HOOK, cw->contact);
+		}
 	}
 	else {
 		/* we should never end here */

Index: extension.h
===================================================================
RCS file: /home/claws-mail/contacts/src/extension.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- extension.h	30 Nov 2011 23:35:12 -0000	1.1
+++ extension.h	9 Dec 2011 23:01:21 -0000	1.2
@@ -38,20 +38,29 @@
 G_BEGIN_DECLS
 
 #include "mainwindow.h"
+#include "plugin.h"
 
+/* Available hooks */
 typedef enum {
+	/* After main window but before plugins */
 	EXTENSION_BEFORE_INIT_HOOK,
+	/* After plugins */
 	EXTENSION_AFTER_INIT_HOOK,
+	EXTENSION_BEFORE_EDIT_ABOOK_HOOK,
+	EXTENSION_AFTER_EDIT_ABOOK_HOOK,
 	EXTENSION_BEFORE_OPEN_ABOOK_HOOK,
 	EXTENSION_AFTER_OPEN_ABOOK_HOOK,
 	EXTENSION_BEFORE_CLOSE_ABOOK_HOOK,
 	EXTENSION_AFTER_CLOSE_ABOOK_HOOK,
 	EXTENSION_BEFORE_DELETE_ABOOK_HOOK,
 	EXTENSION_AFTER_DELETE_ABOOK_HOOK,
+	/* Object will be searched address book (basic_search) */
 	EXTENSION_BEFORE_SEARCH_ABOOK_HOOK,
+	/* Object will be a GSList of found contact(s) */
 	EXTENSION_AFTER_SEARCH_ABOOK_HOOK,
 	EXTENSION_BEFORE_ADD_ABOOK_HOOK,
 	EXTENSION_AFTER_ADD_ABOOK_HOOK,
+	/* Object will be NULL */
 	EXTENSION_BEFORE_INIT_ABOOK_HOOK,
 	EXTENSION_AFTER_INIT_ABOOK_HOOK,
 	EXTENSION_BEFORE_OPEN_CONTACT_HOOK,
@@ -60,19 +69,116 @@
 	EXTENSION_AFTER_CLOSE_CONTACT_HOOK,
 	EXTENSION_BEFORE_DELETE_CONTACT_HOOK,
 	EXTENSION_AFTER_DELETE_CONTACT_HOOK,
+	/* Object will be a Contact containing search data */
 	EXTENSION_BEFORE_SEARCH_CONTACT_HOOK,
+	/* Object will be a GSList of found contact(s) */
 	EXTENSION_AFTER_SEARCH_CONTACT_HOOK,
 	EXTENSION_BEFORE_ADD_CONTACT_HOOK,
 	EXTENSION_AFTER_ADD_CONTACT_HOOK,
+	/* Object will be NULL */
 	EXTENSION_BEFORE_INIT_CONTACT_HOOK,
 	EXTENSION_AFTER_INIT_CONTACT_HOOK,
 	EXTENSION_HOOK_N
 } ExtensionHook;
 
+typedef enum {
+	CONTACTS_NONE,
+	CONTACTS_MAIN_MENU,
+	CONTACTS_CONTACT_MENU,
+	CONTACTS_ADDRESSBOOK_MENU
+} ContactsMenu;
+
+typedef struct {
+	ContactsMenu	menu;
+	gboolean		submenu;
+	const gchar*	parent;
+	const gchar*	sublabel;
+/*	GtkWidget*		root;*/
+} MenuItem;
+
 typedef void (*HOOKFUNC) (const MainWindow* mainwindow, gpointer object);
 
 /* Any extension must implement these functions */
 
+/**
+ * The main application will call this function after loading the
+ * extension providing a uniq id for the extension which is to be
+ * used for further references
+ * @param id uniq id provided by main application
+ * @return 0 if success 1 otherwise
+ */
+gint extension_init(guint id);
+
+/**
+ * Called by main application when the extension should be unloaded
+ * @return TRUE if success FALSE otherwise
+ */
+gboolean extension_done(void);
+
+/**
+ * Called by main application to ensure extension license is compatible
+ * @return license
+ */
+const gchar* extension_license(void);
+
+/**
+ * Called by main application to get name of extension
+ * @return name
+ */
+const gchar* extension_name(void);
+
+/**
+ * Called by main application to get extension's describtion
+ * @return description
+ */
+const gchar* extension_describtion(void);
+
+/* Functions provided by claws-contacts */
+
+/**
+ * Extensions should call this function to register a callback hook
+ * @param id uniq id identifying the extension provided by the
+ * main application when extension_init was called
+ * @param hook_type what kind of hook to subscribe
+ * @param callback a pointer to the callback function
+ * @param error a place holder for error messages
+ * @return 0 if success 1 otherwise
+ */
+gint register_hook_function(guint id,
+						    ExtensionHook hook_type,
+						    HOOKFUNC callback,
+						    gchar** error);
+
+/**
+ * Extensions should call this function to unregister a previous
+ * registred callback hook
+ * @param id uniq id identifying the extension provided by the
+ * main application when extension_init was called
+ * @param hook_type what kind of hook to unsubscribe
+ * @param error a place holder for error messages
+ */
+void unregister_hook_function(guint id,
+						      ExtensionHook hook_type,
+						      gchar** error);
+
+/**
+ * If an extension wants to add an new MenuItem to the application menu
+ * it can call this function. The predefined top-level menus 'File',
+ * 'Tools' and 'Help' is accessable to extensions. If parent is not
+ * found a new menu will be craeted with the name 'parent'
+ * @param id uniq id identifying the extension provided by the
+ * main application when extension_init was called
+ * @param image_menu GtkImageMenuItem
+ * @param menu_item 'Menu' to attach to. Do not free!
+ * @return TRUE if error FALSE otherwise
+ */
+/*
+gboolean add_menu_item(guint id, GtkImageMenuItem* image_menu, MenuItem* menu_item);
+*/
+MenuItem* menu_item_new(void);
+
+gboolean add_menu_item(GtkImageMenuItem* image_menu, MenuItem* menu_item);
+
 G_END_DECLS
 
 #endif

Index: mainwindow.c
===================================================================
RCS file: /home/claws-mail/contacts/src/mainwindow.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- mainwindow.c	30 Nov 2011 23:35:12 -0000	1.5
+++ mainwindow.c	9 Dec 2011 23:01:21 -0000	1.6
@@ -130,84 +130,82 @@
 }
                                         
 static void create_menu(MainWindow* mainwindow) {
-    GtkAccelGroup* accel;
     GtkWidget *item_space,
-              *file_menu, *file, *file_quit, *file_plugin, *page_setup,
+              *file, *file_quit, *file_plugin, *page_setup,
               *abook_menu, *abook, *abook_new, *abook_open, 
               *abook_delete, *abook_close, *abook_edit,
               *contact_menu, *contact, *contact_new, *contact_edit,
-              *contact_print, *contact_delete,
-              *tools_menu, *tools, *tools_prefs, *tools_attribs,
-              *help_menu, *help, *help_text, *help_about;
+              *contact_print, *contact_delete, *tools, *tools_prefs,
+              *tools_attribs, *help, *help_text, *help_about;
 
-    accel = g_object_new(GTK_TYPE_ACCEL_GROUP, NULL);
-    gtk_window_add_accel_group(GTK_WINDOW(mainwindow->window), accel);
+    mainwindow->accel = g_object_new(GTK_TYPE_ACCEL_GROUP, NULL);
+    gtk_window_add_accel_group(GTK_WINDOW(mainwindow->window), mainwindow->accel);
     mainwindow->menu = gtk_menu_bar_new();
 
-    file_menu = gtk_menu_new();
+    mainwindow->file_menu = gtk_menu_new();
 
     file_plugin = gtk_image_menu_item_new_with_mnemonic("_Manage plugins");
-    gtk_image_menu_item_set_accel_group(GTK_IMAGE_MENU_ITEM(file_plugin), accel);
-    gtk_widget_add_accelerator(file_plugin, "activate", accel,
+    gtk_image_menu_item_set_accel_group(GTK_IMAGE_MENU_ITEM(file_plugin), mainwindow->accel);
+    gtk_widget_add_accelerator(file_plugin, "activate", mainwindow->accel,
             GDK_M, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
     g_signal_connect(file_plugin, "activate",
             G_CALLBACK(file_plugin_cb), mainwindow);
-    gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), file_plugin);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mainwindow->file_menu), file_plugin);
 
     page_setup = gtk_image_menu_item_new_with_mnemonic("_Page setup");
-    gtk_image_menu_item_set_accel_group(GTK_IMAGE_MENU_ITEM(page_setup), accel);
-    gtk_widget_add_accelerator(page_setup, "activate", accel,
+    gtk_image_menu_item_set_accel_group(GTK_IMAGE_MENU_ITEM(page_setup), mainwindow->accel);
+    gtk_widget_add_accelerator(page_setup, "activate", mainwindow->accel,
             GDK_P, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
     g_signal_connect(page_setup, "activate",
             G_CALLBACK(printer_page_setup_cb), mainwindow);
-    gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), page_setup);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mainwindow->file_menu), page_setup);
 
     item_space = gtk_separator_menu_item_new();
-    gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), item_space); 
+    gtk_menu_shell_append(GTK_MENU_SHELL(mainwindow->file_menu), item_space); 
 
-    file_quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel);
+    file_quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, mainwindow->accel);
     g_signal_connect(file_quit, "activate", G_CALLBACK(menu_quit_cb), mainwindow);
-    gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), file_quit);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mainwindow->file_menu), file_quit);
 
     file = gtk_menu_item_new_with_mnemonic("_File");
     gtk_menu_shell_append(GTK_MENU_SHELL(mainwindow->menu), file);
-    gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), file_menu);
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), mainwindow->file_menu);
 
     abook_menu = gtk_menu_new();
     
     abook_new = gtk_image_menu_item_new_with_mnemonic("_New");
-    gtk_image_menu_item_set_accel_group(GTK_IMAGE_MENU_ITEM(abook_new), accel);
-    gtk_widget_add_accelerator(abook_new, "activate", accel,
+    gtk_image_menu_item_set_accel_group(GTK_IMAGE_MENU_ITEM(abook_new), mainwindow->accel);
+    gtk_widget_add_accelerator(abook_new, "activate", mainwindow->accel,
             GDK_N, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
     g_signal_connect(abook_new, "activate",
             G_CALLBACK(abook_new_cb), mainwindow);
     gtk_menu_shell_append(GTK_MENU_SHELL(abook_menu), abook_new);
 
-    abook_edit = gtk_image_menu_item_new_from_stock(GTK_STOCK_EDIT, accel);
-    gtk_widget_add_accelerator(abook_edit, "activate", accel,
+    abook_edit = gtk_image_menu_item_new_from_stock(GTK_STOCK_EDIT, mainwindow->accel);
+    gtk_widget_add_accelerator(abook_edit, "activate", mainwindow->accel,
             GDK_E, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
     g_signal_connect(abook_edit, "activate",
             G_CALLBACK(abook_edit_cb), mainwindow);
     gtk_menu_shell_append(GTK_MENU_SHELL(abook_menu), abook_edit);
 
     abook_open = gtk_image_menu_item_new_with_mnemonic("_Open");
-    gtk_image_menu_item_set_accel_group(GTK_IMAGE_MENU_ITEM(abook_open), accel);
-    gtk_widget_add_accelerator(abook_open, "activate", accel,
+    gtk_image_menu_item_set_accel_group(GTK_IMAGE_MENU_ITEM(abook_open), mainwindow->accel);
+    gtk_widget_add_accelerator(abook_open, "activate", mainwindow->accel,
             GDK_O, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
     g_signal_connect(abook_open, "activate",
             G_CALLBACK(abook_open_cb), mainwindow);
     gtk_menu_shell_append(GTK_MENU_SHELL(abook_menu), abook_open);
 
     abook_close = gtk_image_menu_item_new_with_mnemonic("_Close");
-    gtk_image_menu_item_set_accel_group(GTK_IMAGE_MENU_ITEM(abook_close), accel);
-    gtk_widget_add_accelerator(abook_close, "activate", accel,
+    gtk_image_menu_item_set_accel_group(GTK_IMAGE_MENU_ITEM(abook_close), mainwindow->accel);
+    gtk_widget_add_accelerator(abook_close, "activate", mainwindow->accel,
             GDK_L, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
     g_signal_connect(abook_close, "activate",
             G_CALLBACK(abook_close_cb), mainwindow);
     gtk_menu_shell_append(GTK_MENU_SHELL(abook_menu), abook_close);
 
-    abook_delete = gtk_image_menu_item_new_from_stock(GTK_STOCK_DELETE, accel);
-    gtk_widget_add_accelerator(abook_delete, "activate", accel,
+    abook_delete = gtk_image_menu_item_new_from_stock(GTK_STOCK_DELETE, mainwindow->accel);
+    gtk_widget_add_accelerator(abook_delete, "activate", mainwindow->accel,
             GDK_D, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
     g_signal_connect(abook_delete, "activate",
             G_CALLBACK(abook_delete_cb), mainwindow);
@@ -220,30 +218,30 @@
     contact_menu = gtk_menu_new();
 
     contact_new = gtk_image_menu_item_new_with_mnemonic("_New");
-    gtk_image_menu_item_set_accel_group(GTK_IMAGE_MENU_ITEM(contact_new), accel);
-    gtk_widget_add_accelerator(contact_new, "activate", accel,
+    gtk_image_menu_item_set_accel_group(GTK_IMAGE_MENU_ITEM(contact_new), mainwindow->accel);
+    gtk_widget_add_accelerator(contact_new, "activate", mainwindow->accel,
             GDK_N, GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
     g_signal_connect(contact_new, "activate",
             G_CALLBACK(contact_new_cb), mainwindow);
     gtk_menu_shell_append(GTK_MENU_SHELL(contact_menu), contact_new);
 
-    contact_edit = gtk_image_menu_item_new_from_stock(GTK_STOCK_EDIT, accel);
-    gtk_widget_add_accelerator(contact_edit, "activate", accel,
+    contact_edit = gtk_image_menu_item_new_from_stock(GTK_STOCK_EDIT, mainwindow->accel);
+    gtk_widget_add_accelerator(contact_edit, "activate", mainwindow->accel,
             GDK_E, GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
     g_signal_connect(contact_edit, "activate",
             G_CALLBACK(contact_edit_cb), mainwindow);
     gtk_menu_shell_append(GTK_MENU_SHELL(contact_menu), contact_edit);
 
-    contact_print = gtk_image_menu_item_new_from_stock(GTK_STOCK_PRINT, accel);
-    gtk_widget_add_accelerator(contact_print, "activate", accel,
+    contact_print = gtk_image_menu_item_new_from_stock(GTK_STOCK_PRINT, mainwindow->accel);
+    gtk_widget_add_accelerator(contact_print, "activate", mainwindow->accel,
             GDK_P, GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
     g_signal_connect(contact_print, "activate",
             G_CALLBACK(contact_print_prepare_cb), mainwindow);
     gtk_menu_shell_append(GTK_MENU_SHELL(contact_menu), contact_print);
 
     contact_delete = gtk_image_menu_item_new_from_stock(
-            GTK_STOCK_DELETE, accel);
-    gtk_widget_add_accelerator(contact_delete, "activate", accel,
+            GTK_STOCK_DELETE, mainwindow->accel);
+    gtk_widget_add_accelerator(contact_delete, "activate", mainwindow->accel,
             GDK_D, GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
     g_signal_connect(contact_delete, "activate",
             G_CALLBACK(contact_delete_cb), mainwindow);
@@ -253,36 +251,36 @@
     gtk_menu_shell_append(GTK_MENU_SHELL(mainwindow->menu), contact);
     gtk_menu_item_set_submenu(GTK_MENU_ITEM(contact), contact_menu);
 
-    tools_menu = gtk_menu_new();
+    mainwindow->tools_menu = gtk_menu_new();
 
     tools_prefs = gtk_image_menu_item_new_from_stock(
-            GTK_STOCK_PREFERENCES, accel);
-    gtk_widget_add_accelerator(tools_prefs, "activate", accel,
+            GTK_STOCK_PREFERENCES, mainwindow->accel);
+    gtk_widget_add_accelerator(tools_prefs, "activate", mainwindow->accel,
             GDK_P, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
     g_signal_connect(tools_prefs, "activate", 
             G_CALLBACK(tools_prefs_cb), mainwindow);
-    gtk_menu_shell_append(GTK_MENU_SHELL(tools_menu), tools_prefs);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mainwindow->tools_menu), tools_prefs);
 
     tools_attribs = gtk_image_menu_item_new_with_mnemonic("_Attributes");
-    gtk_image_menu_item_set_accel_group(GTK_IMAGE_MENU_ITEM(tools_attribs), accel);
-    gtk_widget_add_accelerator(tools_attribs, "activate", accel,
+    gtk_image_menu_item_set_accel_group(GTK_IMAGE_MENU_ITEM(tools_attribs), mainwindow->accel);
+    gtk_widget_add_accelerator(tools_attribs, "activate", mainwindow->accel,
             GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
     g_signal_connect(tools_attribs, "activate", 
             G_CALLBACK(tools_attribs_cb), mainwindow);
-    gtk_menu_shell_append(GTK_MENU_SHELL(tools_menu), tools_attribs);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mainwindow->tools_menu), tools_attribs);
 
     tools = gtk_menu_item_new_with_mnemonic("_Tools");
     gtk_menu_shell_append(GTK_MENU_SHELL(mainwindow->menu), tools);
-    gtk_menu_item_set_submenu(GTK_MENU_ITEM(tools), tools_menu);
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(tools), mainwindow->tools_menu);
 
-    help_menu = gtk_menu_new();
-    help_text = gtk_image_menu_item_new_from_stock(GTK_STOCK_HELP, accel);
-    help_about = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, accel);
-    gtk_menu_shell_append(GTK_MENU_SHELL(help_menu), help_text);
-    gtk_menu_shell_append(GTK_MENU_SHELL(help_menu), help_about);
+    mainwindow->help_menu = gtk_menu_new();
+    help_text = gtk_image_menu_item_new_from_stock(GTK_STOCK_HELP, mainwindow->accel);
+    help_about = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, mainwindow->accel);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mainwindow->help_menu), help_text);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mainwindow->help_menu), help_about);
     help = gtk_menu_item_new_with_mnemonic("_Help");
     gtk_menu_shell_append(GTK_MENU_SHELL(mainwindow->menu), help);
-    gtk_menu_item_set_submenu(GTK_MENU_ITEM(help), help_menu);
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(help), mainwindow->help_menu);
     g_signal_connect(help_about, "activate",
             G_CALLBACK(show_about), mainwindow);
     g_signal_connect(help_text, "activate",
@@ -638,14 +636,19 @@
     g_string_free(msg, TRUE);
 }
 
-void application_start(gboolean compose, gboolean avoid_extensions) {
+void application_start(gboolean compose, gboolean no_extensions) {
     MainWindow* mainwindow = g_new0(MainWindow, 1);
+    gchar* error;
     
     mainwindow->compose_mode = compose;
+    mainwindow->use_extensions = ! no_extensions;
     mainwindow_create(mainwindow);
 
-    if (! avoid_extensions) {
+    if (mainwindow->use_extensions) {
+        debug_set_mode(TRUE);
         init_hooks(mainwindow);
+        load_extensions(&error);
+        debug_set_mode(FALSE);
     }
 
     GList* pixmaps = load_pixmaps();

Index: Makefile.am
===================================================================
RCS file: /home/claws-mail/contacts/src/Makefile.am,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- Makefile.am	30 Nov 2011 23:35:12 -0000	1.3
+++ Makefile.am	9 Dec 2011 23:01:21 -0000	1.4
@@ -17,7 +17,7 @@
 AM_CPPFLAGS = \
 		-DG_LOG_DOMAIN=\"Claws-Contacts\" \
 		-DPLUGINDIR=\"@PLUGINDIR@\" \
-		-DEXTENSIONDIR=\"@EXTENSIONDIR@\" \
+		-DEXTENSIONDIR=\"@CLAWS_CONTACTS_EXTENSIONDIR@\" \
 		$(LIBGCRYPT_CFLAGS)
 
 claws_contacts_SOURCES = \
@@ -38,7 +38,6 @@
 		    printing.h \
 		    settings.c \
 		    settings.h \
-			extension.c \
 			extension-loader.c \
 			extension-loader.h
 

Index: extension-loader.c
===================================================================
RCS file: /home/claws-mail/contacts/src/extension-loader.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- extension-loader.c	30 Nov 2011 23:35:12 -0000	1.1
+++ extension-loader.c	9 Dec 2011 23:01:21 -0000	1.2
@@ -35,38 +35,89 @@
 
 #include <glib.h>
 #include <glib/gi18n.h>
+#include <gtk/gtk.h>
 
 #include "mainwindow.h"
 #include "extension.h"
 #include "extension-loader.h"
+#include "utils.h"
+#include "gtk-utils.h"
 
-static const GSList* active_hooks[EXTENSION_HOOK_N];
+static GSList* active_hooks[EXTENSION_HOOK_N];
+static GSList* user_menu_items = NULL;
+static GtkWidget* abook_context = NULL;
+static GtkWidget* contact_context = NULL;
+/* The number corresponts to the default number of GtkMenu - 1 */
+static guint menu_count = 4;
+/* The number corresponts to the default number of GtkMenu - separator and quit */
+static guint file_menu_count = 2;
+static GHashTable* extensions = NULL;
 static const MainWindow* mainwindow = NULL;
-static gint NEXT_ID = 0;
+static guint NEXT_ID = 1;
+static const gchar* valid_license[] = {
+	"gpl3",
+	"gpl3+",
+	"gpl2+",
+	NULL
+};
 
-static gint next_id() {
-	gint id = NEXT_ID;
+typedef struct {
+	guint			id;
+	HOOKFUNC		callback;
+	ExtensionHook	type;
+} Callback;
+	
+static guint next_id() {
+	guint id = NEXT_ID;
 	NEXT_ID++;
 	
 	return id;
 }
 
-Extension* extension_new(HOOKFUNC callback) {
+static Extension* extension_new(const gchar* filename) {
 	Extension* extension = g_new0(Extension, 1);
-	extension->callback = callback;
+	extension->filename = g_strdup(filename);
 	extension->id = next_id();
 	
 	return extension;
 }
 
-void extension_free(void) {
+static gboolean compatible_license(Extension* extension) {
+	gboolean res = TRUE;
+	gchar* license;
+	gchar** valid = (gchar **) valid_license;
+	
+	license = g_utf8_strdown(extension->license(), -1);
+	while(*valid && res) {
+		if (utf8_collate(license, *valid++) == 0)
+			res = FALSE;
+	}
+	
+	g_free(license);
+	return res;
 }
 
-gint install_hook_function(ExtensionHook hook_type,
-						   HOOKFUNC callback,
-						   gchar** error) {
+void extension_free(gpointer extension) {
+	Extension* ext = (Extension *) extension;
+	if (! ext)
+		return;
+	
+	debug_print("%s: Unloading\n", ext->name ? ext->name() : ext->filename);
+	g_free(ext->filename);
+	if (ext->module) {
+		if (ext->done)
+			ext->done();
+		g_module_close(ext->module);
+	}
+	g_free(ext);
+}
+
+gint register_hook_function(guint id,
+						    ExtensionHook hook_type,
+						    HOOKFUNC callback,
+						    gchar** error) {
 	gint result = 0;
-	Extension* extension;
+	Callback* cb;
 
 	if (! callback) {
 		if (error)
@@ -74,35 +125,38 @@
 		return TRUE;
 	}
 	
-	extension = extension_new(callback);
-	active_hooks[hook_type] = g_slist_prepend(active_hooks[hook_type], extension);
+	debug_print("Registrering hook function [%d]\n", id); 
+	cb = g_new0(Callback, 1);
+	cb->id = id;
+	cb->callback = callback;
+	cb->type = hook_type;
+	active_hooks[hook_type] = g_slist_prepend(active_hooks[hook_type], cb);
 	
 	return result;
 }
 
-void uninstall_hook_function(gint id, gchar** error) {
-	ExtensionHook i;
-	Extension* ext = NULL;
+void unregister_hook_function(guint id,
+						      ExtensionHook hook_type,
+						      gchar** error) {
+	Callback* cb = NULL;
 	GSList* cur;
 	gboolean found = FALSE;
 	
-	for (i = EXTENSION_BEFORE_INIT_HOOK; i < EXTENSION_HOOK_N && ! ext; i++) {
-		cur = active_hooks[i];
-		while (cur && ! found) {
-			ext = (Extension *) cur->data;
-			if (ext && ext->id == id)
-				found = TRUE;
-			else {
-				ext = NULL;
-				cur = cur->next;
-			}
+	cur = active_hooks[hook_type];
+	while (cur && ! found) {
+		cb = (Callback *) cur->data;
+		if (cb && cb->id == id)
+			found = TRUE;
+		else {
+			cb = NULL;
+			cur = cur->next;
 		}
 	}
 	
-	if (ext) {
-		active_hooks[i] = g_slist_remove(active_hooks[i], ext);
-		g_free(ext);
-		ext = NULL;
+	if (cb) {
+		debug_print("Unregistrering hook function [%d]\n", cb->id); 
+		active_hooks[hook_type] = g_slist_remove(active_hooks[hook_type], cb);
+		g_free(cb);
 	}
 	else {
 		if (error)
@@ -116,18 +170,418 @@
 	mainwindow = (const MainWindow *) main;
 	for (i = EXTENSION_BEFORE_INIT_HOOK; i < EXTENSION_HOOK_N; i++)
 		active_hooks[i] = NULL;
-
+	extensions = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, extension_free);
 }
 
+void done_hooks() {
+	ExtensionHook i;
+	GSList* cur;
+	
+	for (i = EXTENSION_BEFORE_INIT_HOOK; i < EXTENSION_HOOK_N; i++) {
+		cur = active_hooks[i];
+		while (cur) {
+			g_free(cur->data);
+			cur = cur->next;
+		}
+		g_slist_free(active_hooks[i]);
+		active_hooks[i] = NULL;
+	}
+			
+}	
+
 void run_hook_callbacks(ExtensionHook hook, gpointer object) {
 	GSList* cur;
 	
-	if (! object)
+	if (! (EXTENSION_BEFORE_INIT_HOOK || EXTENSION_AFTER_INIT_HOOK) && ! object)
 		return;
-		
+	
 	for (cur = active_hooks[hook]; cur; cur = g_slist_next(cur)) {
-		Extension* ext = (Extension *) cur->data;
-		if (ext && ext->callback)
-			ext->callback(mainwindow, object);
+		Callback* cb = (Callback *) cur->data;
+		if (cb && cb->callback)
+			cb->callback(mainwindow, object);
+	}
+}
+
+Extension* extension_load(const gchar* filename, gchar** error) {
+	Extension* extension;
+	gpointer extension_init, extension_done, extension_license,
+			 extension_name, extension_describtion;
+	
+	if (! filename) {
+		if (error)
+			*error = g_strdup(_("Missing filename"));
+		return NULL;
+	}
+	
+	extension = extension_new(filename);
+
+	debug_print("trying to load `%s'\n", filename);
+	extension->module = g_module_open(filename, 0);
+	if (extension->module == NULL) {
+		if (error)
+			*error = g_strdup(g_module_error());
+		extension_free(extension);
+		return NULL;
+	}
+
+	if (!g_module_symbol(extension->module, "extension_init", &extension_init) ||
+		!g_module_symbol(extension->module, "extension_done", &extension_done) ||
+		!g_module_symbol(extension->module, "extension_name", &extension_name) ||
+		!g_module_symbol(extension->module, "extension_describtion", &extension_describtion) ||
+		!g_module_symbol(extension->module, "extension_license", &extension_license)) {
+			if (error)
+				*error = g_strdup(g_module_error());
+			extension_free(extension);
+			return NULL;
+	}
+	
+	extension->init = extension_init;
+	extension->done = extension_done;
+	extension->license = extension_license;
+	extension->name = extension_name;
+	extension->describtion = extension_describtion;
+	
+	if (compatible_license(extension)) {
+		if (error)
+			*error = g_strconcat(extension->license(), ": not compatible!", NULL);
+		extension_free(extension);
+		return NULL;
+	}
+			
+	g_hash_table_replace(extensions, &extension->id, extension);
+	
+	return extension;
+}
+
+void extension_unload(Extension** extension, gchar** error) {
+	if (! g_hash_table_remove(extensions, &(*extension)->id)) {
+		if (error)
+			*error = g_strdup(_("Extension was not found"));
+	}
+	else
+		*extension = NULL;
+}
+	
+void extension_loader_done() {
+	done_hooks();
+	if (extensions) {
+		g_hash_table_destroy(extensions);
+		extensions = NULL;
+	}
+	mainwindow = NULL;
+	NEXT_ID = 1;
+
+	gslist_free(&user_menu_items, NULL);
+}
+
+static GSList* get_extension_path(gchar** error) {
+    GDir *ext_dir, *ext_sub_dir;
+    GError *err = NULL;
+    const gchar *filename, *ext_name;
+    gchar *path, *ext, *ext_file = NULL;
+    GSList* files = NULL;
+
+	ext_dir = g_dir_open(CLAWS_CONTACTS_EXTENSIONDIR, 0, &err);
+	if (err) {
+		g_warning("g_dir_open() failed: %s\n", err->message);
+		if (error)
+			*error = g_strdup(err->message);
+		g_clear_error(&err);
+		return NULL;
+	}
+	debug_print("Extension dir: %s\n", CLAWS_CONTACTS_EXTENSIONDIR);
+	if (EXTENSIONSUBDIR) {
+		while ((filename = g_dir_read_name(ext_dir))) {
+			g_free(ext_file);
+			ext_file = NULL;
+			path = g_build_filename(CLAWS_CONTACTS_EXTENSIONDIR, filename, NULL);
+			debug_print("Extension dir: %s\n", path);
+			if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
+				ext = g_build_filename(path, "src", ".libs", NULL);
+				ext_sub_dir = g_dir_open(ext, 0, &err);
+				if (err) {
+					g_free(ext);
+					g_clear_error(&err);
+					ext = g_build_filename(path, ".libs", NULL);
+					ext_sub_dir = g_dir_open(ext, 0, &err);
+					if (err) {
+						g_clear_error(&err);
+						continue;
+					}
+				}
+				while ((ext_name = g_dir_read_name(ext_sub_dir)) && ! ext_file) {
+					debug_print("Extension dir: %s\n", ext_name);
+					if (g_str_has_suffix(ext_name, G_MODULE_SUFFIX)) {
+						ext_file = g_build_filename(ext, ext_name, NULL);
+					}
+				}
+				g_dir_close(ext_sub_dir);
+				g_free(ext);
+				if (ext_file)
+					files = g_slist_append(files, g_strdup(ext_file));
+				g_free(ext_file);
+				ext_file = NULL;
+			}
+			g_free(path);
+		}
+	}
+	else {
+		while ((filename = g_dir_read_name(ext_dir))) {
+			if (g_str_has_suffix(filename, G_MODULE_SUFFIX)) {
+				debug_print("Extension dir: %s\n", path);
+				files = g_slist_append(files, g_strdup(filename));
+			}
+		}
+	}
+
+	g_dir_close(ext_dir);
+	
+	return files;
+}
+
+void load_extensions(gchar** error) {
+    Extension* extension;
+    GSList *exts, *cur;
+
+	exts = get_extension_path(error);
+	for (cur = exts; cur; cur = g_slist_next(cur)) {
+		gchar* ext = (gchar *) cur->data;
+		extension = extension_load(ext, error);
+		if (extension) {
+			extension->init(extension->id);
+			debug_print("%s: Loaded\n", extension->name());
+		}
+		else
+			debug_print("%s: %s\n", ext, *error ? *error : "Unknown error");
+	}
+	gslist_free(&exts, g_free);
+}
+
+void unload_extensions() {
+	GHashTableIter iter;
+	gpointer key, value;
+	gchar* error = NULL;
+	GSList *exts = NULL, *cur;
+	
+	if (extensions) {
+		g_hash_table_iter_init(&iter, extensions);
+		while (g_hash_table_iter_next(&iter, &key, &value)) {
+			exts = g_slist_prepend(exts, (Extension *) value);
+		}
+		cur = exts;
+		while (cur) {
+			Extension* extension = (Extension *) cur->data;
+			extension_unload(&extension, &error);
+			if (error) {
+				debug_print("%s\n", error);
+			}
+			g_free(error);
+			error = NULL;
+			cur = cur->next;
+		}
+		gslist_free(&exts, NULL);
+	}
+	extension_loader_done();
+}
+
+GtkWidget* get_menu_items(ContactsMenu menu) {
+	GtkWidget* widget = NULL;
+	
+	switch (menu) {
+		case CONTACTS_ADDRESSBOOK_MENU:
+			widget = abook_context;
+			break;
+		case CONTACTS_CONTACT_MENU:
+			widget = contact_context;
+			break;
+		default:
+			break;
 	}
+	
+	return widget;
+}
+
+static GtkWidget* create_menu_item(GtkWidget** widget, const gchar* label) {
+	GtkWidget* menu = NULL;
+
+	if (! *widget) {
+		menu = gtk_menu_new();
+		gtk_widget_show(menu);
+		gtk_menu_set_accel_group(GTK_MENU(menu), mainwindow->accel);
+		*widget = gtk_image_menu_item_new_with_mnemonic(label);
+		gtk_widget_show(*widget);
+		gtk_menu_item_set_submenu(GTK_MENU_ITEM(*widget), menu);
+	}
+		
+	return menu;
+}
+
+static GtkWidget* insert_sub_menu(GtkImageMenuItem* image_menu,
+								  MenuItem* menu_item,
+								  UserMenuItem* user_menu) {
+	GtkWidget *widget = NULL, *menu = NULL, *tmp;
+	GSList* cur;
+	gboolean force = FALSE;
+
+	if (menu_item->submenu) {
+		for (cur = user_menu_items; cur && ! menu; cur = g_slist_next(cur)) {
+			UserMenuItem* item = (UserMenuItem *) cur->data;
+			if (utf8_collate(item->parent, menu_item->parent) == 0) {
+				if (utf8_collate(item->sublabel, menu_item->sublabel) == 0 ||
+					utf8_collate(item->parent, item->sublabel) == 0) {
+					menu = item->menu;
+					if (utf8_collate(item->parent, item->sublabel) == 0)
+						force = TRUE;
+				}
+			}
+		}
+		if (! menu || force) {
+			if (force) {
+				tmp = create_menu_item(&widget, menu_item->sublabel);
+				gtk_menu_shell_append(GTK_MENU_SHELL(tmp), GTK_WIDGET(image_menu));
+			}
+			else
+				menu = create_menu_item(&widget, menu_item->sublabel);
+			user_menu->menu = (force)? tmp : menu;
+			user_menu->sublabel = menu_item->sublabel;
+		}
+		if (menu) {
+			if (force) {
+				gtk_menu_shell_append(GTK_MENU_SHELL(menu), widget);
+				widget = NULL;
+			}
+			else
+				gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(image_menu));
+		}
+	}
+	else
+		widget = GTK_WIDGET(image_menu);
+		
+	return widget;
+}
+
+MenuItem* menu_item_new(void) {
+	MenuItem* item;
+	
+	item = g_new0(MenuItem, 1);
+	
+	return item;
+}
+
+gboolean add_menu_item(GtkImageMenuItem* image_menu, MenuItem* menu_item) {
+	gboolean added = TRUE;
+	GtkWidget* menu_elem;
+	UserMenuItem* menu;
+	
+	if (! mainwindow)
+		return added;
+
+	if (! menu_item || ! menu_item->parent) {
+		g_critical("Missing menu_item or menu_item invalid");
+		return added;
+	}
+	
+	if (! GTK_IS_IMAGE_MENU_ITEM(image_menu)) {
+		g_critical("menu is not GtkImageMenuItem");
+		return added;
+	}
+
+	if (menu_item->submenu && ! menu_item->sublabel) {
+		g_critical("Submenu is missing label for widget");
+		return added;
+	}
+
+	debug_print("Inserting %s in menu\n", menu_item->parent);
+	
+	menu = g_new0(UserMenuItem, 1);
+	gtk_widget_show(GTK_WIDGET(image_menu));
+	if (strcasecmp("file", menu_item->parent) == 0) {
+		// add to 'File'
+		menu_elem = insert_sub_menu(image_menu, menu_item, menu);
+		if (menu->sublabel && menu_item->parent)
+			menu->parent = menu_item->parent;
+		else {
+			g_free(menu);
+			menu = NULL;
+		}
+		gtk_menu_shell_insert(GTK_MENU_SHELL(mainwindow->file_menu),
+						  menu_elem, file_menu_count);
+		file_menu_count++;
+	}	
+	else if (strcasecmp("tools", menu_item->parent) == 0) {
+		// add to 'Tools' 
+		menu_elem = insert_sub_menu(image_menu, menu_item, menu);
+		if (menu->sublabel && menu_item->parent)
+			menu->parent = menu_item->parent;
+		else {
+			g_free(menu);
+			menu = NULL;
+		}
+		gtk_menu_shell_append(GTK_MENU_SHELL(mainwindow->tools_menu), menu_elem);
+	}
+	else if (strcasecmp("help", menu_item->parent) == 0) {
+		// add to 'Help' 
+		menu_elem = insert_sub_menu(image_menu, menu_item, menu);
+		if (menu->sublabel && menu_item->parent)
+			menu->parent = menu_item->parent;
+		else {
+			g_free(menu);
+			menu = NULL;
+		}
+		gtk_menu_shell_append(GTK_MENU_SHELL(mainwindow->help_menu), menu_elem);
+	}
+	else if (menu_item->menu == CONTACTS_MAIN_MENU) {
+		if (! menu_item->sublabel) {
+			menu_item->sublabel = menu_item->parent;
+			menu_item->submenu = TRUE;
+		}
+		menu_elem = insert_sub_menu(image_menu, menu_item, menu);
+		if (menu->sublabel && menu_item->parent)
+			menu->parent = menu_item->parent;
+		else {
+			g_free(menu);
+			menu = NULL;
+		}
+		if (menu_elem) {
+			gtk_menu_shell_insert(GTK_MENU_SHELL(mainwindow->menu), menu_elem, menu_count);
+			menu_count++;
+		}
+	}
+	else {
+		// context menu
+		if (menu_item->menu == CONTACTS_CONTACT_MENU) {
+			menu_elem = insert_sub_menu(image_menu, menu_item, menu);
+			if (menu->sublabel && menu_item->parent)
+				menu->parent = menu_item->parent;
+			else {
+				g_free(menu);
+				menu = NULL;
+			}
+			if (! contact_context)
+				contact_context = menu_elem;
+			else
+				gtk_menu_shell_append(GTK_MENU_SHELL(contact_context), menu_elem);
+		}
+		else if (menu_item->menu == CONTACTS_ADDRESSBOOK_MENU) {
+			menu_elem = insert_sub_menu(image_menu, menu_item, menu);
+			if (menu->sublabel && menu_item->parent)
+				menu->parent = menu_item->parent;
+			else {
+				g_free(menu);
+				menu = NULL;
+			}
+			if (! abook_context)
+				abook_context = menu_elem;
+			else
+				gtk_menu_shell_append(GTK_MENU_SHELL(abook_context), menu_elem);
+		}
+		else {
+			// error!!!!
+		}
+	}
+
+	if (menu)
+		user_menu_items = g_slist_prepend(user_menu_items, menu);
+
+	return added;
 }

Index: callbacks.c
===================================================================
RCS file: /home/claws-mail/contacts/src/callbacks.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -d -r1.13 -r1.14
--- callbacks.c	28 Nov 2011 21:45:28 -0000	1.13
+++ callbacks.c	9 Dec 2011 23:01:21 -0000	1.14
@@ -51,6 +51,8 @@
 #include "printing.h"
 #include "dbus-service.h"
 #include "settings.h"
+#include "extension-loader.h"
+#include "extension.h"
 
 #define PLUGINGROUP "loaded plugins"
 
@@ -155,6 +157,7 @@
 	
     debug_print("Unloading all plugins\n");
 	plugin_unload_all();
+	unload_extensions();
 	
 	if (win) {
     	debug_print("Closing application\n");
@@ -230,7 +233,8 @@
               *contact_print, *contact_delete;
     GtkAccelGroup* accel;
     const gchar* name = widget->name;
-
+	GtkWidget* context;
+	
     debug_print("Popup called from %s\n", name);
     if (! (strcmp(name, "abook_list") == 0 ||
 		strcmp(name, "contact_list") == 0))
@@ -304,6 +308,12 @@
                 G_CALLBACK(abook_delete_cb), win);
             gtk_menu_shell_append(GTK_MENU_SHELL(menu), abook_delete);
         }
+		/* Insert requested menu_items from extensions */
+		context = get_menu_items(CONTACTS_ADDRESSBOOK_MENU);
+	    if (context) {
+			gtk_widget_unparent(context);
+			gtk_menu_shell_append(GTK_MENU_SHELL(menu),	g_object_ref(context));
+		}
     }
     else if (strcmp(name, "contact_list") == 0) {
 		if (new_row) {
@@ -349,11 +359,17 @@
                 G_CALLBACK(contact_delete_cb), win);
             gtk_menu_shell_append(GTK_MENU_SHELL(menu), contact_delete);
         }
+		/* Insert requested menu_items from extensions */
+		context = get_menu_items(CONTACTS_CONTACT_MENU);
+	    if (context) {
+			gtk_widget_unparent(context);
+			gtk_menu_shell_append(GTK_MENU_SHELL(menu),	g_object_ref(context));
+		}
     }
 	else {
 		return;
 	}
-	
+
     gtk_widget_show_all(menu);
     gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, button, event_time);
 }
@@ -421,6 +437,7 @@
 		}
 	}
 
+	
 	config_get_value(config, PLUGINGROUP, "plugins", &plugins);
 	
 	if (mainwindow) {
@@ -436,6 +453,10 @@
 				g_free(tmp);
 			}
 		}
+		if (mainwindow->use_extensions) {
+			debug_print("BEFORE_INIT_HOOK\n");
+			run_hook_callbacks(EXTENSION_BEFORE_INIT_HOOK, NULL);
+		}
 	}	
 
 	plugin_load_all(window, plugins);
@@ -443,8 +464,13 @@
 	gslist_free(&book, g_free);
 	
 	g_free(configrc);
-	if (mainwindow)
+	if (mainwindow) {
 		destroy_progress_dialog(mainwindow);
+		if (mainwindow->use_extensions) {
+			debug_print("AFTER_INIT_HOOK\n");
+			run_hook_callbacks(EXTENSION_AFTER_INIT_HOOK, NULL);
+		}
+	}
 }
 
 void list_view_clear(GtkTreeView* view, MainWindow* mainwindow) {
@@ -737,6 +763,10 @@
 		}
 	}
 	if (abook && plugin) {
+		if (win->use_extensions) {
+			debug_print("BEFORE_EDIT_ABOOK_HOOK\n");
+			run_hook_callbacks(EXTENSION_BEFORE_EDIT_ABOOK_HOOK, abook);
+		}
 		new = address_book_copy(abook, TRUE);
 		address_book_contacts_free(new);
 		if (address_book_edit(win->window, plugin, &new)) {
@@ -765,6 +795,10 @@
 			update_abook_list(win);
 			win->selected_abook = NULL;
 			address_book_free(&abook);
+			if (win->use_extensions) {
+				debug_print("AFTER_EDIT_ABOOK_HOOK\n");
+				run_hook_callbacks(EXTENSION_AFTER_EDIT_ABOOK_HOOK, abook);
+			}
 		}
 		else {
 			address_book_free(&new);
@@ -804,6 +838,10 @@
 	}
 	plugin = get_selected_plugin(GTK_TREE_VIEW(win->abook_list));
 	if (abook && plugin) {
+		if (win->use_extensions) {
+			debug_print("BEFORE_DELETE_ABOOK_HOOK\n");
+			run_hook_callbacks(EXTENSION_BEFORE_DELETE_ABOOK_HOOK, abook);
+		}
 		if (show_question(win->window, _("Remove '%s'?"), abook->abook_name)) {
 			plugin->abook_delete(abook, &error);
 			if (error) {
@@ -814,6 +852,10 @@
 			address_book_free(&abook);
 			update_abook_list(win);
 		}
+		if (win->use_extensions) {
+			debug_print("AFTER_DELETE_ABOOK_HOOK\n");
+			run_hook_callbacks(EXTENSION_AFTER_DELETE_ABOOK_HOOK, abook);
+		}
 	}
 	else {
 		show_message(win->window, GTK_UTIL_MESSAGE_INFO,
@@ -882,7 +924,11 @@
 				else
 					address_book_free(&book);
 			}
-			if (book) {		
+			if (book) {
+				if (win->use_extensions) {
+					debug_print("BEFORE_OPEN_ABOOK_HOOK\n");
+	            	run_hook_callbacks(EXTENSION_BEFORE_OPEN_ABOOK_HOOK, book);
+				}
 				if (plugin->abook_open(book, &error)) 
 					update_abook_list(win);
 				if (error) {
@@ -890,6 +936,10 @@
 					g_free(error);
 					address_book_free(&book);
 				}
+				if (win->use_extensions) {
+					debug_print("AFTER_OPEN_ABOOK_HOOK\n");
+	            	run_hook_callbacks(EXTENSION_AFTER_OPEN_ABOOK_HOOK, book);
+				}
 			}
 			if (closed) {
 				for (cur = closed; cur; cur = g_list_next(cur)) {
@@ -930,10 +980,18 @@
 	}
 	plugin = get_selected_plugin(GTK_TREE_VIEW(win->abook_list));
 	if (abook && plugin) {
+		if (win->use_extensions) {
+			debug_print("BEFORE_CLOSE_ABOOK_HOOK\n");
+			run_hook_callbacks(EXTENSION_BEFORE_CLOSE_ABOOK_HOOK, abook);
+		}
 		if (show_question(win->window, _("Close '%s' ?"), abook->abook_name)) {
 			plugin->abook_close(abook, &error);
 			update_abook_list(win);
 		}
+		if (win->use_extensions) {
+			debug_print("AFTER_CLOSE_ABOOK_HOOK\n");
+			run_hook_callbacks(EXTENSION_AFTER_CLOSE_ABOOK_HOOK, abook);
+		}
 	}
 	else {
 		show_message(win->window, GTK_UTIL_MESSAGE_INFO,
@@ -960,6 +1018,10 @@
 	if (response) {
 		plugin = plugin_get_plugin(plugin_name);
 		if (plugin) {
+			if (win->use_extensions) {
+				debug_print("BEFORE_INIT_ABOOK_HOOK\n");
+				run_hook_callbacks(EXTENSION_BEFORE_INIT_ABOOK_HOOK, NULL);
+			}
 			if (address_book_edit(win->window, plugin, &book)) {
 				if (address_book_name_exists(plugin, book)) {
 					error = g_strdup_printf("%s: Name is in use!",
@@ -967,6 +1029,10 @@
 					address_book_free(&book);
 					goto error;
 				}
+				if (win->use_extensions) {
+					debug_print("AFTER_INIT_ABOOK_HOOK\n");
+					run_hook_callbacks(EXTENSION_AFTER_INIT_ABOOK_HOOK, book);
+				}
 				if (plugin->file_filter() && strcmp("xml", plugin->file_filter()) == 0 &&
 						g_file_test(book->URL, G_FILE_TEST_EXISTS)) {
 					AddressBook* abook =
@@ -991,12 +1057,20 @@
 						}
 					}
 				}
+				if (win->use_extensions) {
+					debug_print("BEFORE_ADD_ABOOK_HOOK\n");
+					run_hook_callbacks(EXTENSION_BEFORE_ADD_ABOOK_HOOK, book);
+				}
 				if (! plugin->abook_set_config(book, NULL, &error)) {
 					if (plugin->abook_open(book, &error)) 
 						update_abook_list(win);
 				}
 				else
 					address_book_free(&book);
+				if (win->use_extensions) {
+					debug_print("AFTER_ADD_ABOOK_HOOK\n");
+					run_hook_callbacks(EXTENSION_AFTER_ADD_ABOOK_HOOK, book);
+				}
 	error:
 				if (error) {
 					show_message(win->window, GTK_UTIL_MESSAGE_WARNING, "%s", error);
@@ -1613,12 +1687,20 @@
 		g_free(p);
 	}
 	contact = get_selected_contact(tree_view);
+	if (win->use_extensions) {
+		debug_print("BEFORE_OPEN_ABOOK_HOOK\n");
+		run_hook_callbacks(EXTENSION_BEFORE_OPEN_CONTACT_HOOK, contact);
+	}
 	contact_show(win, contact, &error);
 
 	if (error) {
 		show_message(win->window, GTK_UTIL_MESSAGE_ERROR, "%s", error);
 		g_free(error);
 	}
+	if (win->use_extensions) {
+		debug_print("AFTER_OPEN_ABOOK_HOOK\n");
+		run_hook_callbacks(EXTENSION_AFTER_OPEN_CONTACT_HOOK, contact);
+	}
 }
 
 void contact_cell_edited_cb(GtkCellRendererText *cell,
@@ -1786,7 +1868,12 @@
 	plugin = get_selected_plugin(view);
 
 	if (plugin && ! plugin->readonly) {
+		if (win->use_extensions) {
+			debug_print("BEFORE_INIT_CONTACT_HOOK\n");
+			run_hook_callbacks(EXTENSION_BEFORE_INIT_CONTACT_HOOK, NULL);
+		}
 		contact_show(win, NULL, &error);
+		/* EXTENSION_AFTER_INIT_CONTACT_HOOK -> contactwindow->contact_update */
 	}
 	else
 		error = g_strdup(_("Plugin does not support updates"));
@@ -1881,10 +1968,18 @@
 		}
 		if (show_question(win->window,
 			_("Should contact '%s' be deleted?"), con_id ? con_id : "Unknown")) {
+			if (win->use_extensions) {
+				debug_print("BEFORE_DELETE_CONTACT_HOOK\n");
+				run_hook_callbacks(EXTENSION_BEFORE_DELETE_CONTACT_HOOK, contact);
+			}
 			plugin->delete_contact(abook, contact, &error);
 			if (iter && !error) {
 				gtk_list_store_remove(GTK_LIST_STORE(model), iter);
 				abook->contacts = g_list_remove(abook->contacts, contact);
+				if (win->use_extensions) {
+					debug_print("BEFORE_DELETE_CONTACT_HOOK\n");
+					run_hook_callbacks(EXTENSION_AFTER_DELETE_CONTACT_HOOK, contact);
+				}
 				contact_free(contact);
 				g_free(contact);
 			}
@@ -2036,6 +2131,10 @@
 	plugin = get_selected_plugin(view);
 
 	if (abook && plugin) {
+		if (win->use_extensions) {
+			debug_print("BEFORE_SEARCH_CONTACT_HOOK\n");
+			run_hook_callbacks(EXTENSION_BEFORE_SEARCH_ABOOK_HOOK, abook);
+		}
 		contacts = plugin->get_contact(abook, search, &error);
 		g_free(search);
 		if (error) {
@@ -2043,6 +2142,10 @@
 			g_free(error);
 		}
 		else {
+			if (win->use_extensions) {
+				debug_print("AFTER_SEARCH_CONTACT_HOOK\n");
+				run_hook_callbacks(EXTENSION_AFTER_SEARCH_ABOOK_HOOK, contacts);
+			}
 			view = GTK_TREE_VIEW(win->contact_list);
 			list_view_clear(view, win);
 			for (cur = contacts; cur; cur = g_slist_next(cur)) {
@@ -2098,6 +2201,10 @@
 			g_free(error);
 		}
 		else if (contact) {
+			if (win->use_extensions) {
+				debug_print("BEFORE_SEARCH_CONTACT_HOOK (advanced)\n");
+				run_hook_callbacks(EXTENSION_BEFORE_SEARCH_CONTACT_HOOK, contact);
+			}
 			contacts = plugin->search_contact(abook, contact, is_and, &error);
 			if (error) {
 				show_message(win->window, GTK_UTIL_MESSAGE_WARNING, "%s", error);
@@ -2106,6 +2213,10 @@
 			else {
 				contact_free(contact);
 				g_free(contact);
+				if (win->use_extensions) {
+					debug_print("AFTER_SEARCH_CONTACT_HOOK (advanced)\n");
+					run_hook_callbacks(EXTENSION_AFTER_SEARCH_CONTACT_HOOK, contacts);
+				}
 				view = GTK_TREE_VIEW(win->contact_list);
 				list_view_clear(view, win);
 				for (cur = contacts; cur; cur = g_slist_next(cur)) {

Index: gtk-utils.c
===================================================================
RCS file: /home/claws-mail/contacts/src/gtk-utils.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- gtk-utils.c	4 Oct 2011 20:21:40 -0000	1.4
+++ gtk-utils.c	9 Dec 2011 23:01:21 -0000	1.5
@@ -298,7 +298,11 @@
 	gchar* reply = NULL;
 	GSList *cur, *choices = NULL;
 	
-	if (button_cb->remaining_attribs) {
+	if (button_cb->remaining_attribs && ! button_cb->remaining_attribs->data) {
+		show_message(button_cb->parent, GTK_MESSAGE_INFO,
+			"Plugin does not support user defined attributes");
+	}
+	else if (button_cb->remaining_attribs) {
 		for (cur = button_cb->remaining_attribs; cur; cur = g_slist_next(cur)) {
 			AttribDef* attr = (AttribDef *) cur->data;
 			choices = g_slist_prepend(choices, g_strdup(attr->attrib_name));

Index: extension-loader.h
===================================================================
RCS file: /home/claws-mail/contacts/src/extension-loader.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- extension-loader.h	30 Nov 2011 23:35:12 -0000	1.1
+++ extension-loader.h	9 Dec 2011 23:01:21 -0000	1.2
@@ -42,21 +42,31 @@
 #include "mainwindow.h"
 
 typedef struct {
-	gint		id;
-	HOOKFUNC	callback;
-	gchar*		parent;
-	GtkWidget*	menu_item;
+	gchar*			filename;
+	guint			id;
+	GModule*		module;
+	gint			(*init) (guint id);
+	gboolean		(*done) (void);
+	const gchar*	(*license) (void);
+	const gchar*	(*name) (void);
+	const gchar*	(*describtion) (void);
 } Extension;
 
-Extension* extension_new(HOOKFUNC callback);
-void extension_free(void);
+typedef struct {
+	const gchar*	parent;
+	const gchar*	sublabel;
+	GtkWidget*		menu;
+} UserMenuItem;
+
 void init_hooks(MainWindow* main);
 void run_hook_callbacks(ExtensionHook hook, gpointer object);
-gint install_hook_function(ExtensionHook hook_type,
-						   HOOKFUNC callback,
-						   gchar** error);
-void uninstall_hook_function(gint id, gchar** error);
-
+void done_hooks(void);
+Extension* extension_load(const gchar* filename, gchar** error);
+void extension_unload(Extension** extension, gchar** error);
+void extension_loader_done(void);
+void load_extensions(gchar** error);
+void unload_extensions(void);
+GtkWidget* get_menu_items(ContactsMenu menu);
 
 G_END_DECLS
 



More information about the Commits mailing list