Fix warning: excess elements in struct initializer
[claws.git] / src / plugins / python / composewindowtype.c
1 /* Python plugin for Claws-Mail
2  * Copyright (C) 2009 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 #ifdef HAVE_CONFIG_H
19 #  include "config.h"
20 #  include "claws-features.h"
21 #endif
22
23 #include "composewindowtype.h"
24 #include "accounttype.h"
25
26 #include "clawsmailmodule.h"
27 #include "foldertype.h"
28 #include "messageinfotype.h"
29
30 #include "mainwindow.h"
31 #include "account.h"
32 #include "summaryview.h"
33 #include "gtk/combobox.h"
34
35 #include <glib.h>
36 #include <glib/gi18n.h>
37
38 #include <structmember.h>
39
40 #include <string.h>
41
42 typedef struct {
43     PyObject_HEAD
44     PyObject *ui_manager;
45     PyObject *text;
46     PyObject *replyinfo;
47     PyObject *fwdinfo;
48     Compose *compose;
49 } clawsmail_ComposeWindowObject;
50
51 static void ComposeWindow_dealloc(clawsmail_ComposeWindowObject* self)
52 {
53   Py_XDECREF(self->ui_manager);
54   Py_XDECREF(self->text);
55   Py_XDECREF(self->replyinfo);
56   Py_XDECREF(self->fwdinfo);
57   self->ob_type->tp_free((PyObject*)self);
58 }
59
60 static void flush_gtk_queue(void)
61 {
62   while(gtk_events_pending())
63     gtk_main_iteration();
64 }
65
66 static void store_py_object(PyObject **target, PyObject *obj)
67 {
68   Py_XDECREF(*target);
69   if(obj)
70   {
71     Py_INCREF(obj);
72     *target = obj;
73   }
74   else {
75     Py_INCREF(Py_None);
76     *target = Py_None;
77   }
78 }
79
80 static void composewindow_set_compose(clawsmail_ComposeWindowObject *self, Compose *compose)
81 {
82   self->compose = compose;
83
84   store_py_object(&(self->ui_manager), get_gobj_from_address(compose->ui_manager));
85   store_py_object(&(self->text), get_gobj_from_address(compose->text));
86
87   store_py_object(&(self->replyinfo), clawsmail_messageinfo_new(compose->replyinfo));
88   store_py_object(&(self->fwdinfo), clawsmail_messageinfo_new(compose->fwdinfo));
89 }
90
91 static int ComposeWindow_init(clawsmail_ComposeWindowObject *self, PyObject *args, PyObject *kwds)
92 {
93   MainWindow *mainwin;
94   PrefsAccount *ac = NULL;
95   FolderItem *item;
96   GList* list;
97   GList* cur;
98   gboolean did_find_compose;
99   Compose *compose = NULL;
100   const char *ss;
101   unsigned char open_window;
102   /* if __open_window is set to 0/False,
103    * composewindow_set_compose must be called before this object is valid */
104   static char *kwlist[] = {"address", "__open_window", NULL};
105
106   ss = NULL;
107   open_window = 1;
108   PyArg_ParseTupleAndKeywords(args, kwds, "|sb", kwlist, &ss, &open_window);
109
110   if(open_window) {
111     mainwin = mainwindow_get_mainwindow();
112     item = mainwin->summaryview->folder_item;
113     did_find_compose = FALSE;
114
115     if(ss) {
116       ac = account_find_from_address(ss, FALSE);
117       if (ac && ac->protocol != A_NNTP) {
118         compose = compose_new_with_folderitem(ac, item, NULL);
119         did_find_compose = TRUE;
120       }
121     }
122     if(!did_find_compose) {
123       if (item) {
124         ac = account_find_from_item(item);
125         if (ac && ac->protocol != A_NNTP) {
126           compose = compose_new_with_folderitem(ac, item, NULL);
127           did_find_compose = TRUE;
128         }
129       }
130
131       /* use current account */
132       if (!did_find_compose && cur_account && (cur_account->protocol != A_NNTP)) {
133         compose = compose_new_with_folderitem(cur_account, item, NULL);
134         did_find_compose = TRUE;
135       }
136
137       if(!did_find_compose) {
138         /* just get the first one */
139         list = account_get_list();
140         for (cur = list ; cur != NULL ; cur = g_list_next(cur)) {
141           ac = (PrefsAccount *) cur->data;
142           if (ac->protocol != A_NNTP) {
143             compose = compose_new_with_folderitem(ac, item, NULL);
144             did_find_compose = TRUE;
145           }
146         }
147       }
148     }
149
150     if(!did_find_compose)
151       return -1;
152
153     composewindow_set_compose(self, compose);
154     gtk_widget_show_all(compose->window);
155     flush_gtk_queue();
156   }
157   return 0;
158 }
159
160 /* this is here because wrapping GTK_EDITABLEs in PyGTK is buggy */
161 static PyObject* get_python_object_from_gtk_entry(GtkWidget *entry)
162 {
163   return Py_BuildValue("s", gtk_entry_get_text(GTK_ENTRY(entry)));
164 }
165
166 static PyObject* set_gtk_entry_from_python_object(GtkWidget *entry, PyObject *args)
167 {
168   const char *ss;
169
170   if(!PyArg_ParseTuple(args, "s", &ss))
171     return NULL;
172
173   gtk_entry_set_text(GTK_ENTRY(entry), ss);
174
175   Py_INCREF(Py_None);
176   return Py_None;
177 }
178
179 static PyObject* ComposeWindow_get_subject(clawsmail_ComposeWindowObject *self, PyObject *args)
180 {
181   return get_python_object_from_gtk_entry(self->compose->subject_entry);
182 }
183
184 static PyObject* ComposeWindow_set_subject(clawsmail_ComposeWindowObject *self, PyObject *args)
185 {
186   PyObject *ret;
187   ret = set_gtk_entry_from_python_object(self->compose->subject_entry, args);
188   flush_gtk_queue();
189   return ret;
190 }
191
192 static PyObject* ComposeWindow_get_from(clawsmail_ComposeWindowObject *self, PyObject *args)
193 {
194   return get_python_object_from_gtk_entry(self->compose->from_name);
195 }
196
197 static PyObject* ComposeWindow_set_from(clawsmail_ComposeWindowObject *self, PyObject *args)
198 {
199   PyObject *ret;
200   ret = set_gtk_entry_from_python_object(self->compose->from_name, args);
201   flush_gtk_queue();
202   return ret;
203 }
204
205 static PyObject* ComposeWindow_add_To(clawsmail_ComposeWindowObject *self, PyObject *args)
206 {
207   const char *ss;
208
209   if(!PyArg_ParseTuple(args, "s", &ss))
210     return NULL;
211
212   compose_entry_append(self->compose, ss, COMPOSE_TO, PREF_NONE);
213
214   flush_gtk_queue();
215
216   Py_INCREF(Py_None);
217   return Py_None;
218 }
219
220 static PyObject* ComposeWindow_add_Cc(clawsmail_ComposeWindowObject *self, PyObject *args)
221 {
222   const char *ss;
223
224   if(!PyArg_ParseTuple(args, "s", &ss))
225     return NULL;
226
227   compose_entry_append(self->compose, ss, COMPOSE_CC, PREF_NONE);
228
229   flush_gtk_queue();
230
231   Py_INCREF(Py_None);
232   return Py_None;
233 }
234
235 static PyObject* ComposeWindow_add_Bcc(clawsmail_ComposeWindowObject *self, PyObject *args)
236 {
237   const char *ss;
238
239   if(!PyArg_ParseTuple(args, "s", &ss))
240     return NULL;
241
242   compose_entry_append(self->compose, ss, COMPOSE_BCC, PREF_NONE);
243
244   flush_gtk_queue();
245
246   Py_INCREF(Py_None);
247   return Py_None;
248 }
249
250 static PyObject* ComposeWindow_attach(clawsmail_ComposeWindowObject *self, PyObject *args)
251 {
252   PyObject *olist;
253   Py_ssize_t size, iEl;
254   GList *list = NULL;
255
256   if(!PyArg_ParseTuple(args, "O!", &PyList_Type, &olist))
257     return NULL;
258
259   size = PyList_Size(olist);
260   for(iEl = 0; iEl < size; iEl++) {
261     char *ss;
262     PyObject *element = PyList_GET_ITEM(olist, iEl);
263
264     if(!element)
265       continue;
266
267     Py_INCREF(element);
268     if(!PyArg_Parse(element, "s", &ss)) {
269       Py_DECREF(element);
270       if(list)
271         g_list_free(list);
272       return NULL;
273     }
274     list = g_list_prepend(list, ss);
275     Py_DECREF(element);
276   }
277
278   compose_attach_from_list(self->compose, list, FALSE);
279   g_list_free(list);
280
281   flush_gtk_queue();
282
283   Py_INCREF(Py_None);
284   return Py_None;
285 }
286
287 static PyObject* ComposeWindow_get_header_list(clawsmail_ComposeWindowObject *self, PyObject *args)
288 {
289   GSList *walk;
290   PyObject *retval;
291
292   retval = Py_BuildValue("[]");
293   for(walk = self->compose->header_list; walk; walk = walk->next) {
294     ComposeHeaderEntry *headerentry = walk->data;
295     const gchar *header;
296     const gchar *text;
297
298     header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(headerentry->combo))), 0, -1);
299     text = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
300
301     if(text && strcmp("", text)) {
302       PyObject *ee;
303       int ok;
304
305       ee = Py_BuildValue("(ss)", header, text);
306       ok = PyList_Append(retval, ee);
307       Py_DECREF(ee);
308       if(ok == -1) {
309         Py_DECREF(retval);
310         return NULL;
311       }
312     }
313   }
314   return retval;
315 }
316
317 static PyObject* ComposeWindow_set_header_list(clawsmail_ComposeWindowObject *self, PyObject *args)
318 {
319   gint num;
320   GSList *walk;
321   PyObject *headerlist;
322   Py_ssize_t headerlistsize;
323   Py_ssize_t iEl;
324
325   if(!PyArg_ParseTuple(args, "O!", &PyList_Type, &headerlist))
326     return NULL;
327
328   headerlistsize = PyList_Size(headerlist);
329   num = g_slist_length(self->compose->header_list);
330
331   /* check correctness of argument before deleting old content */
332   for(iEl = 0; iEl < headerlistsize; iEl++) {
333     PyObject *element;
334     PyObject *headerfield;
335     PyObject *headercontent;
336
337     /* check that we got a list of tuples with two elements */
338     element = PyList_GET_ITEM(headerlist, iEl);
339     if(!element || !PyObject_TypeCheck(element, &PyTuple_Type) || (PyTuple_Size(element) != 2)) {
340       PyErr_SetString(PyExc_LookupError, "Argument to set_header_list() must be a list of tuples with two strings");
341       return NULL;
342     }
343
344     /* check that the two tuple elements are strings */
345     headerfield = PyTuple_GetItem(element, 0);
346     headercontent = PyTuple_GetItem(element, 1);
347     if(!headerfield || !headercontent
348         || !PyObject_TypeCheck(headerfield, &PyString_Type) || !PyObject_TypeCheck(headercontent, &PyString_Type)) {
349       PyErr_SetString(PyExc_LookupError, "Argument to set_header_list() must be a list of tuples with two strings");
350       return NULL;
351     }
352   }
353
354   /* delete old headers */
355   for(walk = self->compose->header_list; walk; walk = walk->next) {
356     ComposeHeaderEntry *headerentry = walk->data;
357     gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
358   }
359
360   /* if given header list is bigger than current header list, add dummy values */
361   while(num < headerlistsize) {
362     compose_entry_append(self->compose, "dummy1dummy2dummy3", COMPOSE_TO, PREF_NONE);
363     num++;
364   }
365
366   /* set headers to new values */
367   for(iEl = 0; iEl < headerlistsize; iEl++) {
368     PyObject *element;
369     PyObject *headerfield;
370     PyObject *headercontent;
371     ComposeHeaderEntry *headerentry;
372     GtkEditable *editable;
373     gint pos;
374
375     element = PyList_GET_ITEM(headerlist, iEl);
376     headerfield = PyTuple_GetItem(element, 0);
377     headercontent = PyTuple_GetItem(element, 1);
378
379     headerentry = g_slist_nth_data(self->compose->header_list, iEl);
380
381     /* set header field */
382     editable = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(headerentry->combo)));
383     gtk_editable_delete_text(editable, 0, -1);
384     gtk_editable_insert_text(editable, PyString_AsString(headerfield), -1, &pos);
385
386     /* set header content */
387     gtk_entry_set_text(GTK_ENTRY(headerentry->entry), PyString_AsString(headercontent));
388   }
389
390   Py_INCREF(Py_None);
391   return Py_None;
392 }
393
394 static PyObject* ComposeWindow_add_header(clawsmail_ComposeWindowObject *self, PyObject *args)
395 {
396   const char *header;
397   const char *text;
398   gint num;
399
400   if(!PyArg_ParseTuple(args, "ss", &header, &text))
401     return NULL;
402
403   /* add a dummy, and modify it then */
404   compose_entry_append(self->compose, "dummy1dummy2dummy3", COMPOSE_TO, PREF_NONE);
405   num = g_slist_length(self->compose->header_list);
406   if(num > 1) {
407     ComposeHeaderEntry *headerentry;
408     headerentry = g_slist_nth_data(self->compose->header_list, num-2);
409     if(headerentry) {
410       GtkEditable *editable;
411       gint pos;
412       gtk_entry_set_text(GTK_ENTRY(headerentry->entry), text);
413       editable = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(headerentry->combo)));
414       gtk_editable_delete_text(editable, 0, -1);
415       gtk_editable_insert_text(editable, header, -1, &pos);
416     }
417   }
418
419   flush_gtk_queue();
420
421   Py_INCREF(Py_None);
422   return Py_None;
423 }
424
425 static PyObject* ComposeWindow_get_account_selection(clawsmail_ComposeWindowObject *self, PyObject *args)
426 {
427   if(GTK_IS_COMBO_BOX(self->compose->account_combo))
428     return get_gobj_from_address(self->compose->account_combo);
429
430   Py_INCREF(Py_None);
431   return Py_None;
432 }
433
434 static PyObject* ComposeWindow_save_message_to(clawsmail_ComposeWindowObject *self, PyObject *args)
435 {
436   PyObject *arg;
437
438   if(!PyArg_ParseTuple(args, "O", &arg))
439     return NULL;
440
441   if(PyString_Check(arg)) {
442     GtkEditable *editable;
443     gint pos;
444
445     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->compose->savemsg_checkbtn), TRUE);
446
447     editable = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(self->compose->savemsg_combo)));
448     gtk_editable_delete_text(editable, 0, -1);
449     gtk_editable_insert_text(editable, PyString_AsString(arg), -1, &pos);
450   }
451   else if(clawsmail_folder_check(arg)) {
452     GtkEditable *editable;
453     gint pos;
454
455     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->compose->savemsg_checkbtn), TRUE);
456
457     editable = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(self->compose->savemsg_combo)));
458     gtk_editable_delete_text(editable, 0, -1);
459     gtk_editable_insert_text(editable, folder_item_get_identifier(clawsmail_folder_get_item(arg)), -1, &pos);
460   }
461   else if (arg == Py_None){
462     /* turn off checkbutton */
463     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->compose->savemsg_checkbtn), FALSE);
464   }
465   else {
466     PyErr_SetString(PyExc_TypeError, "function takes exactly one argument which may be a folder object, a string, or None");
467     return NULL;
468   }
469
470   flush_gtk_queue();
471
472   Py_INCREF(Py_None);
473   return Py_None;
474 }
475
476 static PyObject* ComposeWindow_set_modified(clawsmail_ComposeWindowObject *self, PyObject *args)
477 {
478   char modified = 0;
479   gboolean old_modified;
480
481   if(!PyArg_ParseTuple(args, "b", &modified))
482     return NULL;
483
484   old_modified = self->compose->modified;
485
486   self->compose->modified = (modified != 0);
487
488   /* If the modified state changed, rewrite window title.
489    * This partly duplicates functionality in compose.c::compose_set_title().
490    * While it's nice to not have to modify Claws Mail for this to work,
491    * it would be cleaner to export that function in Claws Mail. */
492   if((strcmp(gtk_window_get_title(GTK_WINDOW(self->compose->window)), _("Compose message")) != 0) &&
493       (old_modified != self->compose->modified)) {
494       gchar *str;
495       gchar *edited;
496       gchar *subject;
497
498       edited = self->compose->modified  ? _(" [Edited]") : "";
499       subject = gtk_editable_get_chars(GTK_EDITABLE(self->compose->subject_entry), 0, -1);
500       if(subject && strlen(subject))
501         str = g_strdup_printf(_("%s - Compose message%s"),
502             subject, edited);
503       else
504         str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
505       gtk_window_set_title(GTK_WINDOW(self->compose->window), str);
506       g_free(str);
507       g_free(subject);
508   }
509
510   flush_gtk_queue();
511
512   Py_INCREF(Py_None);
513   return Py_None;
514 }
515
516 static PyObject* get_account(clawsmail_ComposeWindowObject *self, void *closure)
517 {
518   if(self->compose->account) {
519     return clawsmail_account_new(self->compose->account);
520   }
521   Py_RETURN_NONE;
522 }
523
524 static int set_account(clawsmail_ComposeWindowObject *self, PyObject *value, void *closure)
525 {
526   PrefsAccount *target_account;
527
528   if(value == NULL) {
529     PyErr_SetString(PyExc_TypeError, "Cannot delete 'account' attribute");
530     return -1;
531   }
532
533   if(!clawsmail_account_check(value)) {
534     PyErr_SetString(PyExc_TypeError, "ComposeWindow.account: Can only assign an account");
535     return -1;
536   }
537
538
539   target_account = clawsmail_account_get_account(value);
540   if(!target_account) {
541     PyErr_SetString(PyExc_TypeError, "Account value broken");
542     return -1;
543   }
544
545   if(!self->compose || !self->compose->account_combo) {
546     PyErr_SetString(PyExc_RuntimeError, "ComposeWindow: Cannot access account");
547     return -1;
548   }
549
550   combobox_select_by_data(GTK_COMBO_BOX(self->compose->account_combo), target_account->account_id);
551
552   return 0;
553 }
554
555
556
557 static PyMethodDef ComposeWindow_methods[] = {
558     {"set_subject", (PyCFunction)ComposeWindow_set_subject, METH_VARARGS,
559      "set_subject(text) - set subject to text\n"
560      "\n"
561      "Set the subject to text. text must be a string."},
562
563     {"get_subject", (PyCFunction)ComposeWindow_get_subject, METH_NOARGS,
564      "get_subject() - get subject\n"
565      "\n"
566      "Get a string of the current subject entry."},
567
568     {"set_from", (PyCFunction)ComposeWindow_set_from, METH_VARARGS,
569      "set_from(text) - set From header entry to text\n"
570      "\n"
571      "Set the From header entry to text. text must be a string.\n"
572      "Beware: No sanity checking is performed."},
573
574     {"get_from", (PyCFunction)ComposeWindow_get_from, METH_NOARGS,
575      "get_from - get From header entry\n"
576      "\n"
577      "Get a string of the current From header entry."},
578
579     {"add_To",  (PyCFunction)ComposeWindow_add_To,  METH_VARARGS,
580      "add_To(text) - append another To header with text\n"
581      "\n"
582      "Add another header line with the combo box set to To:, and the\n"
583      "content set to text."},
584
585     {"add_Cc",  (PyCFunction)ComposeWindow_add_Cc,  METH_VARARGS,
586      "add_Cc(text) - append another Cc header with text\n"
587      "\n"
588      "Add another header line with the combo box set to Cc:, and the\n"
589      "content set to text."},
590
591     {"add_Bcc", (PyCFunction)ComposeWindow_add_Bcc, METH_VARARGS,
592      "add_Bcc(text) - append another Bcc header with text\n"
593      "\n"
594      "Add another header line with the combo box set to Bcc:, and the\n"
595      "content set to text."},
596
597     {"add_header", (PyCFunction)ComposeWindow_add_header, METH_VARARGS,
598      "add_header(headername, text) - add a custom header\n"
599      "\n"
600      "Adds a custom header with the header set to headername, and the\n"
601      "contents set to text."},
602
603     {"get_header_list", (PyCFunction)ComposeWindow_get_header_list, METH_NOARGS,
604      "get_header_list() - get list of headers\n"
605      "\n"
606      "Gets a list of headers that are currently defined in the compose window.\n"
607      "The return value is a list of tuples, where the first tuple element is\n"
608      "the header name (entry in the combo box) and the second element is the contents."},
609
610     {"set_header_list", (PyCFunction)ComposeWindow_set_header_list, METH_VARARGS,
611      "set_header_list(list_of_header_value_pairs) - set list of headers\n"
612      "\n"
613      "Sets the list of headers that are currently defined in the compose window.\n"
614      "This function overwrites the current setting.\n\n"
615      "The parameter is expected to be a list of header name and value tuples,\n"
616      "analogous to the return value of the get_header_list() function."},
617
618     {"attach",  (PyCFunction)ComposeWindow_attach, METH_VARARGS,
619      "attach(filenames) - attach a list of files\n"
620      "\n"
621      "Attach files to the mail. The filenames argument is a list of\n"
622      "string of the filenames that are being attached."},
623
624     {"get_account_selection", (PyCFunction)ComposeWindow_get_account_selection, METH_NOARGS,
625      "get_account_selection() - get account selection widget\n"
626      "\n"
627      "Returns the account selection combo box as a gtk.ComboBox"},
628
629     {"save_message_to", (PyCFunction)ComposeWindow_save_message_to, METH_VARARGS,
630      "save_message_to(folder) - save message to folder id\n"
631      "\n"
632      "Set the folder where the sent message will be saved to. folder may be\n"
633      "a Folder, a string of the folder identifier (e.g. #mh/foo/bar), or\n"
634      "None is which case the message will not be saved at all."},
635
636      {"set_modified", (PyCFunction)ComposeWindow_set_modified, METH_VARARGS,
637      "set_modified(bool) - set or unset modification marker of compose window\n"
638      "\n"
639      "Set or unset the modification marker of the compose window. This marker determines\n"
640      "for example whether you get a confirmation dialog when closing the compose window\n"
641      "or not.\n"
642      "In the usual case, Claws Mail keeps track of the modification status itself.\n"
643      "However, there are cases when it might be desirable to overwrite the marker,\n"
644      "for example because a compose_any script modifies the body or subject which\n"
645      "can be regarded compose window preprocessing and should not trigger a confirmation\n"
646      "dialog on close like a manual edit."},
647
648     {NULL}
649 };
650
651 static PyMemberDef ComposeWindow_members[] = {
652     {"ui_manager", T_OBJECT_EX, offsetof(clawsmail_ComposeWindowObject, ui_manager), 0,
653      "ui_manager - the gtk.UIManager of the compose window"},
654
655     {"text", T_OBJECT_EX, offsetof(clawsmail_ComposeWindowObject, text), 0,
656      "text - the gtk.TextView widget of the message body"},
657
658     {"replyinfo", T_OBJECT_EX, offsetof(clawsmail_ComposeWindowObject, replyinfo), 0,
659      "replyinfo - The MessageInfo object of the message that is being replied to, or None"},
660
661     {"forwardinfo", T_OBJECT_EX, offsetof(clawsmail_ComposeWindowObject, fwdinfo), 0,
662      "forwardinfo - The MessageInfo object of the message that is being forwarded, or None"},
663
664     {NULL}
665 };
666
667 static PyGetSetDef ComposeWindow_getset[] = {
668     {"account", (getter)get_account, (setter)set_account,
669       "account - the account corresponding to this compose window", NULL},
670
671     {NULL}
672 };
673
674 static PyTypeObject clawsmail_ComposeWindowType = {
675     PyObject_HEAD_INIT(NULL)
676     0,                         /*ob_size*/
677     "clawsmail.ComposeWindow", /*tp_name*/
678     sizeof(clawsmail_ComposeWindowObject), /*tp_basicsize*/
679     0,                         /*tp_itemsize*/
680     (destructor)ComposeWindow_dealloc, /*tp_dealloc*/
681     0,                         /*tp_print*/
682     0,                         /*tp_getattr*/
683     0,                         /*tp_setattr*/
684     0,                         /*tp_compare*/
685     0,                         /*tp_repr*/
686     0,                         /*tp_as_number*/
687     0,                         /*tp_as_sequence*/
688     0,                         /*tp_as_mapping*/
689     0,                         /*tp_hash */
690     0,                         /*tp_call*/
691     0,                         /*tp_str*/
692     0,                         /*tp_getattro*/
693     0,                         /*tp_setattro*/
694     0,                         /*tp_as_buffer*/
695     Py_TPFLAGS_DEFAULT,        /*tp_flags*/
696     /* tp_doc */
697     "ComposeWindow objects. Optional argument to constructor: sender account address. ",
698     0,                         /* tp_traverse */
699     0,                         /* tp_clear */
700     0,                         /* tp_richcompare */
701     0,                         /* tp_weaklistoffset */
702     0,                         /* tp_iter */
703     0,                         /* tp_iternext */
704     ComposeWindow_methods,     /* tp_methods */
705     ComposeWindow_members,     /* tp_members */
706     ComposeWindow_getset,      /* tp_getset */
707     0,                         /* tp_base */
708     0,                         /* tp_dict */
709     0,                         /* tp_descr_get */
710     0,                         /* tp_descr_set */
711     0,                         /* tp_dictoffset */
712     (initproc)ComposeWindow_init, /* tp_init */
713     0,                         /* tp_alloc */
714     0,                         /* tp_new */
715     0,                         /* tp_free */
716         0,                         /* tp_is_gc */
717         0,                         /* tp_bases */
718         0,                         /* tp_mro */
719         0,                         /* tp_cache */
720         0,                         /* tp_subclasses */
721         0,                         /* tp_weaklist */
722         0,                         /* tp_del */
723 #if ((PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6) || \
724      (PY_MAJOR_VERSION == 3))
725     0,                         /* tp_version_tag */
726 #endif
727 #if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 4)
728     0,                         /* tp_finalize */
729 #endif
730 };
731
732 gboolean cmpy_add_composewindow(PyObject *module)
733 {
734   clawsmail_ComposeWindowType.tp_new = PyType_GenericNew;
735   if(PyType_Ready(&clawsmail_ComposeWindowType) < 0)
736     return FALSE;
737
738   Py_INCREF(&clawsmail_ComposeWindowType);
739   return (PyModule_AddObject(module, "ComposeWindow", (PyObject*)&clawsmail_ComposeWindowType) == 0);
740 }
741
742 PyObject* clawsmail_compose_new(PyObject *module, Compose *compose)
743 {
744   PyObject *class, *dict;
745   PyObject *self, *args, *kw;
746
747   if(!compose) {
748     Py_INCREF(Py_None);
749     return Py_None;
750   }
751
752   dict = PyModule_GetDict(module);
753   class = PyDict_GetItemString(dict, "ComposeWindow");
754   args = Py_BuildValue("()");
755   kw = Py_BuildValue("{s:b}", "__open_window", 0);
756   self = PyObject_Call(class, args, kw);
757   Py_DECREF(args);
758   Py_DECREF(kw);
759   composewindow_set_compose((clawsmail_ComposeWindowObject*)self, compose);
760   return self;
761 }