Fix/normalize product name in few places in UI and in several source
[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 "folderpropertiestype.h"
31 #include "foldertype.h"
32 #include "messageinfotype.h"
33 #include "accounttype.h"
34 #include "mailboxtype.h"
35
36 #define NO_IMPORT_PYGOBJECT
37 #include <pygobject.h>
38 #define NO_IMPORT_PYGTK
39 #include <pygtk/pygtk.h>
40
41 #include "main.h"
42 #include "mainwindow.h"
43 #include "summaryview.h"
44 #include "quicksearch.h"
45 #include "toolbar.h"
46 #include "prefs_common.h"
47 #include "common/tags.h"
48 #include "account.h"
49
50 #include <glib.h>
51
52 static PyObject *cm_module = NULL;
53
54 PyObject* get_gobj_from_address(gpointer addr)
55 {
56   GObject *obj;
57
58   if (!G_IS_OBJECT(addr))
59       return NULL;
60
61   obj = G_OBJECT(addr);
62
63   if (!obj)
64       return NULL;
65
66   return pygobject_new(obj);
67 }
68
69 static PyObject* private_wrap_gobj(PyObject *self, PyObject *args)
70 {
71     void *addr;
72
73     if (!PyArg_ParseTuple(args, "l", &addr))
74         return NULL;
75
76     return get_gobj_from_address(addr);
77 }
78
79 static PyObject *get_mainwindow_action_group(PyObject *self, PyObject *args)
80 {
81   MainWindow *mainwin;
82
83   mainwin =  mainwindow_get_mainwindow();
84   if(mainwin)
85     return get_gobj_from_address(mainwin->action_group);
86   else
87     return NULL;
88 }
89
90 static PyObject *get_mainwindow_ui_manager(PyObject *self, PyObject *args)
91 {
92   MainWindow *mainwin;
93
94   mainwin =  mainwindow_get_mainwindow();
95   if(mainwin)
96     return get_gobj_from_address(mainwin->ui_manager);
97   else
98     return NULL;
99 }
100
101 static PyObject *get_folderview_selected_folder(PyObject *self, PyObject *args)
102 {
103   MainWindow *mainwin;
104
105   mainwin =  mainwindow_get_mainwindow();
106   if(mainwin && mainwin->folderview) {
107     FolderItem *item;
108     item = folderview_get_selected_item(mainwin->folderview);
109     if(item)
110       return clawsmail_folder_new(item);
111   }
112   Py_RETURN_NONE;
113 }
114
115 static PyObject *get_folderview_selected_mailbox(PyObject *self, PyObject *args)
116 {
117   MainWindow *mainwin;
118
119   mainwin =  mainwindow_get_mainwindow();
120   if(mainwin && mainwin->folderview) {
121     FolderItem *item;
122     item = folderview_get_selected_item(mainwin->folderview);
123     if(item) {
124       gchar *id;
125       id = folder_item_get_identifier(item);
126       /* If there is an id, it's a folder, not a mailbox */
127       if(id) {
128         g_free(id);
129         Py_RETURN_NONE;
130       }
131       else
132         return clawsmail_mailbox_new(item->folder);
133     }
134   }
135   Py_RETURN_NONE;
136 }
137
138 static PyObject *folderview_select_row(PyObject *self, PyObject *args)
139 {
140   MainWindow *mainwin;
141   gboolean ok;
142
143   ok = TRUE;
144   mainwin =  mainwindow_get_mainwindow();
145   if(mainwin && mainwin->folderview) {
146     PyObject *arg;
147     arg = PyTuple_GetItem(args, 0);
148     if(!arg)
149       return NULL;
150     Py_INCREF(arg);
151
152     if(clawsmail_folder_check(arg)) {
153       FolderItem *item;
154       item = clawsmail_folder_get_item(arg);
155       if(item)
156         folderview_select(mainwin->folderview, item);
157     }
158     else if(clawsmail_mailbox_check(arg)) {
159       Folder *folder;
160       folder = clawsmail_mailbox_get_folder(arg);
161       if(folder && folder->node) {
162         folderview_select(mainwin->folderview, folder->node->data);
163       }
164     }
165     else {
166       PyErr_SetString(PyExc_TypeError, "Bad argument type");
167       ok = FALSE;
168     }
169
170     Py_DECREF(arg);
171   }
172   if(ok)
173     Py_RETURN_NONE;
174   else
175     return NULL;
176 }
177
178 static gboolean setup_folderitem_node(GNode *item_node, GNode *item_parent, PyObject **pyparent)
179 {
180   PyObject *pynode, *children;
181   int retval, n_children, i_child;
182   PyObject *folder;
183
184   /* create a python node for the folderitem node */
185   pynode = clawsmail_node_new(cm_module);
186   if(!pynode)
187     return FALSE;
188
189   /* store Folder in pynode */
190   folder = clawsmail_folder_new(item_node->data);
191   retval = PyObject_SetAttrString(pynode, "data", folder);
192   Py_DECREF(folder);
193   if(retval == -1) {
194     Py_DECREF(pynode);
195     return FALSE;
196   }
197
198   if(pyparent && *pyparent) {
199     /* add this node to the parent's childs */
200     children = PyObject_GetAttrString(*pyparent, "children");
201     retval = PyList_Append(children, pynode);
202     Py_DECREF(children);
203
204     if(retval == -1) {
205       Py_DECREF(pynode);
206       return FALSE;
207     }
208   }
209   else if(pyparent) {
210     *pyparent = pynode;
211     Py_INCREF(pynode);
212   }
213
214   /* call this function recursively for all children of the new node */
215   n_children = g_node_n_children(item_node);
216   for(i_child = 0; i_child < n_children; i_child++) {
217     if(!setup_folderitem_node(g_node_nth_child(item_node, i_child), item_node, &pynode)) {
218       Py_DECREF(pynode);
219       return FALSE;
220     }
221   }
222
223   Py_DECREF(pynode);
224   return TRUE;
225 }
226
227 static PyObject* get_folder_tree_from_folder(Folder *folder)
228 {
229   if(folder->node) {
230     PyObject *root;
231     int n_children, i_child;
232
233     /* create root nodes */
234     root = clawsmail_node_new(cm_module);
235     if(!root)
236       return NULL;
237
238     n_children = g_node_n_children(folder->node);
239     for(i_child = 0; i_child < n_children; i_child++) {
240       if(!setup_folderitem_node(g_node_nth_child(folder->node, i_child), folder->node, &root)) {
241         Py_DECREF(root);
242         return NULL;
243       }
244     }
245     return root;
246   }
247   return NULL;
248 }
249
250 static PyObject* get_folder_tree_from_account_name(const char *str)
251 {
252   PyObject *result;
253   GList *walk;
254
255   result = Py_BuildValue("[]");
256   if(!result)
257     return NULL;
258
259   for(walk = folder_get_list(); walk; walk = walk->next) {
260     Folder *folder = walk->data;
261     if(!str || !g_strcmp0(str, folder->name)) {
262       PyObject *tree_from_folder;
263       tree_from_folder = get_folder_tree_from_folder(folder);
264       if(tree_from_folder) {
265         int retval;
266         retval = PyList_Append(result, tree_from_folder);
267         Py_DECREF(tree_from_folder);
268         if(retval == -1) {
269           Py_DECREF(result);
270           return NULL;
271         }
272       }
273       else {
274         Py_DECREF(result);
275         return NULL;
276       }
277     }
278   }
279   return result;
280 }
281
282 static PyObject* get_folder_tree_from_folderitem(FolderItem *item)
283 {
284   PyObject *result;
285   GList *walk;
286
287   for(walk = folder_get_list(); walk; walk = walk->next) {
288     Folder *folder = walk->data;
289     if(folder->node) {
290       GNode *root_node;
291
292       root_node = g_node_find(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
293       if(!root_node)
294         continue;
295
296       result = NULL;
297       if(!setup_folderitem_node(root_node, NULL, &result))
298         return NULL;
299       else
300         return result;
301     }
302   }
303
304   PyErr_SetString(PyExc_LookupError, "Folder not found");
305   return NULL;
306 }
307
308 static PyObject* get_folder_tree(PyObject *self, PyObject *args)
309 {
310   PyObject *arg;
311   PyObject *result;
312   int retval;
313
314   Py_INCREF(Py_None);
315   arg = Py_None;
316   retval = PyArg_ParseTuple(args, "|O", &arg);
317   Py_DECREF(Py_None);
318   if(!retval)
319     return NULL;
320
321   /* calling possibilities:
322    * nothing, the mailbox name in a string, a Folder object */
323
324   /* no arguments: build up a list of folder trees */
325   if(PyTuple_Size(args) == 0) {
326     result = get_folder_tree_from_account_name(NULL);
327   }
328   else if(PyString_Check(arg)){
329     const char *str;
330     str = PyString_AsString(arg);
331     if(!str)
332       return NULL;
333
334     result = get_folder_tree_from_account_name(str);
335   }
336   else if(PyObject_TypeCheck(arg, clawsmail_folder_get_type_object())) {
337     result = get_folder_tree_from_folderitem(clawsmail_folder_get_item(arg));
338   }
339   else if(clawsmail_mailbox_check(arg)) {
340     result = get_folder_tree_from_folder(clawsmail_mailbox_get_folder(arg));
341   }
342   else {
343     PyErr_SetString(PyExc_TypeError, "Parameter must be nothing, a Folder object, a Mailbox object, or a mailbox name string.");
344     return NULL;
345   }
346
347   return result;
348 }
349
350 static PyObject* quicksearch_search(PyObject *self, PyObject *args)
351 {
352   const char *string;
353   int searchtype;
354   QuickSearch *qs;
355   MainWindow *mainwin;
356
357   /* must be given exactly one argument, which is a string */
358   searchtype = prefs_common_get_prefs()->summary_quicksearch_type;
359   if(!PyArg_ParseTuple(args, "s|i", &string, &searchtype))
360     return NULL;
361
362   mainwin = mainwindow_get_mainwindow();
363   if(!mainwin || !mainwin->summaryview || !mainwin->summaryview->quicksearch) {
364     PyErr_SetString(PyExc_LookupError, "Quicksearch not found");
365     return NULL;
366   }
367
368   qs = mainwin->summaryview->quicksearch;
369   quicksearch_set(qs, searchtype, string);
370
371   Py_INCREF(Py_None);
372   return Py_None;
373 }
374
375 static PyObject* quicksearch_clear(PyObject *self, PyObject *args)
376 {
377   QuickSearch *qs;
378   MainWindow *mainwin;
379
380   mainwin = mainwindow_get_mainwindow();
381   if(!mainwin || !mainwin->summaryview || !mainwin->summaryview->quicksearch) {
382     PyErr_SetString(PyExc_LookupError, "Quicksearch not found");
383     return NULL;
384   }
385
386   qs = mainwin->summaryview->quicksearch;
387   quicksearch_set(qs, prefs_common_get_prefs()->summary_quicksearch_type, "");
388
389   Py_INCREF(Py_None);
390   return Py_None;
391 }
392
393 static PyObject* summaryview_select_messages(PyObject *self, PyObject *args)
394 {
395   PyObject *olist;
396   MainWindow *mainwin;
397   Py_ssize_t size, iEl;
398   GSList *msginfos;
399
400   mainwin = mainwindow_get_mainwindow();
401   if(!mainwin || !mainwin->summaryview) {
402     PyErr_SetString(PyExc_LookupError, "SummaryView not found");
403     return NULL;
404   }
405
406   if(!PyArg_ParseTuple(args, "O!", &PyList_Type, &olist)) {
407     PyErr_SetString(PyExc_LookupError, "Argument must be a list of MessageInfo objects.");
408     return NULL;
409   }
410
411   msginfos = NULL;
412   size = PyList_Size(olist);
413   for(iEl = 0; iEl < size; iEl++) {
414     PyObject *element = PyList_GET_ITEM(olist, iEl);
415
416     if(!element || !PyObject_TypeCheck(element, clawsmail_messageinfo_get_type_object())) {
417       PyErr_SetString(PyExc_LookupError, "Argument must be a list of MessageInfo objects.");
418       return NULL;
419     }
420
421     msginfos = g_slist_prepend(msginfos, clawsmail_messageinfo_get_msginfo(element));
422   }
423
424   summary_unselect_all(mainwin->summaryview);
425   summary_select_by_msg_list(mainwin->summaryview, msginfos);
426   g_slist_free(msginfos);
427
428   Py_INCREF(Py_None);
429   return Py_None;
430 }
431
432 static PyObject* get_summaryview_selected_message_list(PyObject *self, PyObject *args)
433 {
434   MainWindow *mainwin;
435   GSList *list, *walk;
436   PyObject *result;
437
438   mainwin = mainwindow_get_mainwindow();
439   if(!mainwin || !mainwin->summaryview) {
440     PyErr_SetString(PyExc_LookupError, "SummaryView not found");
441     return NULL;
442   }
443
444   result = Py_BuildValue("[]");
445   if(!result)
446     return NULL;
447
448   list = summary_get_selected_msg_list(mainwin->summaryview);
449   for(walk = list; walk; walk = walk->next) {
450     PyObject *msg;
451     msg = clawsmail_messageinfo_new(walk->data);
452     if(PyList_Append(result, msg) == -1) {
453       Py_DECREF(result);
454       return NULL;
455     }
456   }
457   g_slist_free(list);
458
459   return result;
460 }
461
462 static PyObject* is_exiting(PyObject *self, PyObject *args)
463 {
464   if(claws_is_exiting())
465     Py_RETURN_TRUE;
466   else
467     Py_RETURN_FALSE;
468 }
469
470 static PyObject* get_tags(PyObject *self, PyObject *args)
471 {
472   Py_ssize_t num_tags;
473   PyObject *tags_tuple;
474   GSList *tags_list;
475
476   tags_list = tags_get_list();
477   num_tags = g_slist_length(tags_list);
478
479   tags_tuple = PyTuple_New(num_tags);
480   if(tags_tuple != NULL) {
481     Py_ssize_t iTag;
482     PyObject *tag_object;
483     GSList *walk;
484
485     iTag = 0;
486     for(walk = tags_list; walk; walk = walk->next) {
487       tag_object = Py_BuildValue("s", tags_get_tag(GPOINTER_TO_INT(walk->data)));
488       if(tag_object == NULL) {
489         Py_DECREF(tags_tuple);
490         return NULL;
491       }
492       PyTuple_SET_ITEM(tags_tuple, iTag++, tag_object);
493     }
494   }
495
496   g_slist_free(tags_list);
497
498   return tags_tuple;
499 }
500
501 static PyObject* get_accounts(PyObject *self, PyObject *args)
502 {
503   PyObject *accounts_tuple;
504   GList *accounts_list;
505   GList *walk;
506
507   accounts_list = account_get_list();
508
509   accounts_tuple = PyTuple_New(g_list_length(accounts_list));
510   if(accounts_tuple) {
511     PyObject *account_object;
512     Py_ssize_t iAccount;
513
514     iAccount = 0;
515     for(walk = accounts_list; walk; walk = walk->next) {
516       account_object = clawsmail_account_new(walk->data);
517       if(account_object == NULL) {
518         Py_DECREF(accounts_tuple);
519         return NULL;
520       }
521       PyTuple_SET_ITEM(accounts_tuple, iAccount++, account_object);
522     }
523   }
524
525   return accounts_tuple;
526 }
527
528 static PyObject* get_mailboxes(PyObject *self, PyObject *args)
529 {
530   PyObject *mailboxes_tuple;
531   GList *mailboxes_list;
532   GList *walk;
533
534   mailboxes_list = folder_get_list();
535
536   mailboxes_tuple = PyTuple_New(g_list_length(mailboxes_list));
537   if(mailboxes_tuple) {
538     PyObject *mailbox_object;
539     Py_ssize_t iMailbox;
540
541     iMailbox = 0;
542     for(walk = mailboxes_list; walk; walk = walk->next) {
543       mailbox_object = clawsmail_mailbox_new(walk->data);
544       if(mailbox_object == NULL) {
545         Py_DECREF(mailboxes_tuple);
546         return NULL;
547       }
548       PyTuple_SET_ITEM(mailboxes_tuple, iMailbox++, mailbox_object);
549     }
550   }
551
552   return mailboxes_tuple;
553 }
554
555
556 static PyObject* make_sure_tag_exists(PyObject *self, PyObject *args)
557 {
558   int retval;
559   const char *tag_str;
560
561   retval = PyArg_ParseTuple(args, "s", &tag_str);
562   if(!retval)
563     return NULL;
564
565   if(IS_NOT_RESERVED_TAG(tag_str) == FALSE) {
566     /* tag name is reserved, raise KeyError */
567     PyErr_SetString(PyExc_ValueError, "Tag name is reserved");
568     return NULL;
569   }
570
571   tags_add_tag(tag_str);
572
573   Py_RETURN_NONE;
574 }
575
576 static PyObject* delete_tag(PyObject *self, PyObject *args)
577 {
578   int retval;
579   const char *tag_str;
580   gint tag_id;
581   MainWindow *mainwin;
582
583   retval = PyArg_ParseTuple(args, "s", &tag_str);
584   if(!retval)
585     return NULL;
586
587   tag_id = tags_get_id_for_str(tag_str);
588   if(tag_id == -1) {
589     PyErr_SetString(PyExc_KeyError, "Tag does not exist");
590     return NULL;
591   }
592
593   tags_remove_tag(tag_id);
594
595   /* update display */
596   mainwin = mainwindow_get_mainwindow();
597   if(mainwin)
598     summary_redisplay_msg(mainwin->summaryview);
599
600   Py_RETURN_NONE;
601 }
602
603
604 static PyObject* rename_tag(PyObject *self, PyObject *args)
605 {
606   int retval;
607   const char *old_tag_str;
608   const char *new_tag_str;
609   gint tag_id;
610   MainWindow *mainwin;
611
612   retval = PyArg_ParseTuple(args, "ss", &old_tag_str, &new_tag_str);
613   if(!retval)
614     return NULL;
615
616   if((IS_NOT_RESERVED_TAG(new_tag_str) == FALSE) ||(IS_NOT_RESERVED_TAG(old_tag_str) == FALSE)) {
617     PyErr_SetString(PyExc_ValueError, "Tag name is reserved");
618     return NULL;
619   }
620
621   tag_id = tags_get_id_for_str(old_tag_str);
622   if(tag_id == -1) {
623     PyErr_SetString(PyExc_KeyError, "Tag does not exist");
624     return NULL;
625   }
626
627   tags_update_tag(tag_id, new_tag_str);
628
629   /* update display */
630   mainwin = mainwindow_get_mainwindow();
631   if(mainwin)
632     summary_redisplay_msg(mainwin->summaryview);
633
634   Py_RETURN_NONE;
635 }
636
637 static gboolean get_message_list_for_move_or_copy(PyObject *messagelist, PyObject *folder, GSList **list)
638 {
639   Py_ssize_t size, iEl;
640   FolderItem *folderitem;
641
642   *list = NULL;
643
644   folderitem = clawsmail_folder_get_item(folder);
645   if(!folderitem) {
646     PyErr_SetString(PyExc_LookupError, "Brokern Folder object.");
647     return FALSE;
648   }
649
650   size = PyList_Size(messagelist);
651   for(iEl = 0; iEl < size; iEl++) {
652     PyObject *element = PyList_GET_ITEM(messagelist, iEl);
653     MsgInfo *msginfo;
654
655     if(!element || !PyObject_TypeCheck(element, clawsmail_messageinfo_get_type_object())) {
656       PyErr_SetString(PyExc_TypeError, "Argument must be a list of MessageInfo objects.");
657       return FALSE;
658     }
659
660     msginfo = clawsmail_messageinfo_get_msginfo(element);
661     if(!msginfo) {
662       PyErr_SetString(PyExc_LookupError, "Broken MessageInfo object.");
663       return FALSE;
664     }
665
666     procmsg_msginfo_set_to_folder(msginfo, folderitem);
667     *list = g_slist_prepend(*list, msginfo);
668   }
669
670   return TRUE;
671 }
672
673 static PyObject* move_or_copy_messages(PyObject *self, PyObject *args, gboolean move)
674 {
675   PyObject *messagelist;
676   PyObject *folder;
677   int retval;
678   GSList *list = NULL;
679
680   retval = PyArg_ParseTuple(args, "O!O!",
681     &PyList_Type, &messagelist,
682     clawsmail_folder_get_type_object(), &folder);
683   if(!retval )
684     return NULL;
685
686   folder_item_update_freeze();
687
688   if(!get_message_list_for_move_or_copy(messagelist, folder, &list))
689     goto err;
690
691   if(move)
692     procmsg_move_messages(list);
693   else
694     procmsg_copy_messages(list);
695
696   folder_item_update_thaw();
697   g_slist_free(list);
698   Py_RETURN_NONE;
699
700 err:
701   folder_item_update_thaw();
702   g_slist_free(list);
703   return NULL;
704 }
705
706 static PyObject* move_messages(PyObject *self, PyObject *args)
707 {
708   return move_or_copy_messages(self, args, TRUE);
709 }
710
711
712 static PyObject* copy_messages(PyObject *self, PyObject *args)
713 {
714   return move_or_copy_messages(self, args, FALSE);
715 }
716
717 static PyObject* get_current_account(PyObject *self, PyObject *args)
718 {
719   PrefsAccount *account;
720   account = account_get_cur_account();
721   if(account) {
722     return clawsmail_account_new(account);
723   }
724   else
725     Py_RETURN_NONE;
726 }
727
728 static PyObject* get_default_account(PyObject *self, PyObject *args)
729 {
730   PrefsAccount *account;
731   account = account_get_default();
732   if(account) {
733     return clawsmail_account_new(account);
734   }
735   else
736     Py_RETURN_NONE;
737 }
738
739
740 static PyMethodDef ClawsMailMethods[] = {
741     /* public */
742     {"get_mainwindow_action_group",  get_mainwindow_action_group, METH_NOARGS,
743      "get_mainwindow_action_group() - get action group of main window menu\n"
744      "\n"
745      "Returns the gtk.ActionGroup for the main window."},
746
747     {"get_mainwindow_ui_manager",  get_mainwindow_ui_manager, METH_NOARGS,
748      "get_mainwindow_ui_manager() - get ui manager of main window\n"
749      "\n"
750      "Returns the gtk.UIManager for the main window."},
751
752     {"get_folder_tree",  get_folder_tree, METH_VARARGS,
753      "get_folder_tree([root]) - get a folder tree\n"
754      "\n"
755      "Without arguments, get a list of folder trees for all mailboxes.\n"
756      "\n"
757      "If the optional root argument is a clawsmail.Folder, the function\n"
758      "returns a tree of subfolders with the given folder as root element.\n"
759      "\n"
760      "If the optional root argument is a clawsmail.Mailbox, the function\n"
761      "returns a tree of folders with the given mailbox as root element.\n"
762      "\n"
763      "If the optional root argument is a string, it is supposed to be a\n"
764      "mailbox name. The function then returns a tree of folders of that mailbox.\n"
765      "\n"
766      "In any case, a tree consists of elements of the type clawsmail.Node."},
767
768     {"get_folderview_selected_folder",  get_folderview_selected_folder, METH_NOARGS,
769      "get_folderview_selected_folder() - get selected folder in folderview\n"
770      "\n"
771      "Returns the currently selected folder as a clawsmail.Folder or None if no folder is selected."},
772     {"folderview_select_folder",  folderview_select_row, METH_VARARGS,
773      "folderview_select_folder(folder) - select folder in folderview\n"
774      "\n"
775      "Takes an argument of type clawsmail.Folder, and selects the corresponding folder.\n"
776      "\n"
777      "DEPRECATED: Use folderview_select() instead."},
778
779     {"get_folderview_selected_mailbox",  get_folderview_selected_mailbox, METH_NOARGS,
780      "get_folderview_selected_mailbox() - get selected mailbox in folderview\n"
781      "\n"
782      "Returns the currently selected mailbox as a clawsmail.Mailbox or None if no mailbox is selected."},
783
784      {"folderview_select",  folderview_select_row, METH_VARARGS,
785       "folderview_select(arg) - select folder or a mailbox in folderview\n"
786       "\n"
787       "Takes an argument of type clawsmail.Folder or clawsmail.Mailbox, and selects the corresponding\n"
788       "row in the folder view."},
789
790     {"quicksearch_search", quicksearch_search, METH_VARARGS,
791      "quicksearch_search(string [, type]) - perform a quicksearch\n"
792      "\n"
793      "Perform a quicksearch of the given string. The optional type argument can be\n"
794      "one of clawsmail.QUICK_SEARCH_SUBJECT, clawsmail.QUICK_SEARCH_FROM, clawsmail.QUICK_SEARCH_TO,\n"
795      "clawsmail.QUICK_SEARCH_EXTENDED, clawsmail.QUICK_SEARCH_MIXED, or clawsmail.QUICK_SEARCH_TAG.\n"
796      "If it is omitted, the current selection is used. The string argument has to be a valid search\n"
797      "string corresponding to the type."},
798
799     {"quicksearch_clear", quicksearch_clear, METH_NOARGS,
800      "quicksearch_clear() - clear the quicksearch"},
801
802     {"get_summaryview_selected_message_list", get_summaryview_selected_message_list, METH_NOARGS,
803      "get_summaryview_selected_message_list() - get selected message list\n"
804      "\n"
805      "Get a list of clawsmail.MessageInfo objects of the current selection."},
806
807     {"summaryview_select_messages", summaryview_select_messages, METH_VARARGS,
808      "summaryview_select_messages(message_list) - select a list of messages in the summary view\n"
809      "\n"
810      "Select a list of clawsmail.MessageInfo objects in the summary view."},
811
812     {"is_exiting", is_exiting, METH_NOARGS,
813      "is_exiting() - test whether Claws Mail is currently exiting\n"
814      "\n"
815      "Returns True if Claws Mail is currently exiting. The most common usage for this is to skip\n"
816      "unnecessary cleanup tasks in a shutdown script when Claws Mail is exiting anyways. If the Python\n"
817      "plugin is explicitly unloaded, the shutdown script will still be called, but this function will\n"
818      "return False."},
819
820     {"move_messages", move_messages, METH_VARARGS,
821      "move_messages(message_list, target_folder) - move a list of messages to a target folder\n"
822      "\n"
823      "Move a list of clawsmail.MessageInfo objects to a target folder.\n"
824      "The target_folder argument has to be a clawsmail.Folder object."},
825
826     {"copy_messages", copy_messages, METH_VARARGS,
827      "copy_messages(message_list, target_folder) - copy a list of messages to a target folder\n"
828      "\n"
829      "Copy a list of clawsmail.MessageInfo objects to a target folder.\n"
830      "The target_folder argument has to be a clawsmail.Folder object."},
831
832     {"get_tags", get_tags, METH_NOARGS,
833      "get_tags() - get a tuple of all tags that Claws Mail knows about\n"
834      "\n"
835      "Get a tuple of strings representing all tags that are defined in Claws Mail."},
836
837     {"make_sure_tag_exists", make_sure_tag_exists, METH_VARARGS,
838      "make_sure_tag_exists(tag) - make sure that the specified tag exists\n"
839      "\n"
840      "This function creates the given tag if it does not exist yet.\n"
841      "It is not an error if the tag already exists. In this case, this function does nothing.\n"
842      "However, if a reserved tag name is chosen, a ValueError exception is raised."},
843
844     {"delete_tag", delete_tag, METH_VARARGS,
845      "delete_tag(tag) - delete a tag\n"
846      "\n"
847      "This function deletes an existing tag.\n"
848      "Raises a KeyError exception if the tag does not exist."},
849
850     {"rename_tag", rename_tag, METH_VARARGS,
851      "rename_tag(old_tag, new_tag) - rename tag old_tag to new_tag\n"
852      "\n"
853      "This function renames an existing tag.\n"
854      "Raises a KeyError exception if the tag does not exist.\n"
855      "Raises a ValueError exception if the old or new tag name is a reserved name."},
856
857      {"get_accounts", get_accounts, METH_NOARGS,
858       "get_accounts() - get a tuple of all accounts that Claws Mail knows about\n"
859       "\n"
860       "Get a tuple of Account objects representing all accounts that are defined in Claws Mail."},
861
862       {"get_current_account", get_current_account, METH_NOARGS,
863        "get_current_account() - get the current account\n"
864        "\n"
865        "Return the object representing the currently selected account."},
866
867      {"get_default_account", get_default_account, METH_NOARGS,
868       "get_default_account() - get the default account\n"
869       "\n"
870       "Return the object representing the default account."},
871
872      {"get_mailboxes", get_mailboxes, METH_NOARGS,
873       "get_mailboxes() - get a tuple of all mailboxes that Claws Mail knows about\n"
874       "\n"
875       "Get a tuple of Mailbox objects representing all mailboxes that are defined in Claws Mail."},
876
877      /* private */
878     {"__gobj", private_wrap_gobj, METH_VARARGS,
879      "__gobj(ptr) - transforms a C GObject pointer into a PyGObject\n"
880      "\n"
881      "For internal usage only."},
882
883     {NULL, NULL, 0, NULL}
884 };
885
886 static gboolean add_miscstuff(PyObject *module)
887 {
888   gboolean retval;
889   PyObject *dict;
890   PyObject *res;
891   const char *cmd =
892       "QUICK_SEARCH_SUBJECT = 0\n"
893       "QUICK_SEARCH_FROM = 1\n"
894       "QUICK_SEARCH_TO = 2\n"
895       "QUICK_SEARCH_EXTENDED = 3\n"
896       "QUICK_SEARCH_MIXED = 4\n"
897       "QUICK_SEARCH_TAG = 5\n"
898       "\n";
899   dict = PyModule_GetDict(module);
900   res = PyRun_String(cmd, Py_file_input, dict, dict);
901   retval = (res != NULL);
902   Py_XDECREF(res);
903   return retval;
904 }
905
906
907 PyMODINIT_FUNC initclawsmail(void)
908 {
909   gboolean ok = TRUE;
910
911   /* create module */
912   cm_module = Py_InitModule3("clawsmail", ClawsMailMethods,
913       "This module can be used to access some of Claws Mail's data structures\n"
914       "in order to extend or modify the user interface or automate repetitive tasks.\n"
915       "\n"
916       "Whenever possible, the interface works with standard GTK+ widgets\n"
917       "via the PyGTK bindings, so you can refer to the GTK+ / PyGTK documentation\n"
918       "to find out about all possible options.\n"
919       "\n"
920       "The interface to Claws Mail in this module is extended on a 'as-needed' basis.\n"
921       "If you're missing something specific, try contacting the author.");
922
923   /* add module member "compose_window" set to None */
924   Py_INCREF(Py_None);
925   if (PyModule_AddObject(cm_module, "compose_window", Py_None) == -1)
926           debug_print("Error: Could not add object 'compose_window'\n");
927
928   /* initialize classes */
929   ok = ok && cmpy_add_node(cm_module);
930   ok = ok && cmpy_add_composewindow(cm_module);
931   ok = ok && cmpy_add_folder(cm_module);
932   ok = ok && cmpy_add_messageinfo(cm_module);
933   ok = ok && cmpy_add_account(cm_module);
934   ok = ok && cmpy_add_folderproperties(cm_module);
935   ok = ok && cmpy_add_mailbox(cm_module);
936
937   /* initialize misc things */
938   if(ok)
939     add_miscstuff(cm_module);
940 }
941
942
943 void put_composewindow_into_module(Compose *compose)
944 {
945   PyObject *pycompose;
946
947   pycompose = clawsmail_compose_new(cm_module, compose);
948   PyObject_SetAttrString(cm_module, "compose_window", pycompose);
949   Py_DECREF(pycompose);
950 }