Improve some sieve-related translations
[claws.git] / src / plugins / vcalendar / vcal_meeting_gtk.c
blob96779cfce2100bbe4ef45ff27b56814f29cf6224
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;
94 struct _VCalAttendee {
95 GtkWidget *address;
96 GtkWidget *remove_btn;
97 GtkWidget *add_btn;
98 GtkWidget *cutype;
99 GtkWidget *hbox;
100 VCalMeeting *meet;
101 gchar *status;
102 GtkWidget *avail_evtbox;
103 GtkWidget *avail_img;
104 gchar *cached_contents;
105 gboolean org;
108 static GdkCursor *watch_cursor = NULL;
110 VCalAttendee *attendee_add(VCalMeeting *meet, gchar *address, gchar *name, gchar *partstat, gchar *cutype, gboolean first);
112 #ifndef GENERIC_UMPC
113 #define TABLE_ADD_LINE(label_text, widget, do_space) { \
114 gchar *tmpstr = g_strdup_printf("<span weight=\"bold\">%s</span>", \
115 label_text?label_text:""); \
116 GtkWidget *label = NULL; \
117 GtkWidget *spacer = NULL; \
118 GtkWidget *s_hbox = NULL; \
119 if (do_space) { \
120 spacer = gtk_label_new(""); \
121 gtk_widget_set_size_request(spacer, 18, 16); \
122 s_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); \
123 gtk_box_pack_start(GTK_BOX(s_hbox), spacer, FALSE, FALSE, 0); \
124 gtk_box_pack_start(GTK_BOX(s_hbox), widget, TRUE, TRUE, 0); \
126 if (label_text) { \
127 label = gtk_label_new(tmpstr); \
128 g_free(tmpstr); \
129 gtk_label_set_use_markup (GTK_LABEL (label), TRUE); \
130 gtk_label_set_xalign (GTK_LABEL(label), 1.0); \
131 gtk_grid_attach(GTK_GRID(meet->table), label, 0, i, 1, 1); \
132 gtk_grid_attach(GTK_GRID(meet->table), do_space?s_hbox:widget, \
133 1, i, 1, 1); \
134 gtk_widget_set_hexpand(do_space?s_hbox:widget, TRUE); \
135 gtk_widget_set_halign(do_space?s_hbox:widget, GTK_ALIGN_FILL); \
136 if (GTK_IS_LABEL(widget)) { \
137 gtk_label_set_use_markup(GTK_LABEL (widget), TRUE); \
138 gtk_label_set_xalign(GTK_LABEL(widget), 0.0); \
139 gtk_label_set_yalign(GTK_LABEL(widget), 0.0); \
140 gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE); \
142 } else { \
143 g_free(tmpstr); \
144 gtk_grid_attach(GTK_GRID(meet->table), do_space?s_hbox:widget, \
145 0, i, 1, 1); \
146 gtk_widget_set_hexpand(do_space?s_hbox:widget, TRUE); \
147 gtk_widget_set_halign(do_space?s_hbox:widget, GTK_ALIGN_FILL); \
149 i++; \
151 #else
152 #define TABLE_ADD_LINE(label_text, widget, do_space, intable1) { \
153 gchar *tmpstr = g_strdup_printf("<span weight=\"bold\">%s</span>", \
154 label_text?label_text:""); \
155 GtkWidget *label = NULL; \
156 GtkWidget *spacer = NULL; \
157 GtkWidget *s_hbox = NULL; \
158 if (do_space) { \
159 spacer = gtk_label_new(""); \
160 gtk_widget_set_size_request(spacer, 18, 16); \
161 s_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); \
162 gtk_box_pack_start(GTK_BOX(s_hbox), spacer, FALSE, FALSE, 0); \
163 gtk_box_pack_start(GTK_BOX(s_hbox), widget, TRUE, TRUE, 0); \
165 if (label_text) { \
166 label = gtk_label_new(tmpstr); \
167 g_free(tmpstr); \
168 gtk_label_set_use_markup (GTK_LABEL (label), TRUE); \
169 gtk_label_set_xalign (GTK_LABEL(label), 1.0); \
170 if(intable1) { \
171 gtk_grid_attach(GTK_GRID(meet->table1), label, \
172 0, i, 1, 1); \
174 else { \
175 gtk_grid_attach(GTK_GRID(meet->table2), label, \
176 0, i, 1, 1); \
178 if(intable1) { \
179 gtk_grid_attach(GTK_GRID(meet->table1), \
180 do_space?s_hbox:widget, 1, i, 1, 1); \
182 else { \
183 gtk_grid_attach(GTK_GRID(meet->table2), \
184 do_space?s_hbox:widget, 1, i, 1, 1); \
186 if (GTK_IS_LABEL(widget)) { \
187 gtk_label_set_use_markup(GTK_LABEL (widget), TRUE); \
188 gtk_label_set_xalign(GTK_LABEL(widget), 0.0); \
189 gtk_label_set_yalign(GTK_LABEL(widget), 0.0); \
190 gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE); \
192 } else { \
193 g_free(tmpstr); \
194 if(intable1) { \
195 gtk_grid_attach(GTK_GRID(meet->table1), \
196 do_space?s_hbox:widget, 0, i, 1, 1); \
197 gtk_widget_set_hexpand(do_space?s_hbox:widget, TRUE); \
198 gtk_widget_set_halign(do_space?s_hbox:widget, \
199 GTK_ALIGN_FILL); \
201 else { \
202 gtk_grid_attach(GTK_GRID(meet->table2), \
203 do_space?s_hbox:widget, 0, i, 1, 1); \
204 gtk_widget_set_hexpand(do_space?s_hbox:widget, TRUE); \
205 gtk_widget_set_halign(do_space?s_hbox:widget, \
206 GTK_ALIGN_FILL); \
209 i++; \
211 #endif
212 enum {
213 DAY,
214 MONTH,
215 YEAR,
216 HOUR,
217 MINUTE
220 static gboolean avail_btn_can_be_sensitive(void)
222 if (vcalprefs.freebusy_get_url == NULL
223 || *vcalprefs.freebusy_get_url == '\0')
224 return FALSE;
225 else
226 return TRUE;
229 static gint get_dtdate(const gchar *str, gint field)
231 time_t t = icaltime_as_timet((icaltime_from_string(str)));
232 struct tm buft;
233 struct tm *lt;
235 tzset();
237 #ifdef G_OS_WIN32
238 if (t < 0)
239 t = 1;
240 #endif
241 lt = localtime_r(&t, &buft);
243 switch(field){
244 case DAY:
245 return lt->tm_mday;
246 case MONTH:
247 return lt->tm_mon + 1;
248 case YEAR:
249 return lt->tm_year + 1900;
250 case HOUR:
251 return lt->tm_hour;
252 case MINUTE:
253 return lt->tm_min;
255 return -1;
259 static void set_watch_cursor(GdkWindow *window)
261 cm_return_if_fail(window != NULL);
263 if (!watch_cursor)
264 watch_cursor = gdk_cursor_new_for_display(
265 gdk_window_get_display(window), GDK_WATCH);
269 static gboolean add_btn_cb(GtkButton *widget, gpointer data)
271 VCalAttendee *attendee = (VCalAttendee *)data;
272 attendee_add(attendee->meet, NULL, NULL, NULL, NULL, FALSE);
273 return TRUE;
276 static gboolean remove_btn_cb(GtkButton *widget, gpointer data)
278 VCalAttendee *attendee = (VCalAttendee *)data;
279 gtk_container_remove(GTK_CONTAINER(attendee->meet->attendees_vbox), attendee->hbox);
280 attendee->meet->attendees = g_slist_remove(attendee->meet->attendees, attendee);
282 g_free(attendee->status);
284 return TRUE;
287 VCalAttendee *attendee_add(VCalMeeting *meet, gchar *address, gchar *name, gchar *partstat, gchar *cutype, gboolean first)
289 GtkWidget *att_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
290 VCalAttendee *attendee = g_new0(VCalAttendee, 1);
292 attendee->address = gtk_entry_new();
293 attendee->cutype = gtk_combo_box_text_new();
294 attendee->avail_evtbox = gtk_event_box_new();
295 attendee->avail_img = gtk_image_new_from_icon_name
296 ("dialog-warning", GTK_ICON_SIZE_SMALL_TOOLBAR);
298 gtk_widget_show(attendee->address);
299 gtk_widget_show(attendee->cutype);
300 gtk_widget_show(attendee->avail_evtbox);
302 CLAWS_SET_TIP(attendee->address, _("Use <tab> to autocomplete from addressbook"));
303 gtk_widget_set_size_request(attendee->avail_evtbox, 18, 16);
304 gtk_event_box_set_visible_window(GTK_EVENT_BOX(attendee->avail_evtbox), FALSE);
305 gtk_container_add (GTK_CONTAINER(attendee->avail_evtbox), attendee->avail_img);
307 if (address) {
308 gchar *str = g_strdup_printf("%s%s%s%s",
309 (name && strlen(name))?name:"",
310 (name && strlen(name))?" <":"",
311 address,
312 (name && strlen(name))?">":"");
313 gtk_entry_set_text(GTK_ENTRY(attendee->address), str);
314 g_free(str);
317 if (partstat)
318 attendee->status = g_strdup(partstat);
320 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(attendee->cutype), _("Individual"));
321 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(attendee->cutype), _("Group"));
322 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(attendee->cutype), _("Resource"));
323 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(attendee->cutype), _("Room"));
325 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 0);
327 if (cutype) {
328 if (!strcmp(cutype, "group"))
329 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 1);
330 if (!strcmp(cutype, "resource"))
331 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 2);
332 if (!strcmp(cutype, "room"))
333 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 3);
336 attendee->add_btn = gtk_button_new_with_label(_("Add..."));
337 attendee->remove_btn = gtk_button_new_with_label(_("Remove"));
338 attendee->meet = meet;
339 attendee->hbox = att_hbox;
341 gtk_widget_show(attendee->add_btn);
342 gtk_widget_show(attendee->remove_btn);
343 gtk_widget_show(attendee->hbox);
345 gtk_box_pack_start(GTK_BOX(attendee->hbox), attendee->avail_evtbox, FALSE, FALSE, 0);
346 gtk_widget_set_sensitive(attendee->remove_btn, !first);
347 meet->attendees = g_slist_append(meet->attendees, attendee);
349 g_signal_connect(G_OBJECT(attendee->remove_btn), "clicked",
350 G_CALLBACK(remove_btn_cb), attendee);
351 g_signal_connect(G_OBJECT(attendee->add_btn), "clicked",
352 G_CALLBACK(add_btn_cb), attendee);
354 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->address, FALSE, FALSE, 0);
355 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->cutype, FALSE, FALSE, 0);
356 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->add_btn, FALSE, FALSE, 0);
357 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->remove_btn, FALSE, FALSE, 0);
358 gtk_box_pack_start(GTK_BOX(meet->attendees_vbox), att_hbox, FALSE, FALSE, 0);
359 address_completion_register_entry(GTK_ENTRY(attendee->address), FALSE);
360 #ifndef GENERIC_UMPC
361 gtk_widget_set_size_request(attendee->address, 320, -1);
362 #else
363 gtk_widget_set_size_request(attendee->address, 220, -1);
364 #endif
365 return attendee;
368 static gchar *get_organizer(VCalMeeting *meet)
370 int index = gtk_combo_box_get_active(GTK_COMBO_BOX(meet->who));
371 int i = 0;
372 GSList *cur = meet->avail_accounts;
373 while (i < index && cur && cur->data) {
374 debug_print("%d:skipping %s\n",i,((PrefsAccount *)(cur->data))->address);
375 cur = cur->next;
376 i++;
378 if (cur && cur->data)
379 return g_strdup(((PrefsAccount *)(cur->data))->address);
380 else
381 return g_strdup("");
384 static gchar *get_organizer_name(VCalMeeting *meet)
386 int index = gtk_combo_box_get_active(GTK_COMBO_BOX(meet->who));
387 int i = 0;
388 GSList *cur = meet->avail_accounts;
389 while (i < index && cur && cur->data) {
390 debug_print("%d:skipping %s\n",i,((PrefsAccount *)(cur->data))->address);
391 cur = cur->next;
392 i++;
394 if (cur && cur->data)
395 return g_strdup(((PrefsAccount *)(cur->data))->name);
396 else
397 return g_strdup("");
400 static int get_current_gmt_offset(void)
402 time_t now = time(NULL);
403 struct tm gmt;
404 struct tm local;
406 tzset();
408 #ifdef G_OS_WIN32
409 if (now < 0)
410 now = 1;
411 #endif
412 gmtime_r(& now, & gmt);
413 localtime_r(& now, & local);
415 local.tm_isdst = 0;
416 return mktime(&local)-mktime(&gmt);
419 static int get_gmt_offset_at_time(time_t then)
421 struct tm gmt;
422 struct tm local;
424 tzset();
426 #ifdef G_OS_WIN32
427 if (then < 0)
428 then = 1;
429 #endif
430 gmtime_r(& then, & gmt);
431 localtime_r(& then, & local);
433 local.tm_isdst = 0;
434 return mktime(&local)-mktime(&gmt);
437 static gchar *get_date(VCalMeeting *meet, int start)
439 struct tm *lt;
440 time_t t;
441 guint d, m, y;
442 int dst_offset = 0;
443 struct tm buft;
445 tzset();
447 t = time(NULL);
448 #ifdef G_OS_WIN32
449 if (t < 0)
450 t = 1;
451 #endif
452 lt = localtime_r(&t, &buft);
454 gtk_calendar_get_date(GTK_CALENDAR(start ? meet->start_c : meet->end_c), &y, &m, &d);
455 lt->tm_mday = d;
456 lt->tm_mon = m;
457 lt->tm_year = y - 1900;
458 lt->tm_hour = 0;
459 lt->tm_min = 0;
460 lt->tm_sec = 0;
462 if (start) {
463 gtkut_time_select_get_time(GTK_COMBO_BOX(meet->start_time), &lt->tm_hour, &lt->tm_min);
464 } else {
465 gtkut_time_select_get_time(GTK_COMBO_BOX(meet->end_time), &lt->tm_hour, &lt->tm_min);
468 debug_print("%d %d %d, %d:%d\n", lt->tm_mday, lt->tm_mon, lt->tm_year, lt->tm_hour, lt->tm_min);
469 t = mktime(lt);
471 dst_offset = get_current_gmt_offset() - get_gmt_offset_at_time(t);
472 debug_print("DST change offset to apply to time %d\n", dst_offset);
473 t += dst_offset;
474 debug_print("%s\n", ctime(&t));
475 return g_strdup(icaltime_as_ical_string(icaltime_from_timet_with_zone(t, FALSE, NULL)));
478 static gchar *get_location(VCalMeeting *meet)
480 return gtk_editable_get_chars(GTK_EDITABLE(meet->location),0, -1);
483 static gchar *get_summary(VCalMeeting *meet)
485 return gtk_editable_get_chars(GTK_EDITABLE(meet->summary),0, -1);
488 static gchar *get_description(VCalMeeting *meet)
490 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(meet->description));
491 GtkTextIter start, end;
493 gtk_text_buffer_get_start_iter(buffer, &start);
494 gtk_text_buffer_get_end_iter(buffer, &end);
495 return gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
498 void vcal_meeting_free(VCalMeeting *meet)
500 debug_print("freeing meeting\n");
501 g_free(meet->uid);
502 address_completion_end(meet->window);
503 g_slist_free(meet->avail_accounts);
504 g_slist_free(meet->attendees);
505 g_free(meet);
508 static void destroy_meeting_cb(GtkWidget *widget, gpointer data)
510 VCalMeeting *meet = (VCalMeeting *)data;
511 vcal_meeting_free(meet);
514 static void vcal_destroy(VCalMeeting *meet)
516 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(meet->description));
517 gtk_text_buffer_remove_selection_clipboard(buffer, gtk_clipboard_get(GDK_SELECTION_PRIMARY));
518 gtk_widget_destroy(meet->window);
521 static gboolean meeting_key_pressed(GtkWidget *widget,
522 GdkEventKey *event,
523 gpointer data)
525 VCalMeeting *meet = (VCalMeeting *)data;
527 if (event && event->keyval == GDK_KEY_Escape) {
528 vcal_destroy(meet);
530 return FALSE;
533 static void meeting_end_changed(GtkWidget *widget, gpointer data);
535 static void meeting_start_changed(GtkWidget *widget, gpointer data)
537 VCalMeeting *meet = (VCalMeeting *)data;
538 struct tm start_lt;
539 struct tm end_lt;
540 time_t start_t, end_t;
541 guint d, m, y;
543 if (!gtkut_time_select_get_time(GTK_COMBO_BOX(meet->start_time), &start_lt.tm_hour, &start_lt.tm_min))
544 return;
545 tzset();
547 start_t = time(NULL);
548 end_t = time(NULL);
549 #ifdef G_OS_WIN32
550 if (start_t < 0)
551 start_t = 1;
552 if (end_t < 0)
553 end_t = 1;
554 #endif
555 localtime_r(&start_t, &start_lt);
556 localtime_r(&end_t, &end_lt);
558 gtk_calendar_get_date(GTK_CALENDAR(meet->start_c), &y, &m, &d);
559 start_lt.tm_mday = d; start_lt.tm_mon = m; start_lt.tm_year = y - 1900;
561 start_t = mktime(&start_lt);
562 debug_print("start %s\n", ctime(&start_t));
564 gtk_calendar_get_date(GTK_CALENDAR(meet->end_c), &y, &m, &d);
565 end_lt.tm_mday = d; end_lt.tm_mon = m; end_lt.tm_year = y - 1900;
567 gtkut_time_select_get_time(GTK_COMBO_BOX(meet->end_time), &end_lt.tm_hour, &end_lt.tm_min);
569 end_t = mktime(&end_lt);
571 debug_print("end %s\n", ctime(&end_t));
573 if (end_t > start_t) {
574 debug_print("ok\n");
575 return;
577 end_t = start_t + 3600;
579 #ifdef G_OS_WIN32
580 if (end_t < 0)
581 end_t = 1;
582 #endif
583 localtime_r(&end_t, &end_lt);
584 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);
586 g_signal_handlers_block_by_func(gtk_bin_get_child(GTK_BIN(meet->end_time)), meeting_end_changed, meet);
587 g_signal_handlers_block_by_func(meet->end_c, meeting_end_changed, meet);
589 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c), end_lt.tm_mday);
591 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
592 end_lt.tm_mon,
593 end_lt.tm_year + 1900);
595 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->end_time), end_lt.tm_hour, end_lt.tm_min);
597 g_signal_handlers_unblock_by_func(gtk_bin_get_child(GTK_BIN(meet->end_time)), meeting_end_changed, meet);
598 g_signal_handlers_unblock_by_func(meet->end_c, meeting_end_changed, meet);
601 static void meeting_end_changed(GtkWidget *widget, gpointer data)
603 VCalMeeting *meet = (VCalMeeting *)data;
604 struct tm start_lt;
605 struct tm end_lt;
606 time_t start_t, end_t;
607 guint d, m, y;
609 if (!gtkut_time_select_get_time(GTK_COMBO_BOX(meet->end_time), &end_lt.tm_hour, &end_lt.tm_min))
610 return;
611 start_t = time(NULL);
612 end_t = time(NULL);
614 tzset();
616 #ifdef G_OS_WIN32
617 if (start_t < 0)
618 start_t = 1;
619 if (end_t < 0)
620 end_t = 1;
621 #endif
622 localtime_r(&start_t, &start_lt);
623 localtime_r(&end_t, &end_lt);
625 gtk_calendar_get_date(GTK_CALENDAR(meet->start_c), &y, &m, &d);
626 start_lt.tm_mday = d; start_lt.tm_mon = m; start_lt.tm_year = y - 1900;
627 gtkut_time_select_get_time(GTK_COMBO_BOX(meet->start_time), &start_lt.tm_hour, &start_lt.tm_min);
629 start_t = mktime(&start_lt);
630 debug_print("start %s\n", ctime(&start_t));
632 gtk_calendar_get_date(GTK_CALENDAR(meet->end_c), &y, &m, &d);
633 end_lt.tm_mday = d; end_lt.tm_mon = m; end_lt.tm_year = y - 1900;
635 end_t = mktime(&end_lt);
637 debug_print("end %s\n", ctime(&end_t));
639 if (end_t > start_t) {
640 debug_print("ok\n");
641 return;
643 start_t = end_t - 3600;
645 tzset();
647 #ifdef G_OS_WIN32
648 if (start_t < 0)
649 start_t = 1;
650 #endif
651 localtime_r(&start_t, &start_lt);
652 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);
654 g_signal_handlers_block_by_func(gtk_bin_get_child(GTK_BIN(meet->start_time)), meeting_start_changed, meet);
655 g_signal_handlers_block_by_func(meet->start_c, meeting_start_changed, meet);
657 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c), start_lt.tm_mday);
659 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
660 start_lt.tm_mon,
661 start_lt.tm_year + 1900);
663 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->start_time), start_lt.tm_hour, start_lt.tm_min);
665 g_signal_handlers_unblock_by_func(gtk_bin_get_child(GTK_BIN(meet->start_time)), meeting_start_changed, meet);
666 g_signal_handlers_unblock_by_func(meet->start_c, meeting_start_changed, meet);
669 static void att_update_icon(VCalMeeting *meet, VCalAttendee *attendee, gint avail, gchar *text)
671 const gchar *icon = "dialog-information";
673 switch (avail) {
674 case 0: icon = "dialog-warning"; break;
675 case 1: icon = "dialog-information"; break;
676 default: icon = "dialog-question"; break;
678 if (!gtk_entry_get_text(GTK_ENTRY(attendee->address))
679 || strlen(gtk_entry_get_text(GTK_ENTRY(attendee->address)))==0) {
680 if (attendee->avail_img) {
681 gtk_widget_hide(attendee->avail_img);
683 CLAWS_SET_TIP(attendee->avail_evtbox, NULL);
684 } else if (attendee->avail_img) {
685 gtk_image_set_from_icon_name
686 (GTK_IMAGE(attendee->avail_img),
687 icon,
688 GTK_ICON_SIZE_SMALL_TOOLBAR);
689 gtk_widget_show(attendee->avail_img);
690 CLAWS_SET_TIP(attendee->avail_evtbox, text);
694 gboolean attendee_available(VCalAttendee *attendee, const gchar *dtstart, const gchar *dtend, const gchar *contents)
696 icalcomponent *toplvl, *vfreebusy;
697 icalproperty *busyprop;
698 struct icaltimetype start = icaltime_from_string(dtstart);
699 struct icaltimetype end = icaltime_from_string(dtend);
700 gboolean result = TRUE;
703 if (contents == NULL)
704 return TRUE;
706 toplvl = icalcomponent_new_from_string((gchar *)contents);
708 if (toplvl == NULL)
709 return TRUE;
711 vfreebusy = icalcomponent_get_first_component(toplvl, ICAL_VFREEBUSY_COMPONENT);
712 while (vfreebusy && icalcomponent_isa(vfreebusy) != ICAL_VFREEBUSY_COMPONENT)
713 vfreebusy = icalcomponent_get_next_component(toplvl, ICAL_VFREEBUSY_COMPONENT);
715 if (vfreebusy) {
716 busyprop = icalcomponent_get_first_property(vfreebusy, ICAL_FREEBUSY_PROPERTY);
717 while (busyprop) {
718 struct icalperiodtype ipt = icalproperty_get_freebusy(busyprop);
720 if ( icaltime_compare(start, ipt.end) >= 0 || icaltime_compare(end, ipt.start) <= 0 ) {
721 result = TRUE;
722 } else {
723 result = FALSE;
724 break;
726 busyprop = icalcomponent_get_next_property(vfreebusy, ICAL_FREEBUSY_PROPERTY);
730 icalcomponent_free(toplvl);
731 return result;
734 static gchar *get_avail_msg(const gchar *unavailable_persons, gboolean multiple,
735 gboolean short_version, gint offset_before, gint offset_after)
737 gchar *msg, *intro = NULL, *outro = NULL, *before = NULL, *after = NULL;
739 if (multiple)
740 intro = g_strdup(_("The following people are busy at the time of your planned meeting:\n- "));
741 else if (!strcmp(unavailable_persons, _("You")))
742 intro = g_strdup(_("You are busy at the time of your planned meeting"));
743 else
744 intro = g_strdup_printf(_("%s is busy at the time of your planned meeting"), unavailable_persons);
745 if (offset_before == 3600)
746 before = g_strdup_printf(_("%d hour sooner"), offset_before/3600);
747 else if (offset_before > 3600 && offset_before%3600 == 0)
748 before = g_strdup_printf(_("%d hours sooner"), offset_before/3600);
749 else if (offset_before > 3600)
750 before = g_strdup_printf(_("%d hours and %d minutes sooner"), offset_before/3600, (offset_before%3600)/60);
751 else if (offset_before == 1800)
752 before = g_strdup_printf(_("%d minutes sooner"), offset_before/60);
753 else
754 before = NULL;
756 if (offset_after == 3600)
757 after = g_strdup_printf(_("%d hour later"), offset_after/3600);
758 else if (offset_after > 3600 && offset_after%3600 == 0)
759 after = g_strdup_printf(_("%d hours later"), offset_after/3600);
760 else if (offset_after > 3600)
761 after = g_strdup_printf(_("%d hours and %d minutes later"), offset_after/3600, (offset_after%3600)/60);
762 else if (offset_after == 1800)
763 after = g_strdup_printf(_("%d minutes later"), offset_after/60);
764 else
765 after = NULL;
767 if (multiple) {
768 if (before && after)
769 outro = g_strdup_printf(_("\n\nEveryone would be available %s or %s."), before, after);
770 else if (before || after)
771 outro = g_strdup_printf(_("\n\nEveryone would be available %s."), before?before:after);
772 else
773 outro = g_strdup_printf(_("\n\nIt isn't possible to have this meeting with everyone "
774 "in the previous or next 6 hours."));
775 } else {
776 if (short_version) {
777 if (before && after)
778 outro = g_markup_printf_escaped(_("would be available %s or %s"), before, after);
779 else if (before || after)
780 outro = g_markup_printf_escaped(_("would be available %s"), before?before:after);
781 else
782 outro = g_strdup_printf(_("not available"));
783 } else {
784 if (before && after)
785 outro = g_markup_printf_escaped(_(", but would be available %s or %s."), before, after);
786 else if (before || after)
787 outro = g_markup_printf_escaped(_(", but would be available %s."), before?before:after);
788 else
789 outro = g_strdup_printf(_(", and isn't available "
790 "in the previous or next 6 hours."));
793 if (multiple && short_version)
794 msg = g_strconcat(outro+2, NULL);
795 else if (multiple)
796 msg = g_strconcat(intro, unavailable_persons, outro, NULL);
797 else if (short_version)
798 msg = g_strdup(outro);
799 else
800 msg = g_strconcat(intro, outro, NULL);
801 g_free(intro);
802 g_free(outro);
803 g_free(before);
804 g_free(after);
805 return msg;
808 static gboolean find_availability(const gchar *dtstart, const gchar *dtend, GSList *attendees, gboolean for_send, VCalMeeting *meet)
810 GSList *cur;
811 gint offset = -1800, offset_before = 0, offset_after = 0;
812 gboolean found = FALSE;
813 gchar *unavailable_persons = NULL;
814 gchar *msg = NULL;
815 struct icaltimetype start = icaltime_from_string(dtstart);
816 struct icaltimetype end = icaltime_from_string(dtend);
817 AlertValue val = G_ALERTALTERNATE;
818 gint total = 0;
819 GHashTable *avail_table_avail = g_hash_table_new(NULL, g_direct_equal);
820 GHashTable *avail_table_before = g_hash_table_new(NULL, g_direct_equal);
821 GHashTable *avail_table_after = g_hash_table_new(NULL, g_direct_equal);
823 for (cur = attendees; cur; cur = cur->next) {
824 VCalAttendee *attendee = (VCalAttendee *)cur->data;
825 if (!attendee_available(attendee, icaltime_as_ical_string(start), icaltime_as_ical_string(end),
826 attendee->cached_contents)) {
827 gchar *mail = NULL;
829 if (attendee->org)
830 mail = g_strdup(_("You"));
831 else
832 mail = gtk_editable_get_chars(GTK_EDITABLE(attendee->address), 0, -1);
834 if (unavailable_persons == NULL) {
835 unavailable_persons = g_markup_printf_escaped("%s", mail);
836 } else {
837 gchar *tmp = g_markup_printf_escaped("%s,\n- %s", unavailable_persons, mail);
838 g_free(unavailable_persons);
839 unavailable_persons = tmp;
841 total++;
842 g_free(mail);
843 att_update_icon(meet, attendee, 0, _("not available"));
844 } else {
845 if (attendee->cached_contents != NULL)
846 att_update_icon(meet, attendee, 1, _("available"));
847 else
848 att_update_icon(meet, attendee, 2, _("Free/busy retrieval failed"));
850 g_hash_table_insert(avail_table_avail, attendee, GINT_TO_POINTER(1));
853 offset = -1800;
854 found = FALSE;
855 while (!found && offset >= -3600*6) {
856 gboolean ok = TRUE;
857 struct icaltimetype new_start = icaltime_from_timet_with_zone(icaltime_as_timet(start)+offset, FALSE, NULL);
858 struct icaltimetype new_end = icaltime_from_timet_with_zone(icaltime_as_timet(end)+offset, FALSE, NULL);
859 for (cur = attendees; cur; cur = cur->next) {
860 VCalAttendee *attendee = (VCalAttendee *)cur->data;
861 debug_print("trying %s - %s (offset %d)\n",
862 icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end), offset);
863 if (!attendee_available(attendee, icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end),
864 attendee->cached_contents)) {
865 ok = FALSE;
866 break;
867 } else {
868 if (!g_hash_table_lookup(avail_table_before, attendee)
869 && !g_hash_table_lookup(avail_table_avail, attendee))
870 g_hash_table_insert(avail_table_before, attendee, GINT_TO_POINTER(-offset));
873 if (ok) {
874 found = TRUE;
875 offset_before = -offset;
877 offset -= 1800;
879 found = FALSE;
880 offset = 1800;
881 while (!found && offset <= 3600*6) {
882 gboolean ok = TRUE;
883 struct icaltimetype new_start = icaltime_from_timet_with_zone(icaltime_as_timet(start)+offset, FALSE, NULL);
884 struct icaltimetype new_end = icaltime_from_timet_with_zone(icaltime_as_timet(end)+offset, FALSE, NULL);
885 for (cur = attendees; cur; cur = cur->next) {
886 VCalAttendee *attendee = (VCalAttendee *)cur->data;
887 debug_print("trying %s - %s (offset %d)\n",
888 icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end), offset);
889 if (!attendee_available(attendee, icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end),
890 attendee->cached_contents)) {
891 ok = FALSE;
892 break;
893 } else {
894 if (!g_hash_table_lookup(avail_table_after, attendee)
895 && !g_hash_table_lookup(avail_table_avail, attendee))
896 g_hash_table_insert(avail_table_after, attendee, GINT_TO_POINTER(offset));
899 if (ok) {
900 found = TRUE;
901 offset_after = offset;
904 offset += 1800;
907 for (cur = attendees; cur; cur = cur->next) {
908 VCalAttendee *attendee = (VCalAttendee *)cur->data;
909 gint ok = GPOINTER_TO_INT(g_hash_table_lookup(avail_table_avail, attendee));
910 gint o_before = GPOINTER_TO_INT(g_hash_table_lookup(avail_table_before, attendee));
911 gint o_after = GPOINTER_TO_INT(g_hash_table_lookup(avail_table_after, attendee));
912 if (!o_before && !o_after && !ok) {
913 att_update_icon(meet, attendee, 0, _("not available"));
914 } else if ((o_before != 0 || o_after != 0) && !ok) {
915 if (attendee->org)
916 msg = get_avail_msg(_("You"), FALSE, TRUE, o_before, o_after);
917 else
918 msg = get_avail_msg(gtk_entry_get_text(GTK_ENTRY(attendee->address)), FALSE, TRUE, o_before, o_after);
919 att_update_icon(meet, attendee, 0, msg);
920 g_free(msg);
924 g_hash_table_destroy(avail_table_before);
925 g_hash_table_destroy(avail_table_after);
927 if (for_send) {
928 msg = get_avail_msg(unavailable_persons, (total > 1), FALSE, offset_before, offset_after);
930 val = alertpanel_full(_("Not everyone is available"), msg,
931 NULL, _("_Cancel"), NULL, _("Send anyway"),
932 NULL, NULL, ALERTFOCUS_FIRST,
933 FALSE, NULL, ALERT_QUESTION);
934 g_free(msg);
936 msg = get_avail_msg(unavailable_persons, TRUE, TRUE, offset_before, offset_after);
937 g_free(unavailable_persons);
938 gtk_image_set_from_icon_name
939 (GTK_IMAGE(meet->total_avail_img),
940 "dialog-warning",
941 GTK_ICON_SIZE_SMALL_TOOLBAR);
942 gtk_widget_show(meet->total_avail_img);
943 gtk_label_set_text(GTK_LABEL(meet->total_avail_msg), _("Not everyone is available. "
944 "See tooltips for more info..."));
945 CLAWS_SET_TIP(meet->total_avail_evtbox, msg);
946 g_free(msg);
947 return (val == G_ALERTALTERNATE);
950 static gboolean check_attendees_availability(VCalMeeting *meet, gboolean tell_if_ok, gboolean for_send)
952 GSList *cur;
953 gchar *tmp = NULL;
954 gchar *real_url = NULL;
955 gint num_format = 0;
956 gchar *change_user = NULL, *change_dom = NULL;
957 gchar *dtstart = NULL;
958 gchar *dtend = NULL;
959 gboolean find_avail = FALSE;
960 gboolean res = TRUE, uncertain = FALSE;
961 gchar *organizer = NULL;
962 VCalAttendee *dummy_org = NULL;
963 gchar *internal_ifb = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
964 "vcalendar", G_DIR_SEPARATOR_S,
965 "internal.ifb", NULL);
966 gboolean local_only = FALSE;
967 GSList *attlist;
968 GdkWindow *gdkwin;
970 if (vcalprefs.freebusy_get_url == NULL
971 || *vcalprefs.freebusy_get_url == '\0') {
972 local_only = TRUE;
973 } else {
974 real_url = g_strdup(vcalprefs.freebusy_get_url);
975 tmp = real_url;
977 while (strchr(tmp, '%')) {
978 tmp = strchr(tmp, '%')+1;
979 num_format++;
981 if (num_format > 2) {
982 g_warning("wrong format in %s!", real_url);
983 g_free(real_url);
984 g_free(internal_ifb);
985 return FALSE;
988 tmp = NULL;
989 if (strstr(real_url, "%u") != NULL) {
990 change_user = strstr(real_url, "%u");
991 *(strstr(real_url, "%u")+1) = 's';
993 if (strstr(real_url, "%d") != NULL) {
994 change_dom = strstr(real_url, "%d");
995 *(strstr(real_url, "%d")+1) = 's';
997 debug_print("url format %s\n", real_url);
999 dtstart = get_date(meet, TRUE);
1000 dtend = get_date(meet, FALSE);
1002 /* hack to check our own avail. */
1003 organizer = get_organizer(meet);
1004 dummy_org = g_new0(VCalAttendee, 1);
1005 dummy_org->address = gtk_entry_new();
1006 dummy_org->avail_img = meet->avail_img;
1007 dummy_org->avail_evtbox = meet->avail_evtbox;
1008 dummy_org->org = TRUE;
1009 gtk_entry_set_text(GTK_ENTRY(dummy_org->address), organizer);
1010 g_free(organizer);
1011 dummy_org->cached_contents = file_read_to_str(internal_ifb);
1012 g_free(internal_ifb);
1014 if (!local_only) {
1015 meet->attendees = g_slist_prepend(meet->attendees, dummy_org);
1016 attlist = meet->attendees;
1017 } else {
1018 attlist = g_slist_prepend(NULL, dummy_org);
1021 gtk_widget_set_sensitive(meet->save_btn, FALSE);
1022 gtk_widget_set_sensitive(meet->avail_btn, FALSE);
1024 gdkwin = gtk_widget_get_window(meet->window);
1025 if (gdkwin != NULL) {
1026 set_watch_cursor(gdkwin);
1027 gdk_window_set_cursor(gdkwin, watch_cursor);
1030 for (cur = attlist; cur && cur->data; cur = cur->next) {
1031 VCalAttendee *attendee = (VCalAttendee *)cur->data;
1032 gchar *email = gtk_editable_get_chars(GTK_EDITABLE(attendee->address), 0, -1);
1033 gchar *remail, *user, *domain;
1034 gchar *contents = NULL;
1036 if (*email == '\0') {
1037 g_free(email);
1038 att_update_icon(meet, attendee, 0, NULL);
1039 continue;
1042 if (!local_only) {
1043 remail = g_strdup(email);
1045 extract_address(remail);
1046 if (strrchr(remail, ' '))
1047 user = g_strdup(strrchr(remail, ' ')+1);
1048 else
1049 user = g_strdup(remail);
1050 if (strchr(user, '@')) {
1051 domain = g_strdup(strchr(user, '@')+1);
1052 *(strchr(user, '@')) = '\0';
1053 } else {
1054 domain = g_strdup("");
1056 g_free(remail);
1057 if (change_user && change_dom) {
1058 if (change_user < change_dom)
1059 tmp = g_strdup_printf(real_url, user, domain);
1060 else
1061 tmp = g_strdup_printf(real_url, domain, user);
1062 } else if (change_user) {
1063 tmp = g_strdup_printf(real_url, user);
1064 } else if (change_dom) {
1065 tmp = g_strdup_printf(real_url, domain);
1066 } else {
1067 tmp = g_strdup(real_url);
1069 g_free(user);
1070 g_free(domain);
1071 debug_print("url to get %s\n", tmp);
1074 if (attendee->cached_contents != NULL) {
1075 contents = attendee->cached_contents;
1076 attendee->cached_contents = NULL;
1077 } else if (!local_only) {
1078 if (strncmp(tmp, "http://", 7)
1079 && strncmp(tmp, "https://", 8)
1080 && strncmp(tmp, "webcal://", 9)
1081 && strncmp(tmp, "webcals://", 10)
1082 && strncmp(tmp, "ftp://", 6)
1083 && strncmp(tmp, "ftps://", 7)
1084 && strncmp(tmp, "sftp://", 7))
1085 contents = file_read_to_str(tmp);
1086 else {
1087 gchar *label = g_strdup_printf(_("Fetching planning for %s..."), email);
1088 if (!strncmp(tmp, "webcal", 6)) {
1089 gchar *tmp2 = g_strdup_printf("http%s", tmp+6);
1090 g_free(tmp);
1091 tmp = tmp2;
1093 contents = vcal_curl_read(tmp, label, FALSE, NULL);
1094 g_free(label);
1096 } else {
1097 contents = NULL;
1100 g_free(email);
1101 g_free(tmp);
1103 if (contents == NULL) {
1104 uncertain = TRUE;
1105 att_update_icon(meet, attendee, 2, _("Free/busy retrieval failed"));
1106 continue;
1108 else {
1109 if (!attendee_available(attendee, dtstart, dtend, contents)) {
1110 find_avail = TRUE;
1111 debug_print("not available!\n");
1112 } else {
1113 debug_print("available!\n");
1114 att_update_icon(meet, attendee, 1, _("Available"));
1116 attendee->cached_contents = contents;
1121 if (find_avail) {
1122 res = find_availability((dtstart), (dtend), attlist, for_send, meet);
1123 } else {
1124 res = TRUE;
1125 if (tell_if_ok) {
1126 if (for_send)
1127 alertpanel_notice(_("Everyone is available."));
1128 else if (!uncertain) {
1129 gtk_image_set_from_icon_name
1130 (GTK_IMAGE(meet->total_avail_img),
1131 "dialog-information",
1132 GTK_ICON_SIZE_SMALL_TOOLBAR);
1133 gtk_widget_show(meet->total_avail_img);
1134 gtk_label_set_text(GTK_LABEL(meet->total_avail_msg), _("Everyone is available."));
1135 CLAWS_SET_TIP(meet->total_avail_evtbox, NULL);
1136 } else {
1137 gtk_image_set_from_icon_name
1138 (GTK_IMAGE(meet->total_avail_img),
1139 "dialog-question",
1140 GTK_ICON_SIZE_SMALL_TOOLBAR);
1141 gtk_widget_show(meet->total_avail_img);
1142 gtk_label_set_text(GTK_LABEL(meet->total_avail_msg), _("Everyone is available."));
1143 CLAWS_SET_TIP(meet->total_avail_evtbox, _("Everyone seems available, but some free/busy information failed to be retrieved."));
1148 for (cur = attlist; cur && cur->data; cur = cur->next) {
1149 VCalAttendee *attendee = (VCalAttendee *)cur->data;
1150 g_free(attendee->cached_contents);
1151 attendee->cached_contents = NULL;
1153 gtk_widget_set_sensitive(meet->save_btn, TRUE);
1154 gtk_widget_set_sensitive(meet->avail_btn, avail_btn_can_be_sensitive());
1156 if (gdkwin != NULL)
1157 gdk_window_set_cursor(gdkwin, NULL);
1159 if (!local_only)
1160 meet->attendees = g_slist_remove(meet->attendees, dummy_org);
1161 else
1162 g_slist_free(attlist);
1163 gtk_widget_destroy(dummy_org->address);
1164 g_free(dummy_org);
1166 if (!local_only)
1167 g_free(real_url);
1169 g_free(dtstart);
1170 g_free(dtend);
1171 return res;
1174 static gboolean check_avail_cb(GtkButton *widget, gpointer data)
1176 VCalMeeting *meet = (VCalMeeting *)data;
1177 check_attendees_availability(meet, TRUE, FALSE);
1178 return TRUE;
1181 static gboolean send_meeting_cb(GtkButton *widget, gpointer data)
1183 VCalMeeting *meet = (VCalMeeting *)data;
1184 gchar *uid = NULL;
1185 gchar *organizer = NULL;
1186 gchar *organizer_name = NULL;
1187 gchar *dtstart = NULL;
1188 gchar *dtend = NULL;
1189 gchar *tzid = NULL;
1190 gchar *location = NULL;
1191 gchar *summary = NULL;
1192 gchar *description = NULL;
1193 VCalEvent *event = NULL;
1194 GSList *cur;
1195 PrefsAccount *account = NULL;
1196 gboolean res = FALSE;
1197 gboolean found_att = FALSE;
1198 Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
1199 gboolean redisp = FALSE;
1200 GdkWindow *gdkwin;
1202 if (meet->uid == NULL && meet->visible &&
1203 !check_attendees_availability(meet, FALSE, TRUE)) {
1204 return FALSE;
1207 if (folder) {
1208 MainWindow *mainwin = mainwindow_get_mainwindow();
1209 if (mainwin->summaryview->folder_item == folder->inbox) {
1210 redisp = TRUE;
1211 summary_show(mainwin->summaryview, NULL, FALSE);
1214 gtk_widget_set_sensitive(meet->save_btn, FALSE);
1215 gtk_widget_set_sensitive(meet->avail_btn, FALSE);
1217 gdkwin = gtk_widget_get_window(meet->window);
1218 if (gdkwin != NULL) {
1219 set_watch_cursor(gdkwin);
1220 gdk_window_set_cursor(gdkwin, watch_cursor);
1223 organizer = get_organizer(meet);
1224 account = account_find_from_address(organizer, FALSE);
1226 if(account == NULL) {
1227 debug_print("can't get account from address %s\n", organizer);
1228 g_free(organizer);
1229 return FALSE;
1232 organizer_name = get_organizer_name(meet);
1234 if (meet->uid) {
1235 uid = g_strdup(meet->uid);
1236 } else {
1237 uid = prefs_account_generate_msgid(account);
1240 dtstart = get_date(meet, TRUE);
1241 dtend = get_date(meet, FALSE);
1242 location = get_location(meet);
1243 summary = get_summary(meet);
1244 description = get_description(meet);
1246 event = vcal_manager_new_event(uid, organizer, organizer_name, location, summary, description,
1247 dtstart, dtend, NULL, tzid, NULL, meet->method,
1248 meet->sequence,
1249 ICAL_VEVENT_COMPONENT);
1251 vcal_manager_update_answer(event, organizer, organizer_name,
1252 ICAL_PARTSTAT_ACCEPTED,
1253 ICAL_CUTYPE_INDIVIDUAL);
1255 for (cur = meet->attendees; cur && cur->data; cur = cur->next) {
1256 VCalAttendee *attendee = (VCalAttendee *)cur->data;
1257 gchar *email = gtk_editable_get_chars(GTK_EDITABLE(attendee->address), 0, -1);
1258 gint index = 0;
1259 gchar *orig_email = email;
1260 gchar *name = NULL;
1261 enum icalparameter_cutype cutype = ICAL_CUTYPE_INDIVIDUAL;
1262 enum icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION;
1264 index = gtk_combo_box_get_active(GTK_COMBO_BOX(attendee->cutype));
1266 cutype = ICAL_CUTYPE_INDIVIDUAL + index;
1267 if (attendee->status) {
1268 if(!strcmp(attendee->status, "accepted"))
1269 status = ICAL_PARTSTAT_ACCEPTED;
1270 if(!strcmp(attendee->status, "tentatively accepted"))
1271 status = ICAL_PARTSTAT_TENTATIVE;
1272 if(!strcmp(attendee->status, "declined"))
1273 status = ICAL_PARTSTAT_DECLINED;
1274 g_free(attendee->status);
1276 if (strlen(email)) {
1277 if (strstr(email, " <")) {
1278 name = email;
1279 email = strstr(email," <") + 2;
1280 *(strstr(name," <")) = '\0';
1281 if (strstr(email, ">"))
1282 *(strstr(email, ">")) = '\0';
1285 vcal_manager_update_answer(event, email, name,
1286 status, cutype);
1288 found_att = strcmp(email, organizer);
1290 g_free(orig_email);
1293 if (found_att)
1294 res = vcal_manager_request(account, event);
1295 else
1296 res = TRUE;
1297 g_free(uid);
1298 g_free(organizer);
1299 g_free(organizer_name);
1300 g_free(dtstart);
1301 g_free(dtend);
1302 g_free(description);
1303 g_free(location);
1304 g_free(summary);
1305 vcal_manager_free_event(event);
1307 gtk_widget_set_sensitive(meet->save_btn, TRUE);
1308 gtk_widget_set_sensitive(meet->avail_btn, avail_btn_can_be_sensitive());
1309 if (gdkwin != NULL)
1310 gdk_window_set_cursor(gdkwin, NULL);
1312 if (res) {
1313 vcal_destroy(meet);
1314 } else {
1315 alertpanel_error(_("Could not send the meeting invitation.\n"
1316 "Check the recipients."));
1319 if (folder)
1320 folder_item_scan(folder->inbox);
1322 if (folder && redisp) {
1323 MainWindow *mainwin = mainwindow_get_mainwindow();
1324 summary_show(mainwin->summaryview, folder->inbox, FALSE);
1327 return res;
1330 static VCalMeeting *vcal_meeting_create_real(VCalEvent *event, gboolean visible)
1332 VCalMeeting *meet = g_new0(VCalMeeting, 1);
1333 GtkTextBuffer *buffer = NULL;
1334 GtkWidget *date_hbox, *date_vbox, *save_hbox, *label, *hbox;
1335 gchar *s = NULL;
1336 int i = 0, num = 0;
1337 GtkWidget *scrolledwin;
1338 GList *accounts;
1339 #ifdef GENERIC_UMPC
1340 GtkWidget *notebook;
1341 GtkWidget *maemo_vbox0;
1342 #endif
1344 meet->visible = visible;
1346 meet->window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "vcal_meeting_gtk");
1347 #ifndef GENERIC_UMPC
1348 meet->table = gtk_grid_new();
1349 #else
1350 meet->table1 = gtk_grid_new();
1351 meet->table2 = gtk_grid_new();
1352 #endif
1353 meet->who = gtk_combo_box_text_new();
1355 meet->start_c = gtk_calendar_new();
1356 meet->end_c = gtk_calendar_new();
1358 meet->avail_evtbox = gtk_event_box_new();
1359 meet->avail_img = gtk_image_new_from_icon_name
1360 ("dialog-warning", GTK_ICON_SIZE_SMALL_TOOLBAR);
1362 meet->start_time = gtkut_time_select_combo_new();
1364 meet->end_time = gtkut_time_select_combo_new();
1366 meet->location = gtk_entry_new();
1367 meet->summary = gtk_entry_new();
1368 meet->description = gtk_text_view_new();
1369 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(meet->description));
1370 gtk_text_view_set_editable(GTK_TEXT_VIEW(meet->description), TRUE);
1371 gtk_text_buffer_add_selection_clipboard(buffer, gtk_clipboard_get(GDK_SELECTION_PRIMARY));
1373 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
1374 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
1375 GTK_POLICY_AUTOMATIC,
1376 GTK_POLICY_AUTOMATIC);
1377 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
1378 GTK_SHADOW_IN);
1379 gtk_container_add(GTK_CONTAINER(scrolledwin), meet->description);
1381 if (event) {
1382 meet->uid = g_strdup(event->uid);
1383 meet->sequence = event->sequence + 1;
1384 meet->method = (event->method == ICAL_METHOD_CANCEL ?
1385 ICAL_METHOD_CANCEL:ICAL_METHOD_REQUEST);
1387 gtk_entry_set_text(GTK_ENTRY(meet->location), event->location);
1388 gtk_entry_set_text(GTK_ENTRY(meet->summary), event->summary);
1389 gtk_text_buffer_set_text(buffer, event->description, -1);
1390 } else
1391 meet->method = ICAL_METHOD_REQUEST;
1393 meet->save_btn = gtk_button_new_with_label(_("Save & Send"));
1394 meet->avail_btn = gtk_button_new_with_label(_("Check availability"));
1396 meet->total_avail_evtbox = gtk_event_box_new();
1397 meet->total_avail_img = gtk_image_new_from_icon_name
1398 ("dialog-warning", GTK_ICON_SIZE_SMALL_TOOLBAR);
1399 meet->total_avail_msg = gtk_label_new("");
1401 gtk_widget_set_size_request(meet->total_avail_evtbox, 18, 16);
1402 gtk_event_box_set_visible_window(GTK_EVENT_BOX(meet->total_avail_evtbox), FALSE);
1403 gtk_container_add (GTK_CONTAINER(meet->total_avail_evtbox), meet->total_avail_img);
1405 g_signal_connect(G_OBJECT(meet->save_btn), "clicked",
1406 G_CALLBACK(send_meeting_cb), meet);
1408 g_signal_connect(G_OBJECT(meet->avail_btn), "clicked",
1409 G_CALLBACK(check_avail_cb), meet);
1411 g_signal_connect(G_OBJECT(meet->window), "destroy",
1412 G_CALLBACK(destroy_meeting_cb), meet);
1413 g_signal_connect(G_OBJECT(meet->window), "key_press_event",
1414 G_CALLBACK(meeting_key_pressed), meet);
1417 gtk_widget_set_size_request(meet->description, -1, 100);
1418 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(meet->description), GTK_WRAP_WORD);
1420 if (!event || (event && !event->dtstart && !event->dtend)) {
1421 time_t t = time (NULL)+ 3600;
1422 struct tm buft1, buft2;
1423 struct tm *lt = localtime_r (&t, &buft1);
1424 mktime(lt);
1425 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c),
1426 lt->tm_mday);
1427 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
1428 lt->tm_mon, lt->tm_year + 1900);
1430 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->start_time), lt->tm_hour, 0);
1432 t += 3600;
1433 lt = localtime_r(&t, &buft2);
1435 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1436 lt->tm_mday);
1437 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1438 lt->tm_mon, lt->tm_year + 1900);
1440 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->end_time), lt->tm_hour, 0);
1441 } else {
1442 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c),
1443 get_dtdate(event->dtstart, DAY));
1444 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1445 get_dtdate(event->dtend, DAY));
1447 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
1448 get_dtdate(event->dtstart, MONTH)-1,
1449 get_dtdate(event->dtstart, YEAR));
1450 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1451 get_dtdate(event->dtend, MONTH)-1,
1452 get_dtdate(event->dtend, YEAR));
1454 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->start_time),
1455 get_dtdate(event->dtstart, HOUR),
1456 get_dtdate(event->dtstart, MINUTE));
1458 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->end_time),
1459 get_dtdate(event->dtend, HOUR),
1460 get_dtdate(event->dtend, MINUTE));
1463 g_signal_connect(G_OBJECT(meet->start_c), "day-selected",
1464 G_CALLBACK(meeting_start_changed), meet);
1465 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(meet->start_time))),
1466 "changed",
1467 G_CALLBACK(meeting_start_changed),
1468 meet);
1470 g_signal_connect(G_OBJECT(meet->end_c), "day-selected",
1471 G_CALLBACK(meeting_end_changed), meet);
1472 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(meet->end_time))),
1473 "changed",
1474 G_CALLBACK(meeting_end_changed),
1475 meet);
1477 #ifndef GENERIC_UMPC
1478 gtk_widget_set_size_request(meet->start_time, 80, -1);
1479 gtk_widget_set_size_request(meet->end_time, 80, -1);
1480 #else
1481 gtk_widget_set_size_request(meet->start_time, 120, -1);
1482 gtk_widget_set_size_request(meet->end_time, 120, -1);
1483 #endif
1485 date_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1486 date_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
1487 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1488 label = gtk_label_new(g_strconcat("<b>",_("Starts at:"),"</b> ",NULL));
1489 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
1490 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1492 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1493 gtk_box_pack_start(GTK_BOX(hbox), meet->start_time, FALSE, FALSE, 0);
1494 label = gtk_label_new(g_strconcat("<b> ",_("on:"),"</b>",NULL));
1495 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
1496 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1497 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1498 gtk_box_pack_start(GTK_BOX(date_vbox), hbox, FALSE, FALSE, 0);
1499 gtk_box_pack_start(GTK_BOX(date_vbox), meet->start_c, FALSE, FALSE, 0);
1500 gtk_box_pack_start(GTK_BOX(date_hbox), date_vbox, FALSE, FALSE, 0);
1502 #ifndef GENERIC_UMPC
1503 label = gtk_label_new(" ");
1504 #else
1505 label = gtk_label_new("");
1506 #endif
1507 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
1508 gtk_box_pack_start(GTK_BOX(date_hbox), label, TRUE, TRUE, 0);
1510 date_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
1511 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1512 label = gtk_label_new(g_strconcat("<b>",_("Ends at:"),"</b> ", NULL));
1513 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
1514 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1516 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1517 gtk_box_pack_start(GTK_BOX(hbox), meet->end_time, FALSE, FALSE, 0);
1518 label = gtk_label_new(g_strconcat("<b> ",_("on:"),"</b>",NULL));
1519 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
1520 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1521 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1522 gtk_box_pack_start(GTK_BOX(date_vbox), hbox, FALSE, FALSE, 0);
1523 gtk_box_pack_start(GTK_BOX(date_vbox), meet->end_c, FALSE, FALSE, 0);
1524 gtk_box_pack_start(GTK_BOX(date_hbox), date_vbox, FALSE, FALSE, 0);
1526 meet->attendees_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
1527 gtk_widget_show_all(meet->attendees_vbox);
1528 if (!event) {
1529 attendee_add(meet, NULL, NULL, NULL, NULL, TRUE);
1530 } else {
1531 gboolean firstadd = TRUE;
1532 GSList *list = vcal_manager_get_answers_emails(event);
1533 while (list && list->data) {
1534 gchar *address = (gchar *)list->data;
1535 gchar *name = vcal_manager_get_attendee_name(event, address);
1536 gchar *answer = vcal_manager_get_reply_text_for_attendee(event, address);
1537 gchar *type = vcal_manager_get_cutype_text_for_attendee(event, address);
1538 if (strcmp(event->organizer, address)) {
1539 attendee_add(meet, address, name, answer, type, firstadd);
1540 firstadd = FALSE;
1542 g_free(name);
1543 g_free(answer);
1544 g_free(type);
1545 list = list->next;
1548 if (firstadd == TRUE)
1549 attendee_add(meet, NULL, NULL, NULL, NULL, TRUE);
1552 if (!event) {
1553 gtk_window_set_title(GTK_WINDOW(meet->window), _("New meeting"));
1554 } else {
1555 gchar *title = g_strdup_printf(_("%s - Edit meeting"),
1556 event->summary);
1557 gtk_window_set_title(GTK_WINDOW(meet->window), title);
1558 g_free(title);
1560 address_completion_start(meet->window);
1562 accounts = account_get_list();
1563 g_return_val_if_fail(accounts != NULL, NULL);
1565 for (i = 0; accounts != NULL; accounts = accounts->next) {
1566 PrefsAccount *ac = (PrefsAccount *)accounts->data;
1568 if (ac->protocol == A_NNTP) {
1569 continue;
1571 if (!event && ac == account_get_cur_account()) {
1572 num = i;
1574 else if (event && !strcmp(ac->address, event->organizer))
1575 num = i;
1577 meet->avail_accounts = g_slist_append(meet->avail_accounts, ac);
1579 if (ac->name)
1580 s = g_strdup_printf("%s: %s <%s>",
1581 ac->account_name,
1582 ac->name, ac->address);
1583 else
1584 s = g_strdup_printf("%s: %s",
1585 ac->account_name, ac->address);
1587 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(meet->who), s);
1588 g_free(s);
1589 i++;
1591 gtk_combo_box_set_active(GTK_COMBO_BOX(meet->who), num);
1593 save_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1594 gtk_box_pack_start(GTK_BOX(save_hbox), meet->save_btn, FALSE, FALSE, 0);
1595 gtk_box_pack_start(GTK_BOX(save_hbox), meet->avail_btn, FALSE, FALSE, 0);
1596 gtk_box_pack_start(GTK_BOX(save_hbox), meet->total_avail_evtbox, FALSE, FALSE, 0);
1597 gtk_box_pack_start(GTK_BOX(save_hbox), meet->total_avail_msg, FALSE, FALSE, 0);
1599 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1600 gtk_box_pack_start(GTK_BOX(hbox), meet->avail_evtbox, FALSE, FALSE, 0);
1601 gtk_box_pack_start(GTK_BOX(hbox), meet->who, TRUE, TRUE, 0);
1603 gtk_widget_set_size_request(meet->avail_evtbox, 18, 16);
1604 gtk_event_box_set_visible_window(GTK_EVENT_BOX(meet->avail_evtbox), FALSE);
1605 gtk_container_add (GTK_CONTAINER(meet->avail_evtbox), meet->avail_img);
1607 #ifndef GENERIC_UMPC
1608 TABLE_ADD_LINE(_("Organizer:"), hbox, FALSE);
1609 TABLE_ADD_LINE(_("Summary:"), meet->summary, TRUE);
1610 TABLE_ADD_LINE(_("Time:"), date_hbox, TRUE);
1611 TABLE_ADD_LINE(_("Location:"), meet->location, TRUE);
1612 TABLE_ADD_LINE(_("Description:"), scrolledwin, TRUE);
1613 TABLE_ADD_LINE(_("Attendees:"), meet->attendees_vbox, FALSE);
1614 TABLE_ADD_LINE("", save_hbox, TRUE);
1616 gtk_widget_set_size_request(meet->window, -1, -1);
1617 gtk_container_add(GTK_CONTAINER(meet->window), meet->table);
1618 #else
1619 TABLE_ADD_LINE(_("Organizer:"), hbox, FALSE, TRUE);
1620 TABLE_ADD_LINE(_("Summary:"), meet->summary, TRUE, TRUE);
1621 TABLE_ADD_LINE(_("Location:"), meet->location, FALSE, TRUE);
1622 TABLE_ADD_LINE(_("Description:"), scrolledwin, TRUE, TRUE);
1623 TABLE_ADD_LINE(_("Attendees:"), meet->attendees_vbox, FALSE, TRUE);
1624 TABLE_ADD_LINE("", date_hbox, TRUE, FALSE);
1626 notebook = gtk_notebook_new ();
1627 gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE);
1628 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
1629 meet->table1,
1630 gtk_label_new_with_mnemonic(_("Event:")));
1632 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
1633 meet->table2,
1634 gtk_label_new_with_mnemonic(_("Time:")));
1635 gtk_widget_show (notebook);
1637 maemo_vbox0 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3);
1638 gtk_box_pack_start(GTK_BOX(maemo_vbox0), notebook, TRUE, TRUE, 0);
1639 gtk_box_pack_start(GTK_BOX(maemo_vbox0), save_hbox, FALSE, FALSE, 0);
1641 gtk_widget_set_size_request(meet->window, -1, -1);
1642 gtk_container_add (GTK_CONTAINER (meet->window), maemo_vbox0);
1644 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(meet->window));
1645 #endif
1646 if (visible) {
1647 GSList *cur;
1648 gtk_widget_show_all(meet->window);
1649 for (cur = meet->attendees; cur; cur = cur->next) {
1650 gtk_widget_hide(((VCalAttendee *)cur->data)->avail_img);
1652 gtk_widget_hide(meet->avail_img);
1653 gtk_widget_hide(meet->total_avail_img);
1654 gtk_widget_set_sensitive(meet->avail_btn, avail_btn_can_be_sensitive());
1657 return meet;
1660 VCalMeeting *vcal_meeting_create(VCalEvent *event)
1662 return vcal_meeting_create_real(event, TRUE);
1665 VCalMeeting *vcal_meeting_create_with_start(VCalEvent *event, struct tm *sdate)
1667 VCalMeeting *meet = vcal_meeting_create(event);
1669 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c),
1670 sdate->tm_mday);
1671 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1672 sdate->tm_mday);
1674 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
1675 sdate->tm_mon, sdate->tm_year+1900);
1676 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1677 sdate->tm_mon, sdate->tm_year+1900);
1679 if (sdate->tm_hour != 0) {
1680 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->start_time), sdate->tm_hour, 0);
1682 if (sdate->tm_hour < 23) {
1683 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->end_time), sdate->tm_hour+1, 0);
1684 } else {
1685 struct tm tm_tomorrow;
1687 tm_tomorrow.tm_mday = sdate->tm_mday;
1688 tm_tomorrow.tm_mon = sdate->tm_mon;
1689 tm_tomorrow.tm_wday = sdate->tm_wday;
1690 tm_tomorrow.tm_year = sdate->tm_year+1900;
1691 tm_tomorrow.tm_hour = sdate->tm_hour;
1692 orage_move_day(&tm_tomorrow, +1);
1693 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1694 tm_tomorrow.tm_mday);
1695 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1696 tm_tomorrow.tm_mon, tm_tomorrow.tm_year);
1698 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->end_time), 0, 0);
1701 return meet;
1704 VCalMeeting *vcal_meeting_create_hidden(VCalEvent *event)
1706 return vcal_meeting_create_real(event, FALSE);
1709 gboolean vcal_meeting_send(VCalMeeting *meet)
1711 return send_meeting_cb(NULL, meet);
1714 gboolean vcal_meeting_alert_check(gpointer data)
1716 GSList *events = NULL, *cur = NULL;
1718 if (!vcalprefs.alert_enable)
1719 return TRUE;
1721 events = vcal_folder_get_waiting_events();
1723 for (cur = events; cur; cur = cur->next) {
1724 VCalEvent *event = (VCalEvent *)cur->data;
1725 time_t start, end, current;
1726 gboolean warn = FALSE;
1728 tzset();
1730 start = icaltime_as_timet(icaltime_from_string(event->dtstart));
1731 end = icaltime_as_timet(icaltime_from_string(event->dtend));
1732 current = time(NULL);
1734 if (start - current <= (vcalprefs.alert_delay*60)
1735 && start - current + 60 > (vcalprefs.alert_delay*60)) {
1736 warn = TRUE;
1737 } else if (event->postponed - current <= (vcalprefs.alert_delay*60)
1738 && event->postponed - current + 60 > (vcalprefs.alert_delay*60)) {
1739 warn = TRUE;
1741 if (warn) {
1742 time_t tmpt = icaltime_as_timet((icaltime_from_string(event->dtstart)));
1743 gchar *estart = NULL;
1744 AlertValue aval;
1745 int length = (end - start) / 60;
1746 gchar *duration = NULL, *hours = NULL, *minutes = NULL;
1747 gchar *message = NULL;
1748 gchar *title = NULL;
1749 gchar *label = NULL;
1750 int postpone_min = 0;
1752 tzset();
1754 estart = g_strdup(ctime(&tmpt));
1756 if (length >= 60)
1757 hours = g_strdup_printf(ngettext("%d hour", "%d hours",
1758 (length/60) > 1 ? 2 : 1), length/60);
1759 if (length%60)
1760 minutes = g_strdup_printf(ngettext("%d minute", "%d minutes",
1761 length%60), length%60);
1763 duration = g_strdup_printf("%s%s%s",
1764 hours?hours:"",
1765 hours && minutes ? " ":"",
1766 minutes?minutes:"");
1768 g_free(hours);
1769 g_free(minutes);
1771 title = g_strdup_printf(_("Upcoming event: %s"), event->summary);
1772 message = g_strdup_printf(_("You have a meeting or event soon.\n"
1773 "It starts at %s and ends %s later.\n"
1774 "Location: %s\n"
1775 "More information:\n\n"
1776 "%s"),
1777 estart,
1778 duration,
1779 event->location?event->location:"",
1780 event->description);
1782 g_free(duration);
1783 g_free(estart);
1785 postpone_min = (vcalprefs.alert_delay/2 > 15) ? 15: (vcalprefs.alert_delay/2);
1786 if (postpone_min == 0)
1787 postpone_min = 1;
1789 label = g_strdup_printf(ngettext("Remind me in %d minute", "Remind me in %d minutes",
1790 postpone_min > 1 ? 2:1),
1791 postpone_min);
1792 aval = alertpanel_full(title, message,
1793 NULL, label, NULL, _("_OK"), NULL, NULL,
1794 ALERTFOCUS_FIRST, FALSE, NULL, ALERT_NOTICE);
1795 g_free(label);
1797 g_free(title);
1798 g_free(message);
1800 if (aval == G_ALERTDEFAULT) {
1801 if (event->postponed == 0)
1802 event->postponed = start + (postpone_min*60);
1803 else
1804 event->postponed += (postpone_min*60);
1805 } else {
1806 event->postponed = (time_t)0;
1808 vcal_manager_save_event(event, FALSE);
1811 vcal_manager_free_event((VCalEvent *)cur->data);
1814 g_slist_free(events);
1816 return TRUE;
1819 void multisync_export(void)
1821 GSList *list = NULL;
1822 gchar *path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1823 "vcalendar", G_DIR_SEPARATOR_S,
1824 "multisync", NULL);
1825 GSList *files = NULL;
1826 GSList *cur = NULL;
1827 gchar *backup_file;
1828 gint i = 0;
1829 icalcomponent *calendar = NULL;
1830 FILE *fp;
1832 if (is_dir_exist(path) && remove_dir_recursive(path) < 0) {
1833 g_free(path);
1834 return;
1836 if (make_dir(path) != 0) {
1837 g_free(path);
1838 return;
1841 list = vcal_folder_get_waiting_events();
1842 for (cur = list; cur; cur = cur->next) {
1843 gchar *tmp = NULL;
1844 VCalEvent *event = (VCalEvent *)cur->data;
1845 gchar *file = g_strdup_printf("multisync%"CM_TIME_FORMAT"-%d",
1846 time(NULL), i);
1848 i++;
1850 calendar =
1851 icalcomponent_vanew(
1852 ICAL_VCALENDAR_COMPONENT,
1853 icalproperty_new_version("2.0"),
1854 icalproperty_new_prodid(
1855 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
1856 icalproperty_new_calscale("GREGORIAN"),
1857 (void*)0
1859 vcal_manager_event_dump(event, FALSE, FALSE, calendar, FALSE);
1860 tmp = g_strconcat(path, G_DIR_SEPARATOR_S, file, NULL);
1861 str_write_to_file(icalcomponent_as_ical_string(calendar), tmp, TRUE);
1862 g_free(tmp);
1863 files = g_slist_append(files, file);
1864 vcal_manager_free_event(event);
1865 icalcomponent_free(calendar);
1868 g_slist_free(list);
1870 backup_file = g_strconcat(path, G_DIR_SEPARATOR_S, "backup_entries", NULL);
1871 fp = claws_fopen(backup_file, "wb");
1872 if (fp) {
1873 for (cur = files; cur; cur = cur->next) {
1874 gchar * file = (char *)cur->data;
1875 if (fprintf(fp, "1 1 %s\n", file) < 0)
1876 FILE_OP_ERROR(file, "fprintf");
1877 g_free(file);
1879 if (claws_safe_fclose(fp) == EOF)
1880 FILE_OP_ERROR(backup_file, "claws_fclose");
1881 } else {
1882 FILE_OP_ERROR(backup_file, "claws_fopen");
1884 g_free(backup_file);
1885 g_free(path);
1886 g_slist_free(files);
1889 gboolean vcal_meeting_export_calendar(const gchar *path,
1890 const gchar *user, const gchar *pass,
1891 gboolean automatic)
1893 GSList *list = vcal_folder_get_waiting_events();
1894 GSList *subs = NULL;
1895 GSList *cur;
1896 icalcomponent *calendar = NULL;
1897 gchar *file;
1898 gchar *tmpfile = get_tmp_file();
1899 gchar *internal_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1900 "vcalendar", G_DIR_SEPARATOR_S,
1901 "internal.ics", NULL);
1903 gboolean res = TRUE;
1904 long filesize = 0;
1906 multisync_export();
1908 if (vcalprefs.export_subs && vcalprefs.export_enable)
1909 subs = vcal_folder_get_webcal_events();
1911 if (g_slist_length(list) == 0 && g_slist_length(subs) == 0) {
1912 g_slist_free(list);
1913 g_slist_free(subs);
1914 if (!automatic) {
1915 alertpanel_full(_("Empty calendar"),
1916 _("There is nothing to export."),
1917 NULL, _("_OK"), NULL, NULL, NULL, NULL,
1918 ALERTFOCUS_FIRST, FALSE, NULL, ALERT_NOTICE);
1919 g_free(tmpfile);
1920 g_free(internal_file);
1921 return FALSE;
1922 } else {
1923 str_write_to_file("", tmpfile, TRUE);
1924 goto putfile;
1928 calendar =
1929 icalcomponent_vanew(
1930 ICAL_VCALENDAR_COMPONENT,
1931 icalproperty_new_version("2.0"),
1932 icalproperty_new_prodid(
1933 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
1934 icalproperty_new_calscale("GREGORIAN"),
1935 (void*)0
1938 for (cur = list; cur; cur = cur->next) {
1939 VCalEvent *event = (VCalEvent *)cur->data;
1940 vcal_manager_event_dump(event, FALSE, FALSE, calendar, FALSE);
1941 vcal_manager_free_event(event);
1944 if (str_write_to_file(icalcomponent_as_ical_string(calendar), internal_file, TRUE) < 0) {
1945 g_warning("can't export internal cal");
1948 for (cur = subs; cur; cur = cur->next) {
1949 /* Not to be freed */
1950 icalcomponent *event = (icalcomponent *)cur->data;
1951 vcal_manager_icalevent_dump(event, NULL, calendar);
1954 if (vcalprefs.export_enable || path == NULL) {
1955 if (str_write_to_file(icalcomponent_as_ical_string(calendar), tmpfile, TRUE) < 0) {
1956 alertpanel_error(_("Could not export the calendar."));
1957 g_free(tmpfile);
1958 icalcomponent_free(calendar);
1959 g_slist_free(list);
1960 g_slist_free(subs);
1961 return FALSE;
1963 filesize = strlen(icalcomponent_as_ical_string(calendar));
1966 icalcomponent_free(calendar);
1968 putfile:
1969 g_free(internal_file);
1970 g_slist_free(list);
1971 g_slist_free(subs);
1973 if (automatic && (!path || strlen(path) == 0 || !vcalprefs.export_enable)) {
1974 g_free(tmpfile);
1975 return TRUE;
1978 if (!path && !automatic)
1979 file = filesel_select_file_save(_("Export calendar to ICS"), NULL);
1980 else
1981 file = g_strdup(path);
1983 if (file
1984 && strncmp(file, "http://", 7)
1985 && strncmp(file, "https://", 8)
1986 && strncmp(file, "webcal://", 9)
1987 && strncmp(file, "webcals://", 10)
1988 && strncmp(file, "ftp://", 6)) {
1989 gchar *afile;
1990 if (file[0] != G_DIR_SEPARATOR)
1991 afile = g_strdup_printf("%s%s%s", get_home_dir(),
1992 G_DIR_SEPARATOR_S, file);
1993 else
1994 afile = g_strdup(file);
1995 if (move_file(tmpfile, afile, TRUE) != 0) {
1996 log_error(LOG_PROTOCOL, _("Couldn't export calendar to '%s'\n"),
1997 afile);
1998 res = FALSE;
2000 g_free(afile);
2001 } else if (file) {
2002 FILE *fp = claws_fopen(tmpfile, "rb");
2003 if (!strncmp(file, "webcal", 6)) {
2004 gchar *tmp = g_strdup_printf("http%s", file+6);
2005 g_free(file);
2006 file = tmp;
2008 if (fp) {
2009 res = vcal_curl_put(file, fp, filesize, user, (pass != NULL ? pass : ""));
2010 claws_fclose(fp);
2013 g_free(tmpfile);
2014 if (file)
2015 g_free(file);
2016 return res;
2019 gboolean vcal_meeting_export_freebusy(const gchar *path, const gchar *user,
2020 const gchar *pass)
2022 GSList *list = vcal_folder_get_waiting_events();
2023 GSList *cur;
2024 icalcomponent *calendar = NULL, *timezone = NULL, *tzc = NULL, *vfreebusy = NULL;
2025 gchar *file;
2026 gchar *tmpfile = get_tmp_file();
2027 gchar *internal_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2028 "vcalendar", G_DIR_SEPARATOR_S,
2029 "internal.ifb", NULL);
2030 time_t whole_start = time(NULL);
2031 time_t whole_end = whole_start + (60*60*24*365);
2032 gboolean res = TRUE;
2033 struct icaltimetype itt_start, itt_end;
2034 long filesize = 0;
2036 multisync_export();
2038 calendar =
2039 icalcomponent_vanew(
2040 ICAL_VCALENDAR_COMPONENT,
2041 icalproperty_new_version("2.0"),
2042 icalproperty_new_prodid(
2043 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
2044 icalproperty_new_calscale("GREGORIAN"),
2045 (void*)0
2048 timezone = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
2050 icalcomponent_add_property(timezone,
2051 icalproperty_new_tzid("UTC"));
2053 tzc = icalcomponent_new(ICAL_XSTANDARD_COMPONENT);
2054 icalcomponent_add_property(tzc,
2055 icalproperty_new_dtstart(
2056 icaltime_from_string("19700101T000000")));
2057 icalcomponent_add_property(tzc,
2058 icalproperty_new_tzoffsetfrom(0.0));
2059 icalcomponent_add_property(tzc,
2060 icalproperty_new_tzoffsetto(0.0));
2061 icalcomponent_add_property(tzc,
2062 icalproperty_new_tzname("Greenwich meridian time"));
2064 icalcomponent_add_component(timezone, tzc);
2066 icalcomponent_add_component(calendar, timezone);
2068 itt_start = icaltime_from_timet_with_zone(whole_start, FALSE, NULL);
2069 itt_end = icaltime_from_timet_with_zone(whole_end, FALSE, NULL);
2070 itt_start.second = itt_start.minute = itt_start.hour = 0;
2071 itt_end.second = 59; itt_end.minute = 59; itt_end.hour = 23;
2074 vfreebusy =
2075 icalcomponent_vanew(
2076 ICAL_VFREEBUSY_COMPONENT,
2077 icalproperty_vanew_dtstart(itt_start, 0),
2078 icalproperty_vanew_dtend(itt_end, 0),
2079 (void*)0
2082 debug_print("DTSTART:%s\nDTEND:%s\n",
2083 icaltime_as_ical_string(itt_start),
2084 icaltime_as_ical_string(itt_end));
2086 for (cur = list; cur; cur = cur->next) {
2087 VCalEvent *event = (VCalEvent *)cur->data;
2088 icalproperty *prop;
2089 struct icalperiodtype ipt;
2091 ipt.start = icaltime_from_string(event->dtstart);
2092 ipt.end = icaltime_from_string(event->dtend);
2093 ipt.duration = icaltime_subtract(ipt.end, ipt.start);
2094 if (icaltime_as_timet(ipt.start) <= icaltime_as_timet(itt_end)
2095 && icaltime_as_timet(ipt.end) >= icaltime_as_timet(itt_start)) {
2096 prop = icalproperty_new_freebusy(ipt);
2097 icalcomponent_add_property(vfreebusy, prop);
2099 vcal_manager_free_event(event);
2102 icalcomponent_add_component(calendar, vfreebusy);
2104 if (str_write_to_file(icalcomponent_as_ical_string(calendar), internal_file, TRUE) < 0) {
2105 g_warning("can't export freebusy");
2108 g_free(internal_file);
2110 if (vcalprefs.export_freebusy_enable) {
2111 if (str_write_to_file(icalcomponent_as_ical_string(calendar), tmpfile, TRUE) < 0) {
2112 alertpanel_error(_("Could not export the freebusy info."));
2113 g_free(tmpfile);
2114 icalcomponent_free(calendar);
2115 g_slist_free(list);
2116 return FALSE;
2118 filesize = strlen(icalcomponent_as_ical_string(calendar));
2121 icalcomponent_free(calendar);
2122 g_slist_free(list);
2124 if ((!path || strlen(path) == 0 || !vcalprefs.export_freebusy_enable)) {
2125 g_free(tmpfile);
2126 return TRUE;
2129 file = g_strdup(path);
2131 if (file
2132 && strncmp(file, "http://", 7)
2133 && strncmp(file, "https://", 8)
2134 && strncmp(file, "webcal://", 9)
2135 && strncmp(file, "webcals://", 10)
2136 && strncmp(file, "ftp://", 6)) {
2137 gchar *afile;
2138 if (file[0] != G_DIR_SEPARATOR)
2139 afile = g_strdup_printf("%s%s%s", get_home_dir(),
2140 G_DIR_SEPARATOR_S, file);
2141 else
2142 afile = g_strdup(file);
2143 if (move_file(tmpfile, file, TRUE) != 0) {
2144 log_error(LOG_PROTOCOL, _("Couldn't export free/busy to '%s'\n"),
2145 afile);
2146 res = FALSE;
2148 g_free(afile);
2149 } else if (file) {
2150 FILE *fp = claws_fopen(tmpfile, "rb");
2151 if (!strncmp(file, "webcal", 6)) {
2152 gchar *tmp = g_strdup_printf("http%s", file+6);
2153 g_free(file);
2154 file = tmp;
2156 if (fp) {
2157 res = vcal_curl_put(file, fp, filesize, user, (pass != NULL ? pass : ""));
2158 claws_fclose(fp);
2161 g_free(tmpfile);
2162 if (file)
2163 g_free(file);
2164 return res;