Remove some deprecation warnings from python-shell
[claws.git] / src / plugins / vcalendar / vcal_meeting_gtk.c
blob309a1b1182b95d5a99cda77c18da6603aa755825
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2022 Colin Leroy <colin@colino.net> and
4 * the Claws Mail team
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #include "claws-features.h"
24 #endif
26 #include <stddef.h>
27 #include <glib.h>
28 #include <glib/gi18n.h>
30 #include "defs.h"
32 #ifdef USE_PTHREAD
33 #include <pthread.h>
34 #endif
35 #include <libical/ical.h>
36 #include <gtk/gtk.h>
37 #include <gdk/gdkkeysyms.h>
38 #include <curl/curl.h>
39 #include <curl/curlver.h>
40 #include "combobox.h"
42 #include "vcalendar.h"
43 #include "vcal_folder.h"
44 #include "vcal_manager.h"
45 #include "vcal_meeting_gtk.h"
46 #include "vcal_prefs.h"
47 #include "common-views.h"
48 #include "mainwindow.h"
49 #include "prefs_account.h"
50 #include "account.h"
51 #include "filesel.h"
52 #include "alertpanel.h"
53 #include "addr_compl.h"
54 #include "gtkutils.h"
55 #include "log.h"
56 #include "utils.h"
57 #include "file-utils.h"
59 struct _VCalMeeting
61 gchar *uid;
62 gint sequence;
63 gint method;
64 GtkWidget *window;
65 #ifndef GENERIC_UMPC
66 GtkWidget *table;
67 #else
68 GtkWidget *table1;
69 GtkWidget *table2;
70 #endif
71 GtkWidget *type;
72 GtkWidget *who;
73 GtkWidget *avail_evtbox;
74 GtkWidget *avail_img;
75 GtkWidget *start_c;
76 GtkWidget *start_time;
77 GtkWidget *end_c;
78 GtkWidget *end_time;
79 GtkWidget *location;
80 GtkWidget *summary;
81 GtkWidget *description;
82 GSList *attendees;
83 GtkWidget *attendees_vbox;
84 GtkWidget *save_btn;
85 GtkWidget *avail_btn;
86 GSList *avail_accounts;
87 GtkWidget *total_avail_evtbox;
88 GtkWidget *total_avail_img;
89 GtkWidget *total_avail_msg;
90 PrefsAccount *account;
91 gboolean visible;
92 gchar *created;
93 gchar *last_modified;
96 struct _VCalAttendee {
97 GtkWidget *address;
98 GtkWidget *remove_btn;
99 GtkWidget *add_btn;
100 GtkWidget *cutype;
101 GtkWidget *hbox;
102 VCalMeeting *meet;
103 gchar *status;
104 GtkWidget *avail_evtbox;
105 GtkWidget *avail_img;
106 gchar *cached_contents;
107 gboolean org;
110 static GdkCursor *watch_cursor = NULL;
112 VCalAttendee *attendee_add(VCalMeeting *meet, gchar *address, gchar *name, gchar *partstat, gchar *cutype, gboolean first);
114 #ifndef GENERIC_UMPC
115 #define TABLE_ADD_LINE(label_text, widget, do_space) { \
116 gchar *tmpstr = g_strdup_printf("<span weight=\"bold\">%s</span>", \
117 label_text?label_text:""); \
118 GtkWidget *label = NULL; \
119 GtkWidget *spacer = NULL; \
120 GtkWidget *s_hbox = NULL; \
121 if (do_space) { \
122 spacer = gtk_label_new(""); \
123 gtk_widget_set_size_request(spacer, 18, 16); \
124 s_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); \
125 gtk_box_pack_start(GTK_BOX(s_hbox), spacer, FALSE, FALSE, 0); \
126 gtk_box_pack_start(GTK_BOX(s_hbox), widget, TRUE, TRUE, 0); \
128 if (label_text) { \
129 label = gtk_label_new(tmpstr); \
130 g_free(tmpstr); \
131 gtk_label_set_use_markup (GTK_LABEL (label), TRUE); \
132 gtk_label_set_xalign (GTK_LABEL(label), 1.0); \
133 gtk_grid_attach(GTK_GRID(meet->table), label, 0, i, 1, 1); \
134 gtk_grid_attach(GTK_GRID(meet->table), do_space?s_hbox:widget, \
135 1, i, 1, 1); \
136 gtk_widget_set_hexpand(do_space?s_hbox:widget, TRUE); \
137 gtk_widget_set_halign(do_space?s_hbox:widget, GTK_ALIGN_FILL); \
138 if (GTK_IS_LABEL(widget)) { \
139 gtk_label_set_use_markup(GTK_LABEL (widget), TRUE); \
140 gtk_label_set_xalign(GTK_LABEL(widget), 0.0); \
141 gtk_label_set_yalign(GTK_LABEL(widget), 0.0); \
142 gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE); \
144 } else { \
145 g_free(tmpstr); \
146 gtk_grid_attach(GTK_GRID(meet->table), do_space?s_hbox:widget, \
147 0, i, 1, 1); \
148 gtk_widget_set_hexpand(do_space?s_hbox:widget, TRUE); \
149 gtk_widget_set_halign(do_space?s_hbox:widget, GTK_ALIGN_FILL); \
151 i++; \
153 #else
154 #define TABLE_ADD_LINE(label_text, widget, do_space, intable1) { \
155 gchar *tmpstr = g_strdup_printf("<span weight=\"bold\">%s</span>", \
156 label_text?label_text:""); \
157 GtkWidget *label = NULL; \
158 GtkWidget *spacer = NULL; \
159 GtkWidget *s_hbox = NULL; \
160 if (do_space) { \
161 spacer = gtk_label_new(""); \
162 gtk_widget_set_size_request(spacer, 18, 16); \
163 s_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); \
164 gtk_box_pack_start(GTK_BOX(s_hbox), spacer, FALSE, FALSE, 0); \
165 gtk_box_pack_start(GTK_BOX(s_hbox), widget, TRUE, TRUE, 0); \
167 if (label_text) { \
168 label = gtk_label_new(tmpstr); \
169 g_free(tmpstr); \
170 gtk_label_set_use_markup (GTK_LABEL (label), TRUE); \
171 gtk_label_set_xalign (GTK_LABEL(label), 1.0); \
172 if(intable1) { \
173 gtk_grid_attach(GTK_GRID(meet->table1), label, \
174 0, i, 1, 1); \
176 else { \
177 gtk_grid_attach(GTK_GRID(meet->table2), label, \
178 0, i, 1, 1); \
180 if(intable1) { \
181 gtk_grid_attach(GTK_GRID(meet->table1), \
182 do_space?s_hbox:widget, 1, i, 1, 1); \
184 else { \
185 gtk_grid_attach(GTK_GRID(meet->table2), \
186 do_space?s_hbox:widget, 1, i, 1, 1); \
188 if (GTK_IS_LABEL(widget)) { \
189 gtk_label_set_use_markup(GTK_LABEL (widget), TRUE); \
190 gtk_label_set_xalign(GTK_LABEL(widget), 0.0); \
191 gtk_label_set_yalign(GTK_LABEL(widget), 0.0); \
192 gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE); \
194 } else { \
195 g_free(tmpstr); \
196 if(intable1) { \
197 gtk_grid_attach(GTK_GRID(meet->table1), \
198 do_space?s_hbox:widget, 0, i, 1, 1); \
199 gtk_widget_set_hexpand(do_space?s_hbox:widget, TRUE); \
200 gtk_widget_set_halign(do_space?s_hbox:widget, \
201 GTK_ALIGN_FILL); \
203 else { \
204 gtk_grid_attach(GTK_GRID(meet->table2), \
205 do_space?s_hbox:widget, 0, i, 1, 1); \
206 gtk_widget_set_hexpand(do_space?s_hbox:widget, TRUE); \
207 gtk_widget_set_halign(do_space?s_hbox:widget, \
208 GTK_ALIGN_FILL); \
211 i++; \
213 #endif
214 enum {
215 DAY,
216 MONTH,
217 YEAR,
218 HOUR,
219 MINUTE
222 static gboolean avail_btn_can_be_sensitive(void)
224 if (vcalprefs.freebusy_get_url == NULL
225 || *vcalprefs.freebusy_get_url == '\0')
226 return FALSE;
227 else
228 return TRUE;
231 static gint get_dtdate(const gchar *str, gint field)
233 time_t t = icaltime_as_timet((icaltime_from_string(str)));
234 struct tm buft;
235 struct tm *lt;
237 tzset();
239 #ifdef G_OS_WIN32
240 if (t < 0)
241 t = 1;
242 #endif
243 lt = localtime_r(&t, &buft);
245 switch(field){
246 case DAY:
247 return lt->tm_mday;
248 case MONTH:
249 return lt->tm_mon + 1;
250 case YEAR:
251 return lt->tm_year + 1900;
252 case HOUR:
253 return lt->tm_hour;
254 case MINUTE:
255 return lt->tm_min;
257 return -1;
261 static void set_watch_cursor(GdkWindow *window)
263 cm_return_if_fail(window != NULL);
265 if (!watch_cursor)
266 watch_cursor = gdk_cursor_new_for_display(
267 gdk_window_get_display(window), GDK_WATCH);
271 static gboolean add_btn_cb(GtkButton *widget, gpointer data)
273 VCalAttendee *attendee = (VCalAttendee *)data;
274 attendee_add(attendee->meet, NULL, NULL, NULL, NULL, FALSE);
275 return TRUE;
278 static gboolean remove_btn_cb(GtkButton *widget, gpointer data)
280 VCalAttendee *attendee = (VCalAttendee *)data;
281 gtk_container_remove(GTK_CONTAINER(attendee->meet->attendees_vbox), attendee->hbox);
282 attendee->meet->attendees = g_slist_remove(attendee->meet->attendees, attendee);
284 g_free(attendee->status);
286 return TRUE;
289 VCalAttendee *attendee_add(VCalMeeting *meet, gchar *address, gchar *name, gchar *partstat, gchar *cutype, gboolean first)
291 GtkWidget *att_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
292 VCalAttendee *attendee = g_new0(VCalAttendee, 1);
294 attendee->address = gtk_entry_new();
295 attendee->cutype = gtk_combo_box_text_new();
296 attendee->avail_evtbox = gtk_event_box_new();
297 attendee->avail_img = gtk_image_new_from_icon_name
298 ("dialog-warning", GTK_ICON_SIZE_SMALL_TOOLBAR);
300 gtk_widget_show(attendee->address);
301 gtk_widget_show(attendee->cutype);
302 gtk_widget_show(attendee->avail_evtbox);
304 CLAWS_SET_TIP(attendee->address, _("Use <tab> to autocomplete from addressbook"));
305 gtk_widget_set_size_request(attendee->avail_evtbox, 18, 16);
306 gtk_event_box_set_visible_window(GTK_EVENT_BOX(attendee->avail_evtbox), FALSE);
307 gtk_container_add (GTK_CONTAINER(attendee->avail_evtbox), attendee->avail_img);
309 if (address) {
310 gchar *str = g_strdup_printf("%s%s%s%s",
311 (name && strlen(name))?name:"",
312 (name && strlen(name))?" <":"",
313 address,
314 (name && strlen(name))?">":"");
315 gtk_entry_set_text(GTK_ENTRY(attendee->address), str);
316 g_free(str);
319 if (partstat)
320 attendee->status = g_strdup(partstat);
322 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(attendee->cutype), _("Individual"));
323 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(attendee->cutype), _("Group"));
324 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(attendee->cutype), _("Resource"));
325 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(attendee->cutype), _("Room"));
327 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 0);
329 if (cutype) {
330 if (!strcmp(cutype, "group"))
331 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 1);
332 if (!strcmp(cutype, "resource"))
333 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 2);
334 if (!strcmp(cutype, "room"))
335 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 3);
338 attendee->add_btn = gtk_button_new_with_label(_("Add..."));
339 attendee->remove_btn = gtk_button_new_with_label(_("Remove"));
340 attendee->meet = meet;
341 attendee->hbox = att_hbox;
343 gtk_widget_show(attendee->add_btn);
344 gtk_widget_show(attendee->remove_btn);
345 gtk_widget_show(attendee->hbox);
347 gtk_box_pack_start(GTK_BOX(attendee->hbox), attendee->avail_evtbox, FALSE, FALSE, 0);
348 gtk_widget_set_sensitive(attendee->remove_btn, !first);
349 meet->attendees = g_slist_append(meet->attendees, attendee);
351 g_signal_connect(G_OBJECT(attendee->remove_btn), "clicked",
352 G_CALLBACK(remove_btn_cb), attendee);
353 g_signal_connect(G_OBJECT(attendee->add_btn), "clicked",
354 G_CALLBACK(add_btn_cb), attendee);
356 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->address, FALSE, FALSE, 0);
357 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->cutype, FALSE, FALSE, 0);
358 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->add_btn, FALSE, FALSE, 0);
359 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->remove_btn, FALSE, FALSE, 0);
360 gtk_box_pack_start(GTK_BOX(meet->attendees_vbox), att_hbox, FALSE, FALSE, 0);
361 address_completion_register_entry(GTK_ENTRY(attendee->address), FALSE);
362 #ifndef GENERIC_UMPC
363 gtk_widget_set_size_request(attendee->address, 320, -1);
364 #else
365 gtk_widget_set_size_request(attendee->address, 220, -1);
366 #endif
367 return attendee;
370 static gchar *get_organizer(VCalMeeting *meet)
372 int index = gtk_combo_box_get_active(GTK_COMBO_BOX(meet->who));
373 int i = 0;
374 GSList *cur = meet->avail_accounts;
375 while (i < index && cur && cur->data) {
376 debug_print("%d:skipping %s\n",i,((PrefsAccount *)(cur->data))->address);
377 cur = cur->next;
378 i++;
380 if (cur && cur->data)
381 return g_strdup(((PrefsAccount *)(cur->data))->address);
382 else
383 return g_strdup("");
386 static gchar *get_organizer_name(VCalMeeting *meet)
388 int index = gtk_combo_box_get_active(GTK_COMBO_BOX(meet->who));
389 int i = 0;
390 GSList *cur = meet->avail_accounts;
391 while (i < index && cur && cur->data) {
392 debug_print("%d:skipping %s\n",i,((PrefsAccount *)(cur->data))->address);
393 cur = cur->next;
394 i++;
396 if (cur && cur->data)
397 return g_strdup(((PrefsAccount *)(cur->data))->name);
398 else
399 return g_strdup("");
402 static int get_current_gmt_offset(void)
404 time_t now = time(NULL);
405 struct tm gmt;
406 struct tm local;
408 tzset();
410 #ifdef G_OS_WIN32
411 if (now < 0)
412 now = 1;
413 #endif
414 gmtime_r(& now, & gmt);
415 localtime_r(& now, & local);
417 local.tm_isdst = 0;
418 return mktime(&local)-mktime(&gmt);
421 static int get_gmt_offset_at_time(time_t then)
423 struct tm gmt;
424 struct tm local;
426 tzset();
428 #ifdef G_OS_WIN32
429 if (then < 0)
430 then = 1;
431 #endif
432 gmtime_r(& then, & gmt);
433 localtime_r(& then, & local);
435 local.tm_isdst = 0;
436 return mktime(&local)-mktime(&gmt);
439 static gchar *get_date(VCalMeeting *meet, int start)
441 struct tm *lt;
442 time_t t;
443 guint d, m, y;
444 int dst_offset = 0;
445 struct tm buft;
447 tzset();
449 t = time(NULL);
450 #ifdef G_OS_WIN32
451 if (t < 0)
452 t = 1;
453 #endif
454 lt = localtime_r(&t, &buft);
456 gtk_calendar_get_date(GTK_CALENDAR(start ? meet->start_c : meet->end_c), &y, &m, &d);
457 lt->tm_mday = d;
458 lt->tm_mon = m;
459 lt->tm_year = y - 1900;
460 lt->tm_hour = 0;
461 lt->tm_min = 0;
462 lt->tm_sec = 0;
464 if (start) {
465 gtkut_time_select_get_time(GTK_COMBO_BOX(meet->start_time), &lt->tm_hour, &lt->tm_min);
466 } else {
467 gtkut_time_select_get_time(GTK_COMBO_BOX(meet->end_time), &lt->tm_hour, &lt->tm_min);
470 debug_print("%d %d %d, %d:%d\n", lt->tm_mday, lt->tm_mon, lt->tm_year, lt->tm_hour, lt->tm_min);
471 t = mktime(lt);
473 dst_offset = get_current_gmt_offset() - get_gmt_offset_at_time(t);
474 debug_print("DST change offset to apply to time %d\n", dst_offset);
475 t += dst_offset;
476 debug_print("%s\n", ctime(&t));
477 return g_strdup(icaltime_as_ical_string(icaltime_from_timet_with_zone(t, FALSE, NULL)));
480 static gchar *get_location(VCalMeeting *meet)
482 return gtk_editable_get_chars(GTK_EDITABLE(meet->location),0, -1);
485 static gchar *get_summary(VCalMeeting *meet)
487 return gtk_editable_get_chars(GTK_EDITABLE(meet->summary),0, -1);
490 static gchar *get_description(VCalMeeting *meet)
492 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(meet->description));
493 GtkTextIter start, end;
495 gtk_text_buffer_get_start_iter(buffer, &start);
496 gtk_text_buffer_get_end_iter(buffer, &end);
497 return gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
500 void vcal_meeting_free(VCalMeeting *meet)
502 debug_print("freeing meeting\n");
503 g_free(meet->uid);
504 address_completion_end(meet->window);
505 g_slist_free(meet->avail_accounts);
506 g_slist_free(meet->attendees);
507 g_free(meet);
510 static void destroy_meeting_cb(GtkWidget *widget, gpointer data)
512 VCalMeeting *meet = (VCalMeeting *)data;
513 vcal_meeting_free(meet);
516 static void vcal_destroy(VCalMeeting *meet)
518 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(meet->description));
519 gtk_text_buffer_remove_selection_clipboard(buffer, gtk_clipboard_get(GDK_SELECTION_PRIMARY));
520 gtk_widget_destroy(meet->window);
523 static gboolean meeting_key_pressed(GtkWidget *widget,
524 GdkEventKey *event,
525 gpointer data)
527 VCalMeeting *meet = (VCalMeeting *)data;
529 if (event && event->keyval == GDK_KEY_Escape) {
530 vcal_destroy(meet);
532 return FALSE;
535 static void meeting_end_changed(GtkWidget *widget, gpointer data);
537 static void meeting_start_changed(GtkWidget *widget, gpointer data)
539 VCalMeeting *meet = (VCalMeeting *)data;
540 struct tm start_lt;
541 struct tm end_lt;
542 time_t start_t, end_t;
543 guint d, m, y;
545 if (!gtkut_time_select_get_time(GTK_COMBO_BOX(meet->start_time), &start_lt.tm_hour, &start_lt.tm_min))
546 return;
547 tzset();
549 start_t = time(NULL);
550 end_t = time(NULL);
551 #ifdef G_OS_WIN32
552 if (start_t < 0)
553 start_t = 1;
554 if (end_t < 0)
555 end_t = 1;
556 #endif
557 localtime_r(&start_t, &start_lt);
558 localtime_r(&end_t, &end_lt);
560 gtk_calendar_get_date(GTK_CALENDAR(meet->start_c), &y, &m, &d);
561 start_lt.tm_mday = d; start_lt.tm_mon = m; start_lt.tm_year = y - 1900;
563 start_t = mktime(&start_lt);
564 debug_print("start %s\n", ctime(&start_t));
566 gtk_calendar_get_date(GTK_CALENDAR(meet->end_c), &y, &m, &d);
567 end_lt.tm_mday = d; end_lt.tm_mon = m; end_lt.tm_year = y - 1900;
569 gtkut_time_select_get_time(GTK_COMBO_BOX(meet->end_time), &end_lt.tm_hour, &end_lt.tm_min);
571 end_t = mktime(&end_lt);
573 debug_print("end %s\n", ctime(&end_t));
575 if (end_t > start_t) {
576 debug_print("ok\n");
577 return;
579 end_t = start_t + 3600;
581 #ifdef G_OS_WIN32
582 if (end_t < 0)
583 end_t = 1;
584 #endif
585 localtime_r(&end_t, &end_lt);
586 debug_print("n %d %d %d, %d:%d\n", end_lt.tm_mday, end_lt.tm_mon, end_lt.tm_year, end_lt.tm_hour, end_lt.tm_min);
588 g_signal_handlers_block_by_func(gtk_bin_get_child(GTK_BIN(meet->end_time)), meeting_end_changed, meet);
589 g_signal_handlers_block_by_func(meet->end_c, meeting_end_changed, meet);
591 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c), end_lt.tm_mday);
593 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
594 end_lt.tm_mon,
595 end_lt.tm_year + 1900);
597 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->end_time), end_lt.tm_hour, end_lt.tm_min);
599 g_signal_handlers_unblock_by_func(gtk_bin_get_child(GTK_BIN(meet->end_time)), meeting_end_changed, meet);
600 g_signal_handlers_unblock_by_func(meet->end_c, meeting_end_changed, meet);
603 static void meeting_end_changed(GtkWidget *widget, gpointer data)
605 VCalMeeting *meet = (VCalMeeting *)data;
606 struct tm start_lt;
607 struct tm end_lt;
608 time_t start_t, end_t;
609 guint d, m, y;
611 if (!gtkut_time_select_get_time(GTK_COMBO_BOX(meet->end_time), &end_lt.tm_hour, &end_lt.tm_min))
612 return;
613 start_t = time(NULL);
614 end_t = time(NULL);
616 tzset();
618 #ifdef G_OS_WIN32
619 if (start_t < 0)
620 start_t = 1;
621 if (end_t < 0)
622 end_t = 1;
623 #endif
624 localtime_r(&start_t, &start_lt);
625 localtime_r(&end_t, &end_lt);
627 gtk_calendar_get_date(GTK_CALENDAR(meet->start_c), &y, &m, &d);
628 start_lt.tm_mday = d; start_lt.tm_mon = m; start_lt.tm_year = y - 1900;
629 gtkut_time_select_get_time(GTK_COMBO_BOX(meet->start_time), &start_lt.tm_hour, &start_lt.tm_min);
631 start_t = mktime(&start_lt);
632 debug_print("start %s\n", ctime(&start_t));
634 gtk_calendar_get_date(GTK_CALENDAR(meet->end_c), &y, &m, &d);
635 end_lt.tm_mday = d; end_lt.tm_mon = m; end_lt.tm_year = y - 1900;
637 end_t = mktime(&end_lt);
639 debug_print("end %s\n", ctime(&end_t));
641 if (end_t > start_t) {
642 debug_print("ok\n");
643 return;
645 start_t = end_t - 3600;
647 tzset();
649 #ifdef G_OS_WIN32
650 if (start_t < 0)
651 start_t = 1;
652 #endif
653 localtime_r(&start_t, &start_lt);
654 debug_print("n %d %d %d, %d:%d\n", start_lt.tm_mday, start_lt.tm_mon, start_lt.tm_year, start_lt.tm_hour, start_lt.tm_min);
656 g_signal_handlers_block_by_func(gtk_bin_get_child(GTK_BIN(meet->start_time)), meeting_start_changed, meet);
657 g_signal_handlers_block_by_func(meet->start_c, meeting_start_changed, meet);
659 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c), start_lt.tm_mday);
661 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
662 start_lt.tm_mon,
663 start_lt.tm_year + 1900);
665 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->start_time), start_lt.tm_hour, start_lt.tm_min);
667 g_signal_handlers_unblock_by_func(gtk_bin_get_child(GTK_BIN(meet->start_time)), meeting_start_changed, meet);
668 g_signal_handlers_unblock_by_func(meet->start_c, meeting_start_changed, meet);
671 static void att_update_icon(VCalMeeting *meet, VCalAttendee *attendee, gint avail, gchar *text)
673 const gchar *icon = "dialog-information";
675 switch (avail) {
676 case 0: icon = "dialog-warning"; break;
677 case 1: icon = "dialog-information"; break;
678 default: icon = "dialog-question"; break;
680 if (!gtk_entry_get_text(GTK_ENTRY(attendee->address))
681 || strlen(gtk_entry_get_text(GTK_ENTRY(attendee->address)))==0) {
682 if (attendee->avail_img) {
683 gtk_widget_hide(attendee->avail_img);
685 CLAWS_SET_TIP(attendee->avail_evtbox, NULL);
686 } else if (attendee->avail_img) {
687 gtk_image_set_from_icon_name
688 (GTK_IMAGE(attendee->avail_img),
689 icon,
690 GTK_ICON_SIZE_SMALL_TOOLBAR);
691 gtk_widget_show(attendee->avail_img);
692 CLAWS_SET_TIP(attendee->avail_evtbox, text);
696 gboolean attendee_available(VCalAttendee *attendee, const gchar *dtstart, const gchar *dtend, const gchar *contents)
698 icalcomponent *toplvl, *vfreebusy;
699 icalproperty *busyprop;
700 struct icaltimetype start = icaltime_from_string(dtstart);
701 struct icaltimetype end = icaltime_from_string(dtend);
702 gboolean result = TRUE;
705 if (contents == NULL)
706 return TRUE;
708 toplvl = icalcomponent_new_from_string((gchar *)contents);
710 if (toplvl == NULL)
711 return TRUE;
713 vfreebusy = icalcomponent_get_first_component(toplvl, ICAL_VFREEBUSY_COMPONENT);
714 while (vfreebusy && icalcomponent_isa(vfreebusy) != ICAL_VFREEBUSY_COMPONENT)
715 vfreebusy = icalcomponent_get_next_component(toplvl, ICAL_VFREEBUSY_COMPONENT);
717 if (vfreebusy) {
718 busyprop = icalcomponent_get_first_property(vfreebusy, ICAL_FREEBUSY_PROPERTY);
719 while (busyprop) {
720 struct icalperiodtype ipt = icalproperty_get_freebusy(busyprop);
722 if ( icaltime_compare(start, ipt.end) >= 0 || icaltime_compare(end, ipt.start) <= 0 ) {
723 result = TRUE;
724 } else {
725 result = FALSE;
726 break;
728 busyprop = icalcomponent_get_next_property(vfreebusy, ICAL_FREEBUSY_PROPERTY);
732 icalcomponent_free(toplvl);
733 return result;
736 static gchar *get_avail_msg(const gchar *unavailable_persons, gboolean multiple,
737 gboolean short_version, gint offset_before, gint offset_after)
739 gchar *msg, *intro = NULL, *outro = NULL, *before = NULL, *after = NULL;
741 if (multiple)
742 intro = g_strdup(_("The following people are busy at the time of your planned meeting:\n- "));
743 else if (!strcmp(unavailable_persons, _("You")))
744 intro = g_strdup(_("You are busy at the time of your planned meeting"));
745 else
746 intro = g_strdup_printf(_("%s is busy at the time of your planned meeting"), unavailable_persons);
747 if (offset_before == 3600)
748 before = g_strdup_printf(_("%d hour sooner"), offset_before/3600);
749 else if (offset_before > 3600 && offset_before%3600 == 0)
750 before = g_strdup_printf(_("%d hours sooner"), offset_before/3600);
751 else if (offset_before > 3600)
752 before = g_strdup_printf(_("%d hours and %d minutes sooner"), offset_before/3600, (offset_before%3600)/60);
753 else if (offset_before == 1800)
754 before = g_strdup_printf(_("%d minutes sooner"), offset_before/60);
755 else
756 before = NULL;
758 if (offset_after == 3600)
759 after = g_strdup_printf(_("%d hour later"), offset_after/3600);
760 else if (offset_after > 3600 && offset_after%3600 == 0)
761 after = g_strdup_printf(_("%d hours later"), offset_after/3600);
762 else if (offset_after > 3600)
763 after = g_strdup_printf(_("%d hours and %d minutes later"), offset_after/3600, (offset_after%3600)/60);
764 else if (offset_after == 1800)
765 after = g_strdup_printf(_("%d minutes later"), offset_after/60);
766 else
767 after = NULL;
769 if (multiple) {
770 if (before && after)
771 outro = g_strdup_printf(_("\n\nEveryone would be available %s or %s."), before, after);
772 else if (before || after)
773 outro = g_strdup_printf(_("\n\nEveryone would be available %s."), before?before:after);
774 else
775 outro = g_strdup_printf(_("\n\nIt isn't possible to have this meeting with everyone "
776 "in the previous or next 6 hours."));
777 } else {
778 if (short_version) {
779 if (before && after)
780 outro = g_markup_printf_escaped(_("would be available %s or %s"), before, after);
781 else if (before || after)
782 outro = g_markup_printf_escaped(_("would be available %s"), before?before:after);
783 else
784 outro = g_strdup_printf(_("not available"));
785 } else {
786 if (before && after)
787 outro = g_markup_printf_escaped(_(", but would be available %s or %s."), before, after);
788 else if (before || after)
789 outro = g_markup_printf_escaped(_(", but would be available %s."), before?before:after);
790 else
791 outro = g_strdup_printf(_(", and isn't available "
792 "in the previous or next 6 hours."));
795 if (multiple && short_version)
796 msg = g_strconcat(outro+2, NULL);
797 else if (multiple)
798 msg = g_strconcat(intro, unavailable_persons, outro, NULL);
799 else if (short_version)
800 msg = g_strdup(outro);
801 else
802 msg = g_strconcat(intro, outro, NULL);
803 g_free(intro);
804 g_free(outro);
805 g_free(before);
806 g_free(after);
807 return msg;
810 static gboolean find_availability(const gchar *dtstart, const gchar *dtend, GSList *attendees, gboolean for_send, VCalMeeting *meet)
812 GSList *cur;
813 gint offset = -1800, offset_before = 0, offset_after = 0;
814 gboolean found = FALSE;
815 gchar *unavailable_persons = NULL;
816 gchar *msg = NULL;
817 struct icaltimetype start = icaltime_from_string(dtstart);
818 struct icaltimetype end = icaltime_from_string(dtend);
819 AlertValue val = G_ALERTALTERNATE;
820 gint total = 0;
821 GHashTable *avail_table_avail = g_hash_table_new(NULL, g_direct_equal);
822 GHashTable *avail_table_before = g_hash_table_new(NULL, g_direct_equal);
823 GHashTable *avail_table_after = g_hash_table_new(NULL, g_direct_equal);
825 for (cur = attendees; cur; cur = cur->next) {
826 VCalAttendee *attendee = (VCalAttendee *)cur->data;
827 if (!attendee_available(attendee, icaltime_as_ical_string(start), icaltime_as_ical_string(end),
828 attendee->cached_contents)) {
829 gchar *mail = NULL;
831 if (attendee->org)
832 mail = g_strdup(_("You"));
833 else
834 mail = gtk_editable_get_chars(GTK_EDITABLE(attendee->address), 0, -1);
836 if (unavailable_persons == NULL) {
837 unavailable_persons = g_markup_printf_escaped("%s", mail);
838 } else {
839 gchar *tmp = g_markup_printf_escaped("%s,\n- %s", unavailable_persons, mail);
840 g_free(unavailable_persons);
841 unavailable_persons = tmp;
843 total++;
844 g_free(mail);
845 att_update_icon(meet, attendee, 0, _("not available"));
846 } else {
847 if (attendee->cached_contents != NULL)
848 att_update_icon(meet, attendee, 1, _("available"));
849 else
850 att_update_icon(meet, attendee, 2, _("Free/busy retrieval failed"));
852 g_hash_table_insert(avail_table_avail, attendee, GINT_TO_POINTER(1));
855 offset = -1800;
856 found = FALSE;
857 while (!found && offset >= -3600*6) {
858 gboolean ok = TRUE;
859 struct icaltimetype new_start = icaltime_from_timet_with_zone(icaltime_as_timet(start)+offset, FALSE, NULL);
860 struct icaltimetype new_end = icaltime_from_timet_with_zone(icaltime_as_timet(end)+offset, FALSE, NULL);
861 for (cur = attendees; cur; cur = cur->next) {
862 VCalAttendee *attendee = (VCalAttendee *)cur->data;
863 debug_print("trying %s - %s (offset %d)\n",
864 icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end), offset);
865 if (!attendee_available(attendee, icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end),
866 attendee->cached_contents)) {
867 ok = FALSE;
868 break;
869 } else {
870 if (!g_hash_table_lookup(avail_table_before, attendee)
871 && !g_hash_table_lookup(avail_table_avail, attendee))
872 g_hash_table_insert(avail_table_before, attendee, GINT_TO_POINTER(-offset));
875 if (ok) {
876 found = TRUE;
877 offset_before = -offset;
879 offset -= 1800;
881 found = FALSE;
882 offset = 1800;
883 while (!found && offset <= 3600*6) {
884 gboolean ok = TRUE;
885 struct icaltimetype new_start = icaltime_from_timet_with_zone(icaltime_as_timet(start)+offset, FALSE, NULL);
886 struct icaltimetype new_end = icaltime_from_timet_with_zone(icaltime_as_timet(end)+offset, FALSE, NULL);
887 for (cur = attendees; cur; cur = cur->next) {
888 VCalAttendee *attendee = (VCalAttendee *)cur->data;
889 debug_print("trying %s - %s (offset %d)\n",
890 icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end), offset);
891 if (!attendee_available(attendee, icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end),
892 attendee->cached_contents)) {
893 ok = FALSE;
894 break;
895 } else {
896 if (!g_hash_table_lookup(avail_table_after, attendee)
897 && !g_hash_table_lookup(avail_table_avail, attendee))
898 g_hash_table_insert(avail_table_after, attendee, GINT_TO_POINTER(offset));
901 if (ok) {
902 found = TRUE;
903 offset_after = offset;
906 offset += 1800;
909 for (cur = attendees; cur; cur = cur->next) {
910 VCalAttendee *attendee = (VCalAttendee *)cur->data;
911 gint ok = GPOINTER_TO_INT(g_hash_table_lookup(avail_table_avail, attendee));
912 gint o_before = GPOINTER_TO_INT(g_hash_table_lookup(avail_table_before, attendee));
913 gint o_after = GPOINTER_TO_INT(g_hash_table_lookup(avail_table_after, attendee));
914 if (!o_before && !o_after && !ok) {
915 att_update_icon(meet, attendee, 0, _("not available"));
916 } else if ((o_before != 0 || o_after != 0) && !ok) {
917 if (attendee->org)
918 msg = get_avail_msg(_("You"), FALSE, TRUE, o_before, o_after);
919 else
920 msg = get_avail_msg(gtk_entry_get_text(GTK_ENTRY(attendee->address)), FALSE, TRUE, o_before, o_after);
921 att_update_icon(meet, attendee, 0, msg);
922 g_free(msg);
926 g_hash_table_destroy(avail_table_before);
927 g_hash_table_destroy(avail_table_after);
929 if (for_send) {
930 msg = get_avail_msg(unavailable_persons, (total > 1), FALSE, offset_before, offset_after);
932 val = alertpanel_full(_("Not everyone is available"), msg,
933 NULL, _("_Cancel"), NULL, _("Send anyway"),
934 NULL, NULL, ALERTFOCUS_FIRST,
935 FALSE, NULL, ALERT_QUESTION);
936 g_free(msg);
938 msg = get_avail_msg(unavailable_persons, TRUE, TRUE, offset_before, offset_after);
939 g_free(unavailable_persons);
940 gtk_image_set_from_icon_name
941 (GTK_IMAGE(meet->total_avail_img),
942 "dialog-warning",
943 GTK_ICON_SIZE_SMALL_TOOLBAR);
944 gtk_widget_show(meet->total_avail_img);
945 gtk_label_set_text(GTK_LABEL(meet->total_avail_msg), _("Not everyone is available. "
946 "See tooltips for more info..."));
947 CLAWS_SET_TIP(meet->total_avail_evtbox, msg);
948 g_free(msg);
949 return (val == G_ALERTALTERNATE);
952 static gboolean check_attendees_availability(VCalMeeting *meet, gboolean tell_if_ok, gboolean for_send)
954 GSList *cur;
955 gchar *tmp = NULL;
956 gchar *real_url = NULL;
957 gint num_format = 0;
958 gchar *change_user = NULL, *change_dom = NULL;
959 gchar *dtstart = NULL;
960 gchar *dtend = NULL;
961 gboolean find_avail = FALSE;
962 gboolean res = TRUE, uncertain = FALSE;
963 gchar *organizer = NULL;
964 VCalAttendee *dummy_org = NULL;
965 gchar *internal_ifb = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
966 "vcalendar", G_DIR_SEPARATOR_S,
967 "internal.ifb", NULL);
968 gboolean local_only = FALSE;
969 GSList *attlist;
970 GdkWindow *gdkwin;
972 if (vcalprefs.freebusy_get_url == NULL
973 || *vcalprefs.freebusy_get_url == '\0') {
974 local_only = TRUE;
975 } else {
976 real_url = g_strdup(vcalprefs.freebusy_get_url);
977 tmp = real_url;
979 while (strchr(tmp, '%')) {
980 tmp = strchr(tmp, '%')+1;
981 num_format++;
983 if (num_format > 2) {
984 g_warning("wrong format in %s!", real_url);
985 g_free(real_url);
986 g_free(internal_ifb);
987 return FALSE;
990 tmp = NULL;
991 if (strstr(real_url, "%u") != NULL) {
992 change_user = strstr(real_url, "%u");
993 *(strstr(real_url, "%u")+1) = 's';
995 if (strstr(real_url, "%d") != NULL) {
996 change_dom = strstr(real_url, "%d");
997 *(strstr(real_url, "%d")+1) = 's';
999 debug_print("url format %s\n", real_url);
1001 dtstart = get_date(meet, TRUE);
1002 dtend = get_date(meet, FALSE);
1004 /* hack to check our own avail. */
1005 organizer = get_organizer(meet);
1006 dummy_org = g_new0(VCalAttendee, 1);
1007 dummy_org->address = gtk_entry_new();
1008 dummy_org->avail_img = meet->avail_img;
1009 dummy_org->avail_evtbox = meet->avail_evtbox;
1010 dummy_org->org = TRUE;
1011 gtk_entry_set_text(GTK_ENTRY(dummy_org->address), organizer);
1012 g_free(organizer);
1013 dummy_org->cached_contents = file_read_to_str(internal_ifb);
1014 g_free(internal_ifb);
1016 if (!local_only) {
1017 meet->attendees = g_slist_prepend(meet->attendees, dummy_org);
1018 attlist = meet->attendees;
1019 } else {
1020 attlist = g_slist_prepend(NULL, dummy_org);
1023 gtk_widget_set_sensitive(meet->save_btn, FALSE);
1024 gtk_widget_set_sensitive(meet->avail_btn, FALSE);
1026 gdkwin = gtk_widget_get_window(meet->window);
1027 if (gdkwin != NULL) {
1028 set_watch_cursor(gdkwin);
1029 gdk_window_set_cursor(gdkwin, watch_cursor);
1032 for (cur = attlist; cur && cur->data; cur = cur->next) {
1033 VCalAttendee *attendee = (VCalAttendee *)cur->data;
1034 gchar *email = gtk_editable_get_chars(GTK_EDITABLE(attendee->address), 0, -1);
1035 gchar *remail, *user, *domain;
1036 gchar *contents = NULL;
1038 if (*email == '\0') {
1039 g_free(email);
1040 att_update_icon(meet, attendee, 0, NULL);
1041 continue;
1044 if (!local_only) {
1045 remail = g_strdup(email);
1047 extract_address(remail);
1048 if (strrchr(remail, ' '))
1049 user = g_strdup(strrchr(remail, ' ')+1);
1050 else
1051 user = g_strdup(remail);
1052 if (strchr(user, '@')) {
1053 domain = g_strdup(strchr(user, '@')+1);
1054 *(strchr(user, '@')) = '\0';
1055 } else {
1056 domain = g_strdup("");
1058 g_free(remail);
1059 if (change_user && change_dom) {
1060 if (change_user < change_dom)
1061 tmp = g_strdup_printf(real_url, user, domain);
1062 else
1063 tmp = g_strdup_printf(real_url, domain, user);
1064 } else if (change_user) {
1065 tmp = g_strdup_printf(real_url, user);
1066 } else if (change_dom) {
1067 tmp = g_strdup_printf(real_url, domain);
1068 } else {
1069 tmp = g_strdup(real_url);
1071 g_free(user);
1072 g_free(domain);
1073 debug_print("url to get %s\n", tmp);
1076 if (attendee->cached_contents != NULL) {
1077 contents = attendee->cached_contents;
1078 attendee->cached_contents = NULL;
1079 } else if (!local_only) {
1080 if (strncmp(tmp, "http://", 7)
1081 && strncmp(tmp, "https://", 8)
1082 && strncmp(tmp, "webcal://", 9)
1083 && strncmp(tmp, "webcals://", 10)
1084 && strncmp(tmp, "ftp://", 6)
1085 && strncmp(tmp, "ftps://", 7)
1086 && strncmp(tmp, "sftp://", 7))
1087 contents = file_read_to_str(tmp);
1088 else {
1089 gchar *label = g_strdup_printf(_("Fetching planning for %s..."), email);
1090 if (!strncmp(tmp, "webcal", 6)) {
1091 gchar *tmp2 = g_strdup_printf("http%s", tmp+6);
1092 g_free(tmp);
1093 tmp = tmp2;
1095 contents = vcal_curl_read(tmp, label, FALSE, NULL);
1096 g_free(label);
1098 } else {
1099 contents = NULL;
1102 g_free(email);
1103 g_free(tmp);
1105 if (contents == NULL) {
1106 uncertain = TRUE;
1107 att_update_icon(meet, attendee, 2, _("Free/busy retrieval failed"));
1108 continue;
1110 else {
1111 if (!attendee_available(attendee, dtstart, dtend, contents)) {
1112 find_avail = TRUE;
1113 debug_print("not available!\n");
1114 } else {
1115 debug_print("available!\n");
1116 att_update_icon(meet, attendee, 1, _("Available"));
1118 attendee->cached_contents = contents;
1123 if (find_avail) {
1124 res = find_availability((dtstart), (dtend), attlist, for_send, meet);
1125 } else {
1126 res = TRUE;
1127 if (tell_if_ok) {
1128 if (for_send)
1129 alertpanel_notice(_("Everyone is available."));
1130 else if (!uncertain) {
1131 gtk_image_set_from_icon_name
1132 (GTK_IMAGE(meet->total_avail_img),
1133 "dialog-information",
1134 GTK_ICON_SIZE_SMALL_TOOLBAR);
1135 gtk_widget_show(meet->total_avail_img);
1136 gtk_label_set_text(GTK_LABEL(meet->total_avail_msg), _("Everyone is available."));
1137 CLAWS_SET_TIP(meet->total_avail_evtbox, NULL);
1138 } else {
1139 gtk_image_set_from_icon_name
1140 (GTK_IMAGE(meet->total_avail_img),
1141 "dialog-question",
1142 GTK_ICON_SIZE_SMALL_TOOLBAR);
1143 gtk_widget_show(meet->total_avail_img);
1144 gtk_label_set_text(GTK_LABEL(meet->total_avail_msg), _("Everyone is available."));
1145 CLAWS_SET_TIP(meet->total_avail_evtbox, _("Everyone seems available, but some free/busy information failed to be retrieved."));
1150 for (cur = attlist; cur && cur->data; cur = cur->next) {
1151 VCalAttendee *attendee = (VCalAttendee *)cur->data;
1152 g_free(attendee->cached_contents);
1153 attendee->cached_contents = NULL;
1155 gtk_widget_set_sensitive(meet->save_btn, TRUE);
1156 gtk_widget_set_sensitive(meet->avail_btn, avail_btn_can_be_sensitive());
1158 if (gdkwin != NULL)
1159 gdk_window_set_cursor(gdkwin, NULL);
1161 if (!local_only)
1162 meet->attendees = g_slist_remove(meet->attendees, dummy_org);
1163 else
1164 g_slist_free(attlist);
1165 gtk_widget_destroy(dummy_org->address);
1166 g_free(dummy_org);
1168 if (!local_only)
1169 g_free(real_url);
1171 g_free(dtstart);
1172 g_free(dtend);
1173 return res;
1176 static gboolean check_avail_cb(GtkButton *widget, gpointer data)
1178 VCalMeeting *meet = (VCalMeeting *)data;
1179 check_attendees_availability(meet, TRUE, FALSE);
1180 return TRUE;
1183 static gboolean send_meeting_cb(GtkButton *widget, gpointer data)
1185 VCalMeeting *meet = (VCalMeeting *)data;
1186 gchar *uid = NULL;
1187 gchar *organizer = NULL;
1188 gchar *organizer_name = NULL;
1189 gchar *dtstart = NULL;
1190 gchar *dtend = NULL;
1191 gchar *tzid = NULL;
1192 gchar *location = NULL;
1193 gchar *summary = NULL;
1194 gchar *description = NULL;
1195 VCalEvent *event = NULL;
1196 GSList *cur;
1197 PrefsAccount *account = NULL;
1198 gboolean res = FALSE;
1199 gboolean found_att = FALSE;
1200 Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
1201 gboolean redisp = FALSE;
1202 GdkWindow *gdkwin;
1204 if (meet->uid == NULL && meet->visible &&
1205 !check_attendees_availability(meet, FALSE, TRUE)) {
1206 return FALSE;
1209 if (folder) {
1210 MainWindow *mainwin = mainwindow_get_mainwindow();
1211 if (mainwin->summaryview->folder_item == folder->inbox) {
1212 redisp = TRUE;
1213 summary_show(mainwin->summaryview, NULL, FALSE);
1216 gtk_widget_set_sensitive(meet->save_btn, FALSE);
1217 gtk_widget_set_sensitive(meet->avail_btn, FALSE);
1219 gdkwin = gtk_widget_get_window(meet->window);
1220 if (gdkwin != NULL) {
1221 set_watch_cursor(gdkwin);
1222 gdk_window_set_cursor(gdkwin, watch_cursor);
1225 organizer = get_organizer(meet);
1226 account = account_find_from_address(organizer, FALSE);
1228 if(account == NULL) {
1229 debug_print("can't get account from address %s\n", organizer);
1230 g_free(organizer);
1231 return FALSE;
1234 organizer_name = get_organizer_name(meet);
1236 if (meet->uid) {
1237 uid = g_strdup(meet->uid);
1238 } else {
1239 uid = prefs_account_generate_msgid(account);
1242 dtstart = get_date(meet, TRUE);
1243 dtend = get_date(meet, FALSE);
1244 location = get_location(meet);
1245 summary = get_summary(meet);
1246 description = get_description(meet);
1248 if (meet->created == NULL) {
1249 meet->created = g_strdup(icaltime_as_ical_string(icaltime_from_timet_with_zone(time(NULL), FALSE, NULL)));
1251 if (meet->last_modified == NULL) {
1252 meet->last_modified = g_strdup(icaltime_as_ical_string(icaltime_from_timet_with_zone(time(NULL), FALSE, NULL)));
1254 event = vcal_manager_new_event(uid, organizer, organizer_name, location, summary, description,
1255 dtstart, dtend, NULL, tzid, NULL, meet->method,
1256 meet->sequence,
1257 meet->created, meet->last_modified, ICAL_VEVENT_COMPONENT);
1259 vcal_manager_update_answer(event, organizer, organizer_name,
1260 ICAL_PARTSTAT_ACCEPTED,
1261 ICAL_CUTYPE_INDIVIDUAL);
1263 for (cur = meet->attendees; cur && cur->data; cur = cur->next) {
1264 VCalAttendee *attendee = (VCalAttendee *)cur->data;
1265 gchar *email = gtk_editable_get_chars(GTK_EDITABLE(attendee->address), 0, -1);
1266 gint index = 0;
1267 gchar *orig_email = email;
1268 gchar *name = NULL;
1269 enum icalparameter_cutype cutype = ICAL_CUTYPE_INDIVIDUAL;
1270 enum icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION;
1272 index = gtk_combo_box_get_active(GTK_COMBO_BOX(attendee->cutype));
1274 cutype = ICAL_CUTYPE_INDIVIDUAL + index;
1275 if (attendee->status) {
1276 if(!strcmp(attendee->status, "accepted"))
1277 status = ICAL_PARTSTAT_ACCEPTED;
1278 if(!strcmp(attendee->status, "tentatively accepted"))
1279 status = ICAL_PARTSTAT_TENTATIVE;
1280 if(!strcmp(attendee->status, "declined"))
1281 status = ICAL_PARTSTAT_DECLINED;
1282 g_free(attendee->status);
1284 if (strlen(email)) {
1285 if (strstr(email, " <")) {
1286 name = email;
1287 email = strstr(email," <") + 2;
1288 *(strstr(name," <")) = '\0';
1289 if (strstr(email, ">"))
1290 *(strstr(email, ">")) = '\0';
1293 vcal_manager_update_answer(event, email, name,
1294 status, cutype);
1296 found_att = strcmp(email, organizer);
1298 g_free(orig_email);
1301 if (found_att)
1302 res = vcal_manager_request(account, event);
1303 else
1304 res = TRUE;
1305 g_free(uid);
1306 g_free(organizer);
1307 g_free(organizer_name);
1308 g_free(dtstart);
1309 g_free(dtend);
1310 g_free(description);
1311 g_free(location);
1312 g_free(summary);
1313 vcal_manager_free_event(event);
1315 gtk_widget_set_sensitive(meet->save_btn, TRUE);
1316 gtk_widget_set_sensitive(meet->avail_btn, avail_btn_can_be_sensitive());
1317 if (gdkwin != NULL)
1318 gdk_window_set_cursor(gdkwin, NULL);
1320 if (res) {
1321 vcal_destroy(meet);
1322 } else {
1323 alertpanel_error(_("Could not send the meeting invitation.\n"
1324 "Check the recipients."));
1327 if (folder)
1328 folder_item_scan(folder->inbox);
1330 if (folder && redisp) {
1331 MainWindow *mainwin = mainwindow_get_mainwindow();
1332 summary_show(mainwin->summaryview, folder->inbox, FALSE);
1335 return res;
1338 static VCalMeeting *vcal_meeting_create_real(VCalEvent *event, gboolean visible)
1340 VCalMeeting *meet = g_new0(VCalMeeting, 1);
1341 GtkTextBuffer *buffer = NULL;
1342 GtkWidget *date_hbox, *date_vbox, *save_hbox, *label, *hbox;
1343 gchar *s = NULL;
1344 int i = 0, num = 0;
1345 GtkWidget *scrolledwin;
1346 GList *accounts;
1347 #ifdef GENERIC_UMPC
1348 GtkWidget *notebook;
1349 GtkWidget *maemo_vbox0;
1350 #endif
1352 meet->visible = visible;
1354 meet->window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "vcal_meeting_gtk");
1355 #ifndef GENERIC_UMPC
1356 meet->table = gtk_grid_new();
1357 #else
1358 meet->table1 = gtk_grid_new();
1359 meet->table2 = gtk_grid_new();
1360 #endif
1361 meet->who = gtk_combo_box_text_new();
1363 meet->start_c = gtk_calendar_new();
1364 meet->end_c = gtk_calendar_new();
1366 meet->avail_evtbox = gtk_event_box_new();
1367 meet->avail_img = gtk_image_new_from_icon_name
1368 ("dialog-warning", GTK_ICON_SIZE_SMALL_TOOLBAR);
1370 meet->start_time = gtkut_time_select_combo_new();
1372 meet->end_time = gtkut_time_select_combo_new();
1374 meet->location = gtk_entry_new();
1375 meet->summary = gtk_entry_new();
1376 meet->description = gtk_text_view_new();
1377 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(meet->description));
1378 gtk_text_view_set_editable(GTK_TEXT_VIEW(meet->description), TRUE);
1379 gtk_text_buffer_add_selection_clipboard(buffer, gtk_clipboard_get(GDK_SELECTION_PRIMARY));
1381 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
1382 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
1383 GTK_POLICY_AUTOMATIC,
1384 GTK_POLICY_AUTOMATIC);
1385 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
1386 GTK_SHADOW_IN);
1387 gtk_container_add(GTK_CONTAINER(scrolledwin), meet->description);
1389 if (event) {
1390 meet->uid = g_strdup(event->uid);
1391 meet->sequence = event->sequence + 1;
1392 meet->created = event->created ? g_strdup(event->created) : NULL;
1393 meet->last_modified = NULL; /* Make sure to update modification time */
1395 meet->method = (event->method == ICAL_METHOD_CANCEL ?
1396 ICAL_METHOD_CANCEL:ICAL_METHOD_REQUEST);
1398 gtk_entry_set_text(GTK_ENTRY(meet->location), event->location);
1399 gtk_entry_set_text(GTK_ENTRY(meet->summary), event->summary);
1400 gtk_text_buffer_set_text(buffer, event->description, -1);
1401 } else {
1402 meet->method = ICAL_METHOD_REQUEST;
1404 meet->save_btn = gtk_button_new_with_label(_("Save & Send"));
1405 meet->avail_btn = gtk_button_new_with_label(_("Check availability"));
1407 meet->total_avail_evtbox = gtk_event_box_new();
1408 meet->total_avail_img = gtk_image_new_from_icon_name
1409 ("dialog-warning", GTK_ICON_SIZE_SMALL_TOOLBAR);
1410 meet->total_avail_msg = gtk_label_new("");
1412 gtk_widget_set_size_request(meet->total_avail_evtbox, 18, 16);
1413 gtk_event_box_set_visible_window(GTK_EVENT_BOX(meet->total_avail_evtbox), FALSE);
1414 gtk_container_add (GTK_CONTAINER(meet->total_avail_evtbox), meet->total_avail_img);
1416 g_signal_connect(G_OBJECT(meet->save_btn), "clicked",
1417 G_CALLBACK(send_meeting_cb), meet);
1419 g_signal_connect(G_OBJECT(meet->avail_btn), "clicked",
1420 G_CALLBACK(check_avail_cb), meet);
1422 g_signal_connect(G_OBJECT(meet->window), "destroy",
1423 G_CALLBACK(destroy_meeting_cb), meet);
1424 g_signal_connect(G_OBJECT(meet->window), "key_press_event",
1425 G_CALLBACK(meeting_key_pressed), meet);
1428 gtk_widget_set_size_request(meet->description, -1, 100);
1429 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(meet->description), GTK_WRAP_WORD);
1431 if (!event || (event && !event->dtstart && !event->dtend)) {
1432 time_t t = time (NULL)+ 3600;
1433 struct tm buft1, buft2;
1434 struct tm *lt = localtime_r (&t, &buft1);
1435 mktime(lt);
1436 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c),
1437 lt->tm_mday);
1438 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
1439 lt->tm_mon, lt->tm_year + 1900);
1441 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->start_time), lt->tm_hour, 0);
1443 t += 3600;
1444 lt = localtime_r(&t, &buft2);
1446 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1447 lt->tm_mday);
1448 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1449 lt->tm_mon, lt->tm_year + 1900);
1451 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->end_time), lt->tm_hour, 0);
1452 } else {
1453 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c),
1454 get_dtdate(event->dtstart, DAY));
1455 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1456 get_dtdate(event->dtend, DAY));
1458 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
1459 get_dtdate(event->dtstart, MONTH)-1,
1460 get_dtdate(event->dtstart, YEAR));
1461 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1462 get_dtdate(event->dtend, MONTH)-1,
1463 get_dtdate(event->dtend, YEAR));
1465 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->start_time),
1466 get_dtdate(event->dtstart, HOUR),
1467 get_dtdate(event->dtstart, MINUTE));
1469 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->end_time),
1470 get_dtdate(event->dtend, HOUR),
1471 get_dtdate(event->dtend, MINUTE));
1474 g_signal_connect(G_OBJECT(meet->start_c), "day-selected",
1475 G_CALLBACK(meeting_start_changed), meet);
1476 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(meet->start_time))),
1477 "changed",
1478 G_CALLBACK(meeting_start_changed),
1479 meet);
1481 g_signal_connect(G_OBJECT(meet->end_c), "day-selected",
1482 G_CALLBACK(meeting_end_changed), meet);
1483 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(meet->end_time))),
1484 "changed",
1485 G_CALLBACK(meeting_end_changed),
1486 meet);
1488 #ifndef GENERIC_UMPC
1489 gtk_widget_set_size_request(meet->start_time, 80, -1);
1490 gtk_widget_set_size_request(meet->end_time, 80, -1);
1491 #else
1492 gtk_widget_set_size_request(meet->start_time, 120, -1);
1493 gtk_widget_set_size_request(meet->end_time, 120, -1);
1494 #endif
1496 date_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1497 date_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
1498 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1499 label = gtk_label_new(g_strconcat("<b>",_("Starts at:"),"</b> ",NULL));
1500 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
1501 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1503 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1504 gtk_box_pack_start(GTK_BOX(hbox), meet->start_time, FALSE, FALSE, 0);
1505 label = gtk_label_new(g_strconcat("<b> ",_("on:"),"</b>",NULL));
1506 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
1507 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1508 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1509 gtk_box_pack_start(GTK_BOX(date_vbox), hbox, FALSE, FALSE, 0);
1510 gtk_box_pack_start(GTK_BOX(date_vbox), meet->start_c, FALSE, FALSE, 0);
1511 gtk_box_pack_start(GTK_BOX(date_hbox), date_vbox, FALSE, FALSE, 0);
1513 #ifndef GENERIC_UMPC
1514 label = gtk_label_new(" ");
1515 #else
1516 label = gtk_label_new("");
1517 #endif
1518 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
1519 gtk_box_pack_start(GTK_BOX(date_hbox), label, TRUE, TRUE, 0);
1521 date_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
1522 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1523 label = gtk_label_new(g_strconcat("<b>",_("Ends at:"),"</b> ", NULL));
1524 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
1525 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1527 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1528 gtk_box_pack_start(GTK_BOX(hbox), meet->end_time, FALSE, FALSE, 0);
1529 label = gtk_label_new(g_strconcat("<b> ",_("on:"),"</b>",NULL));
1530 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
1531 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1532 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1533 gtk_box_pack_start(GTK_BOX(date_vbox), hbox, FALSE, FALSE, 0);
1534 gtk_box_pack_start(GTK_BOX(date_vbox), meet->end_c, FALSE, FALSE, 0);
1535 gtk_box_pack_start(GTK_BOX(date_hbox), date_vbox, FALSE, FALSE, 0);
1537 meet->attendees_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
1538 gtk_widget_show_all(meet->attendees_vbox);
1539 if (!event) {
1540 attendee_add(meet, NULL, NULL, NULL, NULL, TRUE);
1541 } else {
1542 gboolean firstadd = TRUE;
1543 GSList *list = vcal_manager_get_answers_emails(event);
1544 while (list && list->data) {
1545 gchar *address = (gchar *)list->data;
1546 gchar *name = vcal_manager_get_attendee_name(event, address);
1547 gchar *answer = vcal_manager_get_reply_text_for_attendee(event, address);
1548 gchar *type = vcal_manager_get_cutype_text_for_attendee(event, address);
1549 if (strcmp(event->organizer, address)) {
1550 attendee_add(meet, address, name, answer, type, firstadd);
1551 firstadd = FALSE;
1553 g_free(name);
1554 g_free(answer);
1555 g_free(type);
1556 list = list->next;
1559 if (firstadd == TRUE)
1560 attendee_add(meet, NULL, NULL, NULL, NULL, TRUE);
1563 if (!event) {
1564 gtk_window_set_title(GTK_WINDOW(meet->window), _("New meeting"));
1565 } else {
1566 gchar *title = g_strdup_printf(_("%s - Edit meeting"),
1567 event->summary);
1568 gtk_window_set_title(GTK_WINDOW(meet->window), title);
1569 g_free(title);
1571 address_completion_start(meet->window);
1573 accounts = account_get_list();
1574 g_return_val_if_fail(accounts != NULL, NULL);
1576 for (i = 0; accounts != NULL; accounts = accounts->next) {
1577 PrefsAccount *ac = (PrefsAccount *)accounts->data;
1579 if (ac->protocol == A_NNTP) {
1580 continue;
1582 if (!event && ac == account_get_cur_account()) {
1583 num = i;
1585 else if (event && !strcmp(ac->address, event->organizer))
1586 num = i;
1588 meet->avail_accounts = g_slist_append(meet->avail_accounts, ac);
1590 if (ac->name)
1591 s = g_strdup_printf("%s: %s <%s>",
1592 ac->account_name,
1593 ac->name, ac->address);
1594 else
1595 s = g_strdup_printf("%s: %s",
1596 ac->account_name, ac->address);
1598 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(meet->who), s);
1599 g_free(s);
1600 i++;
1602 gtk_combo_box_set_active(GTK_COMBO_BOX(meet->who), num);
1604 save_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1605 gtk_box_pack_start(GTK_BOX(save_hbox), meet->save_btn, FALSE, FALSE, 0);
1606 gtk_box_pack_start(GTK_BOX(save_hbox), meet->avail_btn, FALSE, FALSE, 0);
1607 gtk_box_pack_start(GTK_BOX(save_hbox), meet->total_avail_evtbox, FALSE, FALSE, 0);
1608 gtk_box_pack_start(GTK_BOX(save_hbox), meet->total_avail_msg, FALSE, FALSE, 0);
1610 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1611 gtk_box_pack_start(GTK_BOX(hbox), meet->avail_evtbox, FALSE, FALSE, 0);
1612 gtk_box_pack_start(GTK_BOX(hbox), meet->who, TRUE, TRUE, 0);
1614 gtk_widget_set_size_request(meet->avail_evtbox, 18, 16);
1615 gtk_event_box_set_visible_window(GTK_EVENT_BOX(meet->avail_evtbox), FALSE);
1616 gtk_container_add (GTK_CONTAINER(meet->avail_evtbox), meet->avail_img);
1618 #ifndef GENERIC_UMPC
1619 TABLE_ADD_LINE(_("Organizer:"), hbox, FALSE);
1620 TABLE_ADD_LINE(_("Summary:"), meet->summary, TRUE);
1621 TABLE_ADD_LINE(_("Time:"), date_hbox, TRUE);
1622 TABLE_ADD_LINE(_("Location:"), meet->location, TRUE);
1623 TABLE_ADD_LINE(_("Description:"), scrolledwin, TRUE);
1624 TABLE_ADD_LINE(_("Attendees:"), meet->attendees_vbox, FALSE);
1625 TABLE_ADD_LINE("", save_hbox, TRUE);
1627 gtk_widget_set_size_request(meet->window, -1, -1);
1628 gtk_container_add(GTK_CONTAINER(meet->window), meet->table);
1629 #else
1630 TABLE_ADD_LINE(_("Organizer:"), hbox, FALSE, TRUE);
1631 TABLE_ADD_LINE(_("Summary:"), meet->summary, TRUE, TRUE);
1632 TABLE_ADD_LINE(_("Location:"), meet->location, FALSE, TRUE);
1633 TABLE_ADD_LINE(_("Description:"), scrolledwin, TRUE, TRUE);
1634 TABLE_ADD_LINE(_("Attendees:"), meet->attendees_vbox, FALSE, TRUE);
1635 TABLE_ADD_LINE("", date_hbox, TRUE, FALSE);
1637 notebook = gtk_notebook_new ();
1638 gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE);
1639 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
1640 meet->table1,
1641 gtk_label_new_with_mnemonic(_("Event:")));
1643 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
1644 meet->table2,
1645 gtk_label_new_with_mnemonic(_("Time:")));
1646 gtk_widget_show (notebook);
1648 maemo_vbox0 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3);
1649 gtk_box_pack_start(GTK_BOX(maemo_vbox0), notebook, TRUE, TRUE, 0);
1650 gtk_box_pack_start(GTK_BOX(maemo_vbox0), save_hbox, FALSE, FALSE, 0);
1652 gtk_widget_set_size_request(meet->window, -1, -1);
1653 gtk_container_add (GTK_CONTAINER (meet->window), maemo_vbox0);
1655 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(meet->window));
1656 #endif
1657 if (visible) {
1658 GSList *cur;
1659 gtk_widget_show_all(meet->window);
1660 for (cur = meet->attendees; cur; cur = cur->next) {
1661 gtk_widget_hide(((VCalAttendee *)cur->data)->avail_img);
1663 gtk_widget_hide(meet->avail_img);
1664 gtk_widget_hide(meet->total_avail_img);
1665 gtk_widget_set_sensitive(meet->avail_btn, avail_btn_can_be_sensitive());
1668 return meet;
1671 VCalMeeting *vcal_meeting_create(VCalEvent *event)
1673 return vcal_meeting_create_real(event, TRUE);
1676 VCalMeeting *vcal_meeting_create_with_start(VCalEvent *event, struct tm *sdate)
1678 VCalMeeting *meet = vcal_meeting_create(event);
1680 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c),
1681 sdate->tm_mday);
1682 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1683 sdate->tm_mday);
1685 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
1686 sdate->tm_mon, sdate->tm_year+1900);
1687 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1688 sdate->tm_mon, sdate->tm_year+1900);
1690 if (sdate->tm_hour != 0) {
1691 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->start_time), sdate->tm_hour, 0);
1693 if (sdate->tm_hour < 23) {
1694 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->end_time), sdate->tm_hour+1, 0);
1695 } else {
1696 struct tm tm_tomorrow;
1698 tm_tomorrow.tm_mday = sdate->tm_mday;
1699 tm_tomorrow.tm_mon = sdate->tm_mon;
1700 tm_tomorrow.tm_wday = sdate->tm_wday;
1701 tm_tomorrow.tm_year = sdate->tm_year+1900;
1702 tm_tomorrow.tm_hour = sdate->tm_hour;
1703 orage_move_day(&tm_tomorrow, +1);
1704 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1705 tm_tomorrow.tm_mday);
1706 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1707 tm_tomorrow.tm_mon, tm_tomorrow.tm_year);
1709 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->end_time), 0, 0);
1712 return meet;
1715 VCalMeeting *vcal_meeting_create_hidden(VCalEvent *event)
1717 return vcal_meeting_create_real(event, FALSE);
1720 gboolean vcal_meeting_send(VCalMeeting *meet)
1722 return send_meeting_cb(NULL, meet);
1725 gboolean vcal_meeting_alert_check(gpointer data)
1727 GSList *events = NULL, *cur = NULL;
1729 if (!vcalprefs.alert_enable)
1730 return TRUE;
1732 events = vcal_folder_get_waiting_events();
1734 for (cur = events; cur; cur = cur->next) {
1735 VCalEvent *event = (VCalEvent *)cur->data;
1736 time_t start, end, current;
1737 gboolean warn = FALSE;
1739 tzset();
1741 start = icaltime_as_timet(icaltime_from_string(event->dtstart));
1742 end = icaltime_as_timet(icaltime_from_string(event->dtend));
1743 current = time(NULL);
1745 if (start - current <= (vcalprefs.alert_delay*60)
1746 && start - current + 60 > (vcalprefs.alert_delay*60)) {
1747 warn = TRUE;
1748 } else if (event->postponed - current <= (vcalprefs.alert_delay*60)
1749 && event->postponed - current + 60 > (vcalprefs.alert_delay*60)) {
1750 warn = TRUE;
1752 if (warn) {
1753 time_t tmpt = icaltime_as_timet((icaltime_from_string(event->dtstart)));
1754 gchar *estart = NULL;
1755 AlertValue aval;
1756 int length = (end - start) / 60;
1757 gchar *duration = NULL, *hours = NULL, *minutes = NULL;
1758 gchar *message = NULL;
1759 gchar *title = NULL;
1760 gchar *label = NULL;
1761 int postpone_min = 0;
1763 tzset();
1765 estart = g_strdup(ctime(&tmpt));
1767 if (length >= 60)
1768 hours = g_strdup_printf(ngettext("%d hour", "%d hours",
1769 (length/60) > 1 ? 2 : 1), length/60);
1770 if (length%60)
1771 minutes = g_strdup_printf(ngettext("%d minute", "%d minutes",
1772 length%60), length%60);
1774 duration = g_strdup_printf("%s%s%s",
1775 hours?hours:"",
1776 hours && minutes ? " ":"",
1777 minutes?minutes:"");
1779 g_free(hours);
1780 g_free(minutes);
1782 title = g_strdup_printf(_("Upcoming event: %s"), event->summary);
1783 message = g_strdup_printf(_("You have a meeting or event soon.\n"
1784 "It starts at %s and ends %s later.\n"
1785 "Location: %s\n"
1786 "More information:\n\n"
1787 "%s"),
1788 estart,
1789 duration,
1790 event->location?event->location:"",
1791 event->description);
1793 g_free(duration);
1794 g_free(estart);
1796 postpone_min = (vcalprefs.alert_delay/2 > 15) ? 15: (vcalprefs.alert_delay/2);
1797 if (postpone_min == 0)
1798 postpone_min = 1;
1800 label = g_strdup_printf(ngettext("Remind me in %d minute", "Remind me in %d minutes",
1801 postpone_min > 1 ? 2:1),
1802 postpone_min);
1803 aval = alertpanel_full(title, message,
1804 NULL, label, NULL, _("_OK"), NULL, NULL,
1805 ALERTFOCUS_FIRST, FALSE, NULL, ALERT_NOTICE);
1806 g_free(label);
1808 g_free(title);
1809 g_free(message);
1811 if (aval == G_ALERTDEFAULT) {
1812 if (event->postponed == 0)
1813 event->postponed = start + (postpone_min*60);
1814 else
1815 event->postponed += (postpone_min*60);
1816 } else {
1817 event->postponed = (time_t)0;
1819 vcal_manager_save_event(event, FALSE);
1822 vcal_manager_free_event((VCalEvent *)cur->data);
1825 g_slist_free(events);
1827 return TRUE;
1830 void multisync_export(void)
1832 GSList *list = NULL;
1833 gchar *path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1834 "vcalendar", G_DIR_SEPARATOR_S,
1835 "multisync", NULL);
1836 GSList *files = NULL;
1837 GSList *cur = NULL;
1838 gchar *backup_file;
1839 gint i = 0;
1840 icalcomponent *calendar = NULL;
1841 FILE *fp;
1843 if (is_dir_exist(path) && remove_dir_recursive(path) < 0) {
1844 g_free(path);
1845 return;
1847 if (make_dir(path) != 0) {
1848 g_free(path);
1849 return;
1852 list = vcal_folder_get_waiting_events();
1853 for (cur = list; cur; cur = cur->next) {
1854 gchar *tmp = NULL;
1855 VCalEvent *event = (VCalEvent *)cur->data;
1856 gchar *file = g_strdup_printf("multisync%"CM_TIME_FORMAT"-%d",
1857 time(NULL), i);
1859 i++;
1861 calendar =
1862 icalcomponent_vanew(
1863 ICAL_VCALENDAR_COMPONENT,
1864 icalproperty_new_version("2.0"),
1865 icalproperty_new_prodid(
1866 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
1867 icalproperty_new_calscale("GREGORIAN"),
1868 (void*)0
1870 vcal_manager_event_dump(event, FALSE, FALSE, calendar, FALSE);
1871 tmp = g_strconcat(path, G_DIR_SEPARATOR_S, file, NULL);
1872 str_write_to_file(icalcomponent_as_ical_string(calendar), tmp, TRUE);
1873 g_free(tmp);
1874 files = g_slist_append(files, file);
1875 vcal_manager_free_event(event);
1876 icalcomponent_free(calendar);
1879 g_slist_free(list);
1881 backup_file = g_strconcat(path, G_DIR_SEPARATOR_S, "backup_entries", NULL);
1882 fp = claws_fopen(backup_file, "wb");
1883 if (fp) {
1884 for (cur = files; cur; cur = cur->next) {
1885 gchar * file = (char *)cur->data;
1886 if (fprintf(fp, "1 1 %s\n", file) < 0)
1887 FILE_OP_ERROR(file, "fprintf");
1888 g_free(file);
1890 if (claws_safe_fclose(fp) == EOF)
1891 FILE_OP_ERROR(backup_file, "claws_fclose");
1892 } else {
1893 FILE_OP_ERROR(backup_file, "claws_fopen");
1895 g_free(backup_file);
1896 g_free(path);
1897 g_slist_free(files);
1900 gboolean vcal_meeting_export_calendar(const gchar *path,
1901 const gchar *user, const gchar *pass,
1902 gboolean automatic)
1904 GSList *list = vcal_folder_get_waiting_events();
1905 GSList *subs = NULL;
1906 GSList *cur;
1907 icalcomponent *calendar = NULL;
1908 gchar *file;
1909 gchar *tmpfile = get_tmp_file();
1910 gchar *internal_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1911 "vcalendar", G_DIR_SEPARATOR_S,
1912 "internal.ics", NULL);
1914 gboolean res = TRUE;
1915 long filesize = 0;
1917 multisync_export();
1919 if (vcalprefs.export_subs && vcalprefs.export_enable)
1920 subs = vcal_folder_get_webcal_events();
1922 if (g_slist_length(list) == 0 && g_slist_length(subs) == 0) {
1923 g_slist_free(list);
1924 g_slist_free(subs);
1925 if (!automatic) {
1926 alertpanel_full(_("Empty calendar"),
1927 _("There is nothing to export."),
1928 NULL, _("_OK"), NULL, NULL, NULL, NULL,
1929 ALERTFOCUS_FIRST, FALSE, NULL, ALERT_NOTICE);
1930 g_free(tmpfile);
1931 g_free(internal_file);
1932 return FALSE;
1933 } else {
1934 str_write_to_file("", tmpfile, TRUE);
1935 goto putfile;
1939 calendar =
1940 icalcomponent_vanew(
1941 ICAL_VCALENDAR_COMPONENT,
1942 icalproperty_new_version("2.0"),
1943 icalproperty_new_prodid(
1944 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
1945 icalproperty_new_calscale("GREGORIAN"),
1946 (void*)0
1949 for (cur = list; cur; cur = cur->next) {
1950 VCalEvent *event = (VCalEvent *)cur->data;
1951 vcal_manager_event_dump(event, FALSE, FALSE, calendar, FALSE);
1952 vcal_manager_free_event(event);
1955 if (str_write_to_file(icalcomponent_as_ical_string(calendar), internal_file, TRUE) < 0) {
1956 g_warning("can't export internal cal");
1959 for (cur = subs; cur; cur = cur->next) {
1960 /* Not to be freed */
1961 icalcomponent *event = (icalcomponent *)cur->data;
1962 vcal_manager_icalevent_dump(event, NULL, calendar);
1965 if (vcalprefs.export_enable || path == NULL) {
1966 if (str_write_to_file(icalcomponent_as_ical_string(calendar), tmpfile, TRUE) < 0) {
1967 alertpanel_error(_("Could not export the calendar."));
1968 g_free(tmpfile);
1969 icalcomponent_free(calendar);
1970 g_slist_free(list);
1971 g_slist_free(subs);
1972 return FALSE;
1974 filesize = strlen(icalcomponent_as_ical_string(calendar));
1977 icalcomponent_free(calendar);
1979 putfile:
1980 g_free(internal_file);
1981 g_slist_free(list);
1982 g_slist_free(subs);
1984 if (automatic && (!path || strlen(path) == 0 || !vcalprefs.export_enable)) {
1985 g_free(tmpfile);
1986 return TRUE;
1989 if (!path && !automatic)
1990 file = filesel_select_file_save(_("Export calendar to ICS"), NULL);
1991 else
1992 file = g_strdup(path);
1994 if (file
1995 && strncmp(file, "http://", 7)
1996 && strncmp(file, "https://", 8)
1997 && strncmp(file, "webcal://", 9)
1998 && strncmp(file, "webcals://", 10)
1999 && strncmp(file, "ftp://", 6)) {
2000 gchar *afile;
2001 if (file[0] != G_DIR_SEPARATOR)
2002 afile = g_strdup_printf("%s%s%s", get_home_dir(),
2003 G_DIR_SEPARATOR_S, file);
2004 else
2005 afile = g_strdup(file);
2006 if (move_file(tmpfile, afile, TRUE) != 0) {
2007 log_error(LOG_PROTOCOL, _("Couldn't export calendar to '%s'\n"),
2008 afile);
2009 res = FALSE;
2011 g_free(afile);
2012 } else if (file) {
2013 FILE *fp = claws_fopen(tmpfile, "rb");
2014 if (!strncmp(file, "webcal", 6)) {
2015 gchar *tmp = g_strdup_printf("http%s", file+6);
2016 g_free(file);
2017 file = tmp;
2019 if (fp) {
2020 res = vcal_curl_put(file, fp, filesize, user, (pass != NULL ? pass : ""));
2021 claws_fclose(fp);
2024 g_free(tmpfile);
2025 if (file)
2026 g_free(file);
2027 return res;
2030 gboolean vcal_meeting_export_freebusy(const gchar *path, const gchar *user,
2031 const gchar *pass)
2033 GSList *list = vcal_folder_get_waiting_events();
2034 GSList *cur;
2035 icalcomponent *calendar = NULL, *timezone = NULL, *tzc = NULL, *vfreebusy = NULL;
2036 gchar *file;
2037 gchar *tmpfile = get_tmp_file();
2038 gchar *internal_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2039 "vcalendar", G_DIR_SEPARATOR_S,
2040 "internal.ifb", NULL);
2041 time_t whole_start = time(NULL);
2042 time_t whole_end = whole_start + (60*60*24*365);
2043 gboolean res = TRUE;
2044 struct icaltimetype itt_start, itt_end;
2045 long filesize = 0;
2047 multisync_export();
2049 calendar =
2050 icalcomponent_vanew(
2051 ICAL_VCALENDAR_COMPONENT,
2052 icalproperty_new_version("2.0"),
2053 icalproperty_new_prodid(
2054 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
2055 icalproperty_new_calscale("GREGORIAN"),
2056 (void*)0
2059 timezone = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
2061 icalcomponent_add_property(timezone,
2062 icalproperty_new_tzid("UTC"));
2064 tzc = icalcomponent_new(ICAL_XSTANDARD_COMPONENT);
2065 icalcomponent_add_property(tzc,
2066 icalproperty_new_dtstart(
2067 icaltime_from_string("19700101T000000")));
2068 icalcomponent_add_property(tzc,
2069 icalproperty_new_tzoffsetfrom(0.0));
2070 icalcomponent_add_property(tzc,
2071 icalproperty_new_tzoffsetto(0.0));
2072 icalcomponent_add_property(tzc,
2073 icalproperty_new_tzname("Greenwich meridian time"));
2075 icalcomponent_add_component(timezone, tzc);
2077 icalcomponent_add_component(calendar, timezone);
2079 itt_start = icaltime_from_timet_with_zone(whole_start, FALSE, NULL);
2080 itt_end = icaltime_from_timet_with_zone(whole_end, FALSE, NULL);
2081 itt_start.second = itt_start.minute = itt_start.hour = 0;
2082 itt_end.second = 59; itt_end.minute = 59; itt_end.hour = 23;
2085 vfreebusy =
2086 icalcomponent_vanew(
2087 ICAL_VFREEBUSY_COMPONENT,
2088 icalproperty_vanew_dtstart(itt_start, 0),
2089 icalproperty_vanew_dtend(itt_end, 0),
2090 (void*)0
2093 debug_print("DTSTART:%s\nDTEND:%s\n",
2094 icaltime_as_ical_string(itt_start),
2095 icaltime_as_ical_string(itt_end));
2097 for (cur = list; cur; cur = cur->next) {
2098 VCalEvent *event = (VCalEvent *)cur->data;
2099 icalproperty *prop;
2100 struct icalperiodtype ipt;
2102 ipt.start = icaltime_from_string(event->dtstart);
2103 ipt.end = icaltime_from_string(event->dtend);
2104 ipt.duration = icaltime_subtract(ipt.end, ipt.start);
2105 if (icaltime_as_timet(ipt.start) <= icaltime_as_timet(itt_end)
2106 && icaltime_as_timet(ipt.end) >= icaltime_as_timet(itt_start)) {
2107 prop = icalproperty_new_freebusy(ipt);
2108 icalcomponent_add_property(vfreebusy, prop);
2110 vcal_manager_free_event(event);
2113 icalcomponent_add_component(calendar, vfreebusy);
2115 if (str_write_to_file(icalcomponent_as_ical_string(calendar), internal_file, TRUE) < 0) {
2116 g_warning("can't export freebusy");
2119 g_free(internal_file);
2121 if (vcalprefs.export_freebusy_enable) {
2122 if (str_write_to_file(icalcomponent_as_ical_string(calendar), tmpfile, TRUE) < 0) {
2123 alertpanel_error(_("Could not export the freebusy info."));
2124 g_free(tmpfile);
2125 icalcomponent_free(calendar);
2126 g_slist_free(list);
2127 return FALSE;
2129 filesize = strlen(icalcomponent_as_ical_string(calendar));
2132 icalcomponent_free(calendar);
2133 g_slist_free(list);
2135 if ((!path || strlen(path) == 0 || !vcalprefs.export_freebusy_enable)) {
2136 g_free(tmpfile);
2137 return TRUE;
2140 file = g_strdup(path);
2142 if (file
2143 && strncmp(file, "http://", 7)
2144 && strncmp(file, "https://", 8)
2145 && strncmp(file, "webcal://", 9)
2146 && strncmp(file, "webcals://", 10)
2147 && strncmp(file, "ftp://", 6)) {
2148 gchar *afile;
2149 if (file[0] != G_DIR_SEPARATOR)
2150 afile = g_strdup_printf("%s%s%s", get_home_dir(),
2151 G_DIR_SEPARATOR_S, file);
2152 else
2153 afile = g_strdup(file);
2154 if (move_file(tmpfile, file, TRUE) != 0) {
2155 log_error(LOG_PROTOCOL, _("Couldn't export free/busy to '%s'\n"),
2156 afile);
2157 res = FALSE;
2159 g_free(afile);
2160 } else if (file) {
2161 FILE *fp = claws_fopen(tmpfile, "rb");
2162 if (!strncmp(file, "webcal", 6)) {
2163 gchar *tmp = g_strdup_printf("http%s", file+6);
2164 g_free(file);
2165 file = tmp;
2167 if (fp) {
2168 res = vcal_curl_put(file, fp, filesize, user, (pass != NULL ? pass : ""));
2169 claws_fclose(fp);
2172 g_free(tmpfile);
2173 if (file)
2174 g_free(file);
2175 return res;