e0a3a5d0d17d7743da0fde556a1b0d9c225bb47b
[claws.git] / src / plugins / python / clawsmailmodule.c
1 /* Python plugin for Claws-Mail
2  * Copyright (C) 2009-2012 Holger Berndt
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "clawsmailmodule.h"
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #include <glib.h>
26 #include <glib/gi18n.h>
27
28 #include "nodetype.h"
29 #include "composewindowtype.h"
30 #include "foldertype.h"
31 #include "messageinfotype.h"
32
33 #include <pygobject.h>
34 #include <pygtk/pygtk.h>
35
36 #include "main.h"
37 #include "mainwindow.h"
38 #include "summaryview.h"
39 #include "quicksearch.h"
40 #include "toolbar.h"
41 #include "prefs_common.h"
42 #include "common/tags.h"
43
44 #include <glib.h>
45
46 static PyObject *cm_module = NULL;
47
48 PyObject* get_gobj_from_address(gpointer addr)
49 {
50   GObject *obj;
51
52   if (!G_IS_OBJECT(addr))
53       return NULL;
54
55   obj = G_OBJECT(addr);
56
57   if (!obj)
58       return NULL;
59
60   return pygobject_new(obj);
61 }
62
63 static PyObject* private_wrap_gobj(PyObject *self, PyObject *args)
64 {
65     void *addr;
66
67     if (!PyArg_ParseTuple(args, "l", &addr))
68         return NULL;
69
70     return get_gobj_from_address(addr);
71 }
72
73 static PyObject *get_mainwindow_action_group(PyObject *self, PyObject *args)
74 {
75   MainWindow *mainwin;
76
77   mainwin =  mainwindow_get_mainwindow();
78   if(mainwin)
79     return get_gobj_from_address(mainwin->action_group);
80   else
81     return NULL;
82 }
83
84 static PyObject *get_mainwindow_ui_manager(PyObject *self, PyObject *args)
85 {
86   MainWindow *mainwin;
87
88   mainwin =  mainwindow_get_mainwindow();
89   if(mainwin)
90     return get_gobj_from_address(mainwin->ui_manager);
91   else
92     return NULL;
93 }
94
95 static PyObject *get_folderview_selected_folder(PyObject *self, PyObject *args)
96 {
97   MainWindow *mainwin;
98
99   mainwin =  mainwindow_get_mainwindow();
100   if(mainwin && mainwin->folderview) {
101     FolderItem *item;
102     item = folderview_get_selected_item(mainwin->folderview);
103     if(item)
104       return clawsmail_folder_new(item);
105   }
106   Py_INCREF(Py_None);
107   return Py_None;
108 }
109
110 static PyObject *folderview_select_folder(PyObject *self, PyObject *args)
111 {
112   MainWindow *mainwin;
113
114   mainwin =  mainwindow_get_mainwindow();
115   if(mainwin && mainwin->folderview) {
116     FolderItem *item;
117     PyObject *folder;
118     folder = PyTuple_GetItem(args, 0);
119     if(!folder)
120       return NULL;
121     Py_INCREF(folder);
122     item = clawsmail_folder_get_item(folder);
123     Py_DECREF(folder);
124     if(item)
125       folderview_select(mainwin->folderview, item);
126   }
127   Py_INCREF(Py_None);
128   return Py_None;
129 }
130
131 static gboolean setup_folderitem_node(GNode *item_node, GNode *item_parent, PyObject **pyparent)
132 {
133   PyObject *pynode, *children;
134   int retval, n_children, i_child;
135   PyObject *folder;
136
137   /* create a python node for the folderitem node */
138   pynode = clawsmail_node_new(cm_module);
139   if(!pynode)
140     return FALSE;
141
142   /* store Folder in pynode */
143   folder = clawsmail_folder_new(item_node->data);
144   retval = PyObject_SetAttrString(pynode, "data", folder);
145   Py_DECREF(folder);
146   if(retval == -1) {
147     Py_DECREF(pynode);
148     return FALSE;
149   }
150
151   if(pyparent && *pyparent) {
152     /* add this node to the parent's childs */
153     children = PyObject_GetAttrString(*pyparent, "children");
154     retval = PyList_Append(children, pynode);
155     Py_DECREF(children);
156
157     if(retval == -1) {
158       Py_DECREF(pynode);
159       return FALSE;
160     }
161   }
162   else if(pyparent) {
163     *pyparent = pynode;
164     Py_INCREF(pynode);
165   }
166
167   /* call this function recursively for all children of the new node */
168   n_children = g_node_n_children(item_node);
169   for(i_child = 0; i_child < n_children; i_child++) {
170     if(!setup_folderitem_node(g_node_nth_child(item_node, i_child), item_node, &pynode)) {
171       Py_DECREF(pynode);
172       return FALSE;
173     }
174   }
175
176   Py_DECREF(pynode);
177   return TRUE;
178 }
179
180 static PyObject* get_folder_tree_from_account_name(const char *str)
181 {
182   PyObject *result;
183   GList *walk;
184
185   result = Py_BuildValue("[]");
186   if(!result)
187     return NULL;
188
189   for(walk = folder_get_list(); walk; walk = walk->next) {
190     Folder *folder = walk->data;
191     if((!str || !g_strcmp0(str, folder->name)) && folder->node) {
192       PyObject *root;
193       int n_children, i_child, retval;
194
195       /* create root nodes */
196       root = clawsmail_node_new(cm_module);
197       if(!root) {
198         Py_DECREF(result);
199         return NULL;
200       }
201
202       n_children = g_node_n_children(folder->node);
203       for(i_child = 0; i_child < n_children; i_child++) {
204         if(!setup_folderitem_node(g_node_nth_child(folder->node, i_child), folder->node, &root)) {
205           Py_DECREF(root);
206           Py_DECREF(result);
207           return NULL;
208         }
209       }
210       retval = PyList_Append(result, root);
211       Py_DECREF(root);
212       if(retval == -1) {
213         Py_DECREF(result);
214         return NULL;
215       }
216     }
217   }
218   return result;
219 }
220
221 static PyObject* get_folder_tree_from_folderitem(FolderItem *item)
222 {
223   PyObject *result;
224   GList *walk;
225
226   for(walk = folder_get_list(); walk; walk = walk->next) {
227     Folder *folder = walk->data;
228     if(folder->node) {
229       GNode *root_node;
230
231       root_node = g_node_find(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
232       if(!root_node)
233         continue;
234
235       result = NULL;
236       if(!setup_folderitem_node(root_node, NULL, &result))
237         return NULL;
238       else
239         return result;
240     }
241   }
242
243   PyErr_SetString(PyExc_LookupError, "Folder not found");
244   return NULL;
245 }
246
247 static PyObject* get_folder_tree(PyObject *self, PyObject *args)
248 {
249   PyObject *arg;
250   PyObject *result;
251   int retval;
252
253   Py_INCREF(Py_None);
254   arg = Py_None;
255   retval = PyArg_ParseTuple(args, "|O", &arg);
256   Py_DECREF(Py_None);
257   if(!retval)
258     return NULL;
259
260   /* calling possibilities:
261    * nothing, the mailbox name in a string, a Folder object */
262
263   /* no arguments: build up a list of folder trees */
264   if(PyTuple_Size(args) == 0) {
265     result = get_folder_tree_from_account_name(NULL);
266   }
267   else if(PyString_Check(arg)){
268     const char *str;
269     str = PyString_AsString(arg);
270     if(!str)
271       return NULL;
272
273     result = get_folder_tree_from_account_name(str);
274   }
275   else if(PyObject_TypeCheck(arg, clawsmail_folder_get_type_object())) {
276     result = get_folder_tree_from_folderitem(clawsmail_folder_get_item(arg));
277   }
278   else {
279     PyErr_SetString(PyExc_TypeError, "Parameter must be nothing, a mailbox string or a Folder object.");
280     return NULL;
281   }
282
283   return result;
284 }
285
286 static PyObject* quicksearch_search(PyObject *self, PyObject *args)
287 {
288   const char *string;
289   int searchtype;
290   QuickSearch *qs;
291   MainWindow *mainwin;
292
293   /* must be given exactly one argument, which is a string */
294   searchtype = prefs_common.summary_quicksearch_type;
295   if(!PyArg_ParseTuple(args, "s|i", &string, &searchtype))
296     return NULL;
297
298   mainwin = mainwindow_get_mainwindow();
299   if(!mainwin || !mainwin->summaryview || !mainwin->summaryview->quicksearch) {
300     PyErr_SetString(PyExc_LookupError, "Quicksearch not found");
301     return NULL;
302   }
303
304   qs = mainwin->summaryview->quicksearch;
305   quicksearch_set(qs, searchtype, string);
306
307   Py_INCREF(Py_None);
308   return Py_None;
309 }
310
311 static PyObject* quicksearch_clear(PyObject *self, PyObject *args)
312 {
313   QuickSearch *qs;
314   MainWindow *mainwin;
315
316   mainwin = mainwindow_get_mainwindow();
317   if(!mainwin || !mainwin->summaryview || !mainwin->summaryview->quicksearch) {
318     PyErr_SetString(PyExc_LookupError, "Quicksearch not found");
319     return NULL;
320   }
321
322   qs = mainwin->summaryview->quicksearch;
323   quicksearch_set(qs, prefs_common.summary_quicksearch_type, "");
324
325   Py_INCREF(Py_None);
326   return Py_None;
327 }
328
329 static PyObject* summaryview_select_messages(PyObject *self, PyObject *args)
330 {
331   PyObject *olist;
332   MainWindow *mainwin;
333   Py_ssize_t size, iEl;
334   GSList *msginfos;
335
336   mainwin = mainwindow_get_mainwindow();
337   if(!mainwin || !mainwin->summaryview) {
338     PyErr_SetString(PyExc_LookupError, "SummaryView not found");
339     return NULL;
340   }
341
342   if(!PyArg_ParseTuple(args, "O!", &PyList_Type, &olist)) {
343     PyErr_SetString(PyExc_LookupError, "Argument must be a list of MessageInfo objects.");
344     return NULL;
345   }
346
347   msginfos = NULL;
348   size = PyList_Size(olist);
349   for(iEl = 0; iEl < size; iEl++) {
350     PyObject *element = PyList_GET_ITEM(olist, iEl);
351
352     if(!element || !PyObject_TypeCheck(element, clawsmail_messageinfo_get_type_object())) {
353       PyErr_SetString(PyExc_LookupError, "Argument must be a list of MessageInfo objects.");
354       return NULL;
355     }
356
357     msginfos = g_slist_prepend(msginfos, clawsmail_messageinfo_get_msginfo(element));
358   }
359
360   summary_unselect_all(mainwin->summaryview);
361   summary_select_by_msg_list(mainwin->summaryview, msginfos);
362   g_slist_free(msginfos);
363
364   Py_INCREF(Py_None);
365   return Py_None;
366 }
367
368 static PyObject* get_summaryview_selected_message_list(PyObject *self, PyObject *args)
369 {
370   MainWindow *mainwin;
371   GSList *list, *walk;
372   PyObject *result;
373
374   mainwin = mainwindow_get_mainwindow();
375   if(!mainwin || !mainwin->summaryview) {
376     PyErr_SetString(PyExc_LookupError, "SummaryView not found");
377     return NULL;
378   }
379
380   result = Py_BuildValue("[]");
381   if(!result)
382     return NULL;
383
384   list = summary_get_selected_msg_list(mainwin->summaryview);
385   for(walk = list; walk; walk = walk->next) {
386     PyObject *msg;
387     msg = clawsmail_messageinfo_new(walk->data);
388     if(PyList_Append(result, msg) == -1) {
389       Py_DECREF(result);
390       return NULL;
391     }
392   }
393   g_slist_free(list);
394
395   return result;
396 }
397
398 static PyObject* is_exiting(PyObject *self, PyObject *args)
399 {
400   if(claws_is_exiting())
401     Py_RETURN_TRUE;
402   else
403     Py_RETURN_FALSE;
404 }
405
406 static PyObject* get_tags(PyObject *self, PyObject *args)
407 {
408   Py_ssize_t num_tags;
409   PyObject *tags_tuple;
410   GSList *tags_list;
411
412   tags_list = tags_get_list();
413   num_tags = g_slist_length(tags_list);
414
415   tags_tuple = PyTuple_New(num_tags);
416   if(tags_tuple != NULL) {
417     Py_ssize_t iTag;
418     PyObject *tag_object;
419     GSList *walk;
420
421     iTag = 0;
422     for(walk = tags_list; walk; walk = walk->next) {
423       tag_object = Py_BuildValue("s", tags_get_tag(GPOINTER_TO_INT(walk->data)));
424       if(tag_object == NULL) {
425         Py_DECREF(tags_tuple);
426         return NULL;
427       }
428       PyTuple_SET_ITEM(tags_tuple, iTag++, tag_object);
429     }
430   }
431
432   g_slist_free(tags_list);
433
434   return tags_tuple;
435 }
436
437 static PyObject* make_sure_tag_exists(PyObject *self, PyObject *args)
438 {
439   int retval;
440   const char *tag_str;
441
442   retval = PyArg_ParseTuple(args, "s", &tag_str);
443   if(!retval)
444     return NULL;
445
446   if(IS_NOT_RESERVED_TAG(tag_str) == FALSE) {
447     /* tag name is reserved, raise KeyError */
448     PyErr_SetString(PyExc_ValueError, "Tag name is reserved");
449     return NULL;
450   }
451
452   tags_add_tag(tag_str);
453
454   Py_RETURN_NONE;
455 }
456
457 static PyObject* delete_tag(PyObject *self, PyObject *args)
458 {
459   int retval;
460   const char *tag_str;
461   gint tag_id;
462   MainWindow *mainwin;
463
464   retval = PyArg_ParseTuple(args, "s", &tag_str);
465   if(!retval)
466     return NULL;
467
468   tag_id = tags_get_id_for_str(tag_str);
469   if(tag_id == -1) {
470     PyErr_SetString(PyExc_KeyError, "Tag does not exist");
471     return NULL;
472   }
473
474   tags_remove_tag(tag_id);
475
476   /* update display */
477   mainwin = mainwindow_get_mainwindow();
478   if(mainwin)
479     summary_redisplay_msg(mainwin->summaryview);
480
481   Py_RETURN_NONE;
482 }
483
484
485 static PyObject* rename_tag(PyObject *self, PyObject *args)
486 {
487   int retval;
488   const char *old_tag_str;
489   const char *new_tag_str;
490   gint tag_id;
491   MainWindow *mainwin;
492
493   retval = PyArg_ParseTuple(args, "ss", &old_tag_str, &new_tag_str);
494   if(!retval)
495     return NULL;
496
497   if((IS_NOT_RESERVED_TAG(new_tag_str) == FALSE) ||(IS_NOT_RESERVED_TAG(old_tag_str) == FALSE)) {
498     PyErr_SetString(PyExc_ValueError, "Tag name is reserved");
499     return NULL;
500   }
501
502   tag_id = tags_get_id_for_str(old_tag_str);
503   if(tag_id == -1) {
504     PyErr_SetString(PyExc_KeyError, "Tag does not exist");
505     return NULL;
506   }
507
508   tags_update_tag(tag_id, new_tag_str);
509
510   /* update display */
511   mainwin = mainwindow_get_mainwindow();
512   if(mainwin)
513     summary_redisplay_msg(mainwin->summaryview);
514
515   Py_RETURN_NONE;
516 }
517
518 static gboolean get_message_list_for_move_or_copy(PyObject *messagelist, PyObject *folder, GSList **list)
519 {
520   Py_ssize_t size, iEl;
521   FolderItem *folderitem;
522   
523   *list = NULL;
524
525   folderitem = clawsmail_folder_get_item(folder);
526   if(!folderitem) {
527     PyErr_SetString(PyExc_LookupError, "Brokern Folder object.");
528     return FALSE;
529   }
530
531   size = PyList_Size(messagelist);
532   for(iEl = 0; iEl < size; iEl++) {
533     PyObject *element = PyList_GET_ITEM(messagelist, iEl);
534     MsgInfo *msginfo;
535
536     if(!element || !PyObject_TypeCheck(element, clawsmail_messageinfo_get_type_object())) {
537       PyErr_SetString(PyExc_TypeError, "Argument must be a list of MessageInfo objects.");
538       return FALSE;
539     }
540     
541     msginfo = clawsmail_messageinfo_get_msginfo(element);
542     if(!msginfo) {
543       PyErr_SetString(PyExc_LookupError, "Broken MessageInfo object.");
544       return FALSE;
545     }
546    
547     procmsg_msginfo_set_to_folder(msginfo, folderitem);
548     *list = g_slist_prepend(*list, msginfo);
549   }
550  
551   return TRUE;
552 }
553
554 static PyObject* move_or_copy_messages(PyObject *self, PyObject *args, gboolean move)
555 {
556   PyObject *messagelist;
557   PyObject *folder;
558   int retval;
559   GSList *list = NULL;
560   
561   retval = PyArg_ParseTuple(args, "O!O!",
562     &PyList_Type, &messagelist,
563     clawsmail_folder_get_type_object(), &folder);
564   if(!retval )
565     return NULL;  
566
567   folder_item_update_freeze();
568   
569   if(!get_message_list_for_move_or_copy(messagelist, folder, &list))
570     goto err;
571   
572   if(move)   
573     procmsg_move_messages(list);
574   else
575     procmsg_copy_messages(list);
576       
577   folder_item_update_thaw();
578   g_slist_free(list);
579   Py_RETURN_NONE;
580
581 err:
582   folder_item_update_thaw();
583   g_slist_free(list);
584   return NULL;
585 }
586
587 static PyObject* move_messages(PyObject *self, PyObject *args)
588 {
589   return move_or_copy_messages(self, args, TRUE);
590 }
591
592
593 static PyObject* copy_messages(PyObject *self, PyObject *args)
594 {
595   return move_or_copy_messages(self, args, FALSE);
596 }
597
598 static PyMethodDef ClawsMailMethods[] = {
599     /* public */
600     {"get_mainwindow_action_group",  get_mainwindow_action_group, METH_NOARGS,
601      "get_mainwindow_action_group() - get action group of main window menu\n"
602      "\n"
603      "Returns the gtk.ActionGroup for the main window."},
604
605     {"get_mainwindow_ui_manager",  get_mainwindow_ui_manager, METH_NOARGS, 
606      "get_mainwindow_ui_manager() - get ui manager of main window\n"
607      "\n"
608      "Returns the gtk.UIManager for the main window."},
609
610     {"get_folder_tree",  get_folder_tree, METH_VARARGS,
611      "get_folder_tree([root]) - get a folder tree\n"
612      "\n"
613      "Without arguments, get a list of folder trees for all mailboxes.\n"
614      "\n"
615      "If the optional root argument is a string, it is supposed to be a\n"
616      "mailbox name. The function then returns a tree of folders of that mailbox.\n"
617      "\n"
618      "If the optional root argument is a clawsmail.Folder, the function\n"
619      "returns a tree of subfolders with the given folder as root element.\n"
620      "\n"
621      "In any case, a tree consists of elements of the type clawsmail.Node."},
622
623     {"get_folderview_selected_folder",  get_folderview_selected_folder, METH_NOARGS,
624      "get_folderview_selected_folder() - get selected folder in folderview\n"
625      "\n"
626      "Returns the currently selected folder as a clawsmail.Folder."},
627     {"folderview_select_folder",  folderview_select_folder, METH_VARARGS,
628      "folderview_select_folder(folder) - select folder in folderview\n"
629      "\n"
630      "Takes an argument of type clawsmail.Folder, and selects the corresponding folder."},
631
632     {"quicksearch_search", quicksearch_search, METH_VARARGS,
633      "quicksearch_search(string [, type]) - perform a quicksearch\n"
634      "\n"
635      "Perform a quicksearch of the given string. The optional type argument can be\n"
636      "one of clawsmail.QUICK_SEARCH_SUBJECT, clawsmail.QUICK_SEARCH_FROM, clawsmail.QUICK_SEARCH_TO,\n"
637      "clawsmail.QUICK_SEARCH_EXTENDED, clawsmail.QUICK_SEARCH_MIXED, or clawsmail.QUICK_SEARCH_TAG.\n"
638      "If it is omitted, the current selection is used. The string argument has to be a valid search\n"
639      "string corresponding to the type."},
640
641     {"quicksearch_clear", quicksearch_clear, METH_NOARGS,
642      "quicksearch_clear() - clear the quicksearch"},
643
644     {"get_summaryview_selected_message_list", get_summaryview_selected_message_list, METH_NOARGS,
645      "get_summaryview_selected_message_list() - get selected message list\n"
646      "\n"
647      "Get a list of clawsmail.MessageInfo objects of the current selection."},
648
649     {"summaryview_select_messages", summaryview_select_messages, METH_VARARGS,
650      "summaryview_select_messages(message_list) - select a list of messages in the summary view\n"
651      "\n"
652      "Select a list of clawsmail.MessageInfo objects in the summary view."},
653
654     {"is_exiting", is_exiting, METH_NOARGS,
655      "is_exiting() - test whether Claws Mail is currently exiting\n"
656      "\n"
657      "Returns True if Claws Mail is currently exiting. The most common usage for this is to skip\n"
658      "unnecessary cleanup tasks in a shutdown script when Claws Mail is exiting anyways. If the Python\n"
659      "plugin is explicitly unloaded, the shutdown script will still be called, but this function will\n"
660      "return False."},
661
662     {"move_messages", move_messages, METH_VARARGS,
663      "move_messages(message_list, target_folder) - move a list of messages to a target folder\n"
664      "\n"
665      "Move a list of clawsmail.MessageInfo objects to a target folder.\n"
666      "The target_folder argument has to be a clawsmail.Folder object."},
667
668     {"copy_messages", copy_messages, METH_VARARGS,
669      "copy_messages(message_list, target_folder) - copy a list of messages to a target folder\n"
670      "\n"
671      "Copy a list of clawsmail.MessageInfo objects to a target folder.\n"
672      "The target_folder argument has to be a clawsmail.Folder object."},
673
674     {"get_tags", get_tags, METH_NOARGS,
675      "get_tags() - get a tuple of all tags that Claws Mail knows about\n"
676      "\n"
677      "Get a tuple of strings representing all tags that are defined in Claws Mail."},
678
679     {"make_sure_tag_exists", make_sure_tag_exists, METH_VARARGS,
680      "make_sure_tag_exists(tag) - make sure that the specified tag exists\n"
681      "\n"
682      "This function creates the given tag if it does not exist yet.\n"
683      "It is not an error if the tag already exists. In this case, this function does nothing.\n"
684      "However, if a reserved tag name is chosen, a ValueError exception is raised."},
685
686     {"delete_tag", delete_tag, METH_VARARGS,
687      "delete_tag(tag) - delete a tag\n"
688      "\n"
689      "This function deletes an existing tag.\n"
690      "Raises a KeyError exception if the tag does not exist."},
691
692     {"rename_tag", rename_tag, METH_VARARGS,
693      "rename_tag(old_tag, new_tag) - rename tag old_tag to new_tag\n"
694      "\n"
695      "This function renames an existing tag.\n"
696      "Raises a KeyError exception if the tag does not exist.\n"
697      "Raises a ValueError exception if the old or new tag name is a reserved name."},
698
699      /* private */
700     {"__gobj", private_wrap_gobj, METH_VARARGS,
701      "__gobj(ptr) - transforms a C GObject pointer into a PyGObject\n"
702      "\n"
703      "For internal usage only."},
704
705     {NULL, NULL, 0, NULL}
706 };
707
708 static void initmiscstuff(PyObject *module)
709 {
710   PyObject *dict;
711   PyObject *res;
712   const char *cmd =
713       "QUICK_SEARCH_SUBJECT = 0\n"
714       "QUICK_SEARCH_FROM = 1\n"
715       "QUICK_SEARCH_TO = 2\n"
716       "QUICK_SEARCH_EXTENDED = 3\n"
717       "QUICK_SEARCH_MIXED = 4\n"
718       "QUICK_SEARCH_TAG = 5\n"
719       "\n";
720   dict = PyModule_GetDict(module);
721   res = PyRun_String(cmd, Py_file_input, dict, dict);
722   Py_XDECREF(res);
723 }
724
725
726 void claws_mail_python_init(void)
727 {
728   if (!Py_IsInitialized())
729       Py_Initialize();
730
731   /* create module */
732   cm_module = Py_InitModule3("clawsmail", ClawsMailMethods,
733       "This module can be used to access some of Claws Mail's data structures\n"
734       "in order to extend or modify the user interface or automate repetitive tasks.\n"
735       "\n"
736       "Whenever possible, the interface works with standard GTK+ widgets\n"
737       "via the PyGTK bindings, so you can refer to the GTK+ / PyGTK documentation\n"
738       "to find out about all possible options.\n"
739       "\n"
740       "The interface to Claws Mail in this module is extended on a 'as-needed' basis.\n"
741       "If you're missing something specific, try contacting the author.");
742
743   /* initialize classes */
744   initnode(cm_module);
745   initcomposewindow(cm_module);
746   initfolder(cm_module);
747   initmessageinfo(cm_module);
748
749   /* initialize misc things */
750   initmiscstuff(cm_module);
751
752   PyRun_SimpleString("import clawsmail\n");
753   PyRun_SimpleString("clawsmail.compose_window = None\n");
754 }
755
756
757 void put_composewindow_into_module(Compose *compose)
758 {
759   PyObject *pycompose;
760
761   pycompose = clawsmail_compose_new(cm_module, compose);
762   PyObject_SetAttrString(cm_module, "compose_window", pycompose);
763   Py_DECREF(pycompose);
764 }