2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 Colin Leroy <colin@colino.net> and
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.
23 #include "claws-features.h"
28 #include <glib/gi18n.h>
37 #include <gdk/gdkkeysyms.h>
38 #include <curl/curl.h>
39 #include <curl/curlver.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"
52 #include "alertpanel.h"
53 #include "addr_compl.h"
72 GtkWidget *avail_evtbox;
75 GtkWidget *start_time;
80 GtkWidget *description;
82 GtkWidget *attendees_vbox;
85 GSList *avail_accounts;
86 GtkWidget *total_avail_evtbox;
87 GtkWidget *total_avail_img;
88 GtkWidget *total_avail_msg;
89 PrefsAccount *account;
91 #if !GTK_CHECK_VERSION(2,12,0)
96 struct _VCalAttendee {
98 GtkWidget *remove_btn;
104 GtkWidget *avail_evtbox;
105 GtkWidget *avail_img;
106 gchar *cached_contents;
110 static GdkCursor *watch_cursor = NULL;
112 VCalAttendee *attendee_add(VCalMeeting *meet, gchar *address, gchar *name, gchar *partstat, gchar *cutype, gboolean first);
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; \
122 spacer = gtk_label_new(""); \
123 gtk_widget_set_size_request(spacer, 18, 16); \
124 s_hbox = gtk_hbox_new(FALSE, 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); \
129 label = gtk_label_new(tmpstr); \
131 gtk_label_set_use_markup (GTK_LABEL (label), TRUE); \
132 gtk_misc_set_alignment (GTK_MISC(label), 1, 0.5); \
133 gtk_table_attach (GTK_TABLE (meet->table), \
134 label, 0, 1, i, i+1, \
135 GTK_FILL, GTK_FILL, 6, 6); \
136 gtk_table_attach (GTK_TABLE (meet->table), \
137 do_space?s_hbox:widget, 1, 2, i, i+1, \
138 GTK_FILL|GTK_EXPAND, GTK_FILL, 6, 6); \
139 if (GTK_IS_LABEL(widget)) { \
140 gtk_label_set_use_markup(GTK_LABEL (widget), TRUE); \
141 gtk_misc_set_alignment (GTK_MISC(widget),0, 0); \
142 gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE); \
146 gtk_table_attach (GTK_TABLE (meet->table), \
147 do_space?s_hbox:widget, 0, 2, i, i+1, \
148 GTK_FILL|GTK_EXPAND, GTK_FILL, 6, 6); \
153 #define TABLE_ADD_LINE(label_text, widget, do_space, intable1) { \
154 gchar *tmpstr = g_strdup_printf("<span weight=\"bold\">%s</span>", \
155 label_text?label_text:""); \
156 GtkWidget *label = NULL; \
157 GtkWidget *spacer = NULL; \
158 GtkWidget *s_hbox = NULL; \
160 spacer = gtk_label_new(""); \
161 gtk_widget_set_size_request(spacer, 18, 16); \
162 s_hbox = gtk_hbox_new(FALSE, 6); \
163 gtk_box_pack_start(GTK_BOX(s_hbox), spacer, FALSE, FALSE, 0); \
164 gtk_box_pack_start(GTK_BOX(s_hbox), widget, TRUE, TRUE, 0); \
167 label = gtk_label_new(tmpstr); \
169 gtk_label_set_use_markup (GTK_LABEL (label), TRUE); \
170 gtk_misc_set_alignment (GTK_MISC(label), 1, 0.5); \
172 gtk_table_attach (GTK_TABLE (meet->table1), \
173 label, 0, 1, i, i+1, \
174 GTK_FILL, GTK_FILL, 1, 1); \
177 gtk_table_attach (GTK_TABLE (meet->table2), \
178 label, 0, 1, i, i+1, \
179 GTK_FILL, GTK_FILL, 1, 1); \
182 gtk_table_attach (GTK_TABLE (meet->table1), \
183 do_space?s_hbox:widget, 1, 2, i, i+1, \
184 GTK_FILL|GTK_EXPAND, GTK_FILL, 1, 1); \
187 gtk_table_attach (GTK_TABLE (meet->table2), \
188 do_space?s_hbox:widget, 1, 2, i, i+1, \
189 GTK_FILL|GTK_EXPAND, GTK_FILL, 1, 1); \
191 if (GTK_IS_LABEL(widget)) { \
192 gtk_label_set_use_markup(GTK_LABEL (widget), TRUE); \
193 gtk_misc_set_alignment (GTK_MISC(widget),0, 0); \
194 gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE); \
199 gtk_table_attach (GTK_TABLE (meet->table1), \
200 do_space?s_hbox:widget, 0, 2, i, i+1, \
201 GTK_FILL|GTK_EXPAND, GTK_FILL, 1, 1); \
204 gtk_table_attach (GTK_TABLE (meet->table2), \
205 do_space?s_hbox:widget, 0, 2, i, i+1, \
206 GTK_FILL|GTK_EXPAND, GTK_FILL, 1, 1); \
220 static gboolean avail_btn_can_be_sensitive(void)
222 if (vcalprefs.freebusy_get_url == NULL
223 || *vcalprefs.freebusy_get_url == '\0')
229 static gint get_dtdate(const gchar *str, gint field)
231 time_t t = icaltime_as_timet((icaltime_from_string(str)));
241 lt = localtime_r(&t, &buft);
247 return lt->tm_mon + 1;
249 return lt->tm_year + 1900;
259 static gboolean add_btn_cb(GtkButton *widget, gpointer data)
261 VCalAttendee *attendee = (VCalAttendee *)data;
262 attendee_add(attendee->meet, NULL, NULL, NULL, NULL, FALSE);
266 static gboolean remove_btn_cb(GtkButton *widget, gpointer data)
268 VCalAttendee *attendee = (VCalAttendee *)data;
269 gtk_container_remove(GTK_CONTAINER(attendee->meet->attendees_vbox), attendee->hbox);
270 attendee->meet->attendees = g_slist_remove(attendee->meet->attendees, attendee);
272 g_free(attendee->status);
277 VCalAttendee *attendee_add(VCalMeeting *meet, gchar *address, gchar *name, gchar *partstat, gchar *cutype, gboolean first)
279 GtkWidget *att_hbox = gtk_hbox_new(FALSE, 6);
280 VCalAttendee *attendee = g_new0(VCalAttendee, 1);
281 #if !(GTK_CHECK_VERSION(2,12,0))
282 GtkTooltips *tips = meet->tips;
285 attendee->address = gtk_entry_new();
286 attendee->cutype = gtk_combo_box_new_text();
287 attendee->avail_evtbox = gtk_event_box_new();
288 attendee->avail_img = gtk_image_new_from_stock
289 (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
291 gtk_widget_show(attendee->address);
292 gtk_widget_show(attendee->cutype);
293 gtk_widget_show(attendee->avail_evtbox);
295 CLAWS_SET_TIP(attendee->address, _("Use <tab> to autocomplete from addressbook"));
296 gtk_widget_set_size_request(attendee->avail_evtbox, 18, 16);
297 gtk_event_box_set_visible_window(GTK_EVENT_BOX(attendee->avail_evtbox), FALSE);
298 gtk_container_add (GTK_CONTAINER(attendee->avail_evtbox), attendee->avail_img);
301 gchar *str = g_strdup_printf("%s%s%s%s",
302 (name && strlen(name))?name:"",
303 (name && strlen(name))?" <":"",
305 (name && strlen(name))?">":"");
306 gtk_entry_set_text(GTK_ENTRY(attendee->address), str);
311 attendee->status = g_strdup(partstat);
313 gtk_combo_box_append_text(GTK_COMBO_BOX(attendee->cutype), _("Individual"));
314 gtk_combo_box_append_text(GTK_COMBO_BOX(attendee->cutype), _("Group"));
315 gtk_combo_box_append_text(GTK_COMBO_BOX(attendee->cutype), _("Resource"));
316 gtk_combo_box_append_text(GTK_COMBO_BOX(attendee->cutype), _("Room"));
318 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 0);
321 if (!strcmp(cutype, "group"))
322 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 1);
323 if (!strcmp(cutype, "resource"))
324 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 2);
325 if (!strcmp(cutype, "room"))
326 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 3);
329 attendee->add_btn = gtk_button_new_with_label(_("Add..."));
330 attendee->remove_btn = gtk_button_new_with_label(_("Remove"));
331 attendee->meet = meet;
332 attendee->hbox = att_hbox;
334 gtk_widget_show(attendee->add_btn);
335 gtk_widget_show(attendee->remove_btn);
336 gtk_widget_show(attendee->hbox);
338 gtk_box_pack_start(GTK_BOX(attendee->hbox), attendee->avail_evtbox, FALSE, FALSE, 0);
339 gtk_widget_set_sensitive(attendee->remove_btn, !first);
340 meet->attendees = g_slist_append(meet->attendees, attendee);
342 g_signal_connect(G_OBJECT(attendee->remove_btn), "clicked",
343 G_CALLBACK(remove_btn_cb), attendee);
344 g_signal_connect(G_OBJECT(attendee->add_btn), "clicked",
345 G_CALLBACK(add_btn_cb), attendee);
347 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->address, FALSE, FALSE, 0);
348 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->cutype, FALSE, FALSE, 0);
349 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->add_btn, FALSE, FALSE, 0);
350 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->remove_btn, FALSE, FALSE, 0);
351 gtk_box_pack_start(GTK_BOX(meet->attendees_vbox), att_hbox, FALSE, FALSE, 0);
352 address_completion_register_entry(GTK_ENTRY(attendee->address), FALSE);
354 gtk_widget_set_size_request(attendee->address, 320, -1);
356 gtk_widget_set_size_request(attendee->address, 220, -1);
361 static gchar *get_organizer(VCalMeeting *meet)
363 int index = gtk_combo_box_get_active(GTK_COMBO_BOX(meet->who));
365 GSList *cur = meet->avail_accounts;
366 while (i < index && cur && cur->data) {
367 debug_print("%d:skipping %s\n",i,((PrefsAccount *)(cur->data))->address);
372 return g_strdup(((PrefsAccount *)(cur->data))->address);
377 static gchar *get_organizer_name(VCalMeeting *meet)
379 int index = gtk_combo_box_get_active(GTK_COMBO_BOX(meet->who));
381 GSList *cur = meet->avail_accounts;
382 while (i < index && cur && cur->data) {
383 debug_print("%d:skipping %s\n",i,((PrefsAccount *)(cur->data))->address);
388 return g_strdup(((PrefsAccount *)(cur->data))->name);
393 static void get_time_from_combo(GtkWidget *combo, int *h, int *m)
401 tmp = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(combo))), 0, -1);
402 parts = g_strsplit(tmp, ":", 2);
403 if (parts[0] && parts[1] && *parts[0] && *parts[1]) {
411 static int get_current_gmt_offset(void)
413 time_t now = time(NULL);
423 gmtime_r(& now, & gmt);
424 localtime_r(& now, & local);
427 return mktime(&local)-mktime(&gmt);
430 static int get_gmt_offset_at_time(time_t then)
441 gmtime_r(& then, & gmt);
442 localtime_r(& then, & local);
445 return mktime(&local)-mktime(&gmt);
448 static gchar *get_date(VCalMeeting *meet, int start)
463 lt = localtime_r(&t, &buft);
465 gtk_calendar_get_date(GTK_CALENDAR(start ? meet->start_c : meet->end_c), &y, &m, &d);
468 lt->tm_year = y - 1900;
474 get_time_from_combo(meet->start_time, <->tm_hour, <->tm_min);
476 get_time_from_combo(meet->end_time, <->tm_hour, <->tm_min);
479 debug_print("%d %d %d, %d:%d\n", lt->tm_mday, lt->tm_mon, lt->tm_year, lt->tm_hour, lt->tm_min);
482 dst_offset = get_current_gmt_offset() - get_gmt_offset_at_time(t);
483 debug_print("DST change offset to apply to time %d\n", dst_offset);
485 debug_print("%s\n", ctime(&t));
486 return g_strdup(icaltime_as_ical_string(icaltime_from_timet(t, FALSE)));
489 static gchar *get_location(VCalMeeting *meet)
491 return gtk_editable_get_chars(GTK_EDITABLE(meet->location),0, -1);
494 static gchar *get_summary(VCalMeeting *meet)
496 return gtk_editable_get_chars(GTK_EDITABLE(meet->summary),0, -1);
499 static gchar *get_description(VCalMeeting *meet)
501 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(meet->description));
502 GtkTextIter start, end;
504 gtk_text_buffer_get_start_iter(buffer, &start);
505 gtk_text_buffer_get_end_iter(buffer, &end);
506 return gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
509 void vcal_meeting_free(VCalMeeting *meet)
511 debug_print("freeing meeting\n");
513 address_completion_end(meet->window);
514 g_slist_free(meet->avail_accounts);
515 g_slist_free(meet->attendees);
519 static void destroy_meeting_cb(GtkWidget *widget, gpointer data)
521 VCalMeeting *meet = (VCalMeeting *)data;
522 vcal_meeting_free(meet);
525 static void vcal_destroy(VCalMeeting *meet)
527 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(meet->description));
528 gtk_text_buffer_remove_selection_clipboard(buffer, gtk_clipboard_get(GDK_SELECTION_PRIMARY));
529 gtk_widget_destroy(meet->window);
532 static gboolean meeting_key_pressed(GtkWidget *widget,
536 VCalMeeting *meet = (VCalMeeting *)data;
538 if (event && event->keyval == GDK_Escape) {
544 static int get_list_item_num(int h, int m)
552 static void meeting_end_changed(GtkWidget *widget, gpointer data);
554 static void meeting_start_changed(GtkWidget *widget, gpointer data)
556 VCalMeeting *meet = (VCalMeeting *)data;
559 time_t start_t, end_t;
563 if (strlen(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(meet->start_time))))) < 5)
567 start_t = time(NULL);
575 localtime_r(&start_t, &start_lt);
576 localtime_r(&end_t, &end_lt);
578 gtk_calendar_get_date(GTK_CALENDAR(meet->start_c), &y, &m, &d);
579 start_lt.tm_mday = d; start_lt.tm_mon = m; start_lt.tm_year = y - 1900;
580 get_time_from_combo(meet->start_time, &start_lt.tm_hour, &start_lt.tm_min);
582 start_t = mktime(&start_lt);
583 debug_print("start %s\n", ctime(&start_t));
585 gtk_calendar_get_date(GTK_CALENDAR(meet->end_c), &y, &m, &d);
586 end_lt.tm_mday = d; end_lt.tm_mon = m; end_lt.tm_year = y - 1900;
588 get_time_from_combo(meet->end_time, &end_lt.tm_hour, &end_lt.tm_min);
589 end_t = mktime(&end_lt);
591 debug_print("end %s\n", ctime(&end_t));
593 if (end_t > start_t) {
597 end_t = start_t + 3600;
603 localtime_r(&end_t, &end_lt);
604 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);
606 g_signal_handlers_block_by_func(gtk_bin_get_child(GTK_BIN(meet->end_time)), meeting_end_changed, meet);
607 g_signal_handlers_block_by_func(meet->end_c, meeting_end_changed, meet);
609 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c), end_lt.tm_mday);
611 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
613 end_lt.tm_year + 1900);
615 num = get_list_item_num(end_lt.tm_hour, end_lt.tm_min);
617 gchar *time_text = g_strdup_printf("%02d:%02d", end_lt.tm_hour, end_lt.tm_min);
618 combobox_select_by_text(GTK_COMBO_BOX(meet->end_time), time_text);
621 gchar *tmp = g_strdup_printf("%02d:%02d",
624 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(meet->end_time))),
628 g_signal_handlers_unblock_by_func(gtk_bin_get_child(GTK_BIN(meet->end_time)), meeting_end_changed, meet);
629 g_signal_handlers_unblock_by_func(meet->end_c, meeting_end_changed, meet);
632 static void meeting_end_changed(GtkWidget *widget, gpointer data)
634 VCalMeeting *meet = (VCalMeeting *)data;
637 time_t start_t, end_t;
641 if (strlen(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(meet->end_time))))) < 5)
643 start_t = time(NULL);
654 localtime_r(&start_t, &start_lt);
655 localtime_r(&end_t, &end_lt);
657 gtk_calendar_get_date(GTK_CALENDAR(meet->start_c), &y, &m, &d);
658 start_lt.tm_mday = d; start_lt.tm_mon = m; start_lt.tm_year = y - 1900;
659 get_time_from_combo(meet->start_time, &start_lt.tm_hour, &start_lt.tm_min);
661 start_t = mktime(&start_lt);
662 debug_print("start %s\n", ctime(&start_t));
664 gtk_calendar_get_date(GTK_CALENDAR(meet->end_c), &y, &m, &d);
665 end_lt.tm_mday = d; end_lt.tm_mon = m; end_lt.tm_year = y - 1900;
666 get_time_from_combo(meet->end_time, &end_lt.tm_hour, &end_lt.tm_min);
668 end_t = mktime(&end_lt);
670 debug_print("end %s\n", ctime(&end_t));
672 if (end_t > start_t) {
676 start_t = end_t - 3600;
684 localtime_r(&start_t, &start_lt);
685 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);
687 g_signal_handlers_block_by_func(gtk_bin_get_child(GTK_BIN(meet->start_time)), meeting_start_changed, meet);
688 g_signal_handlers_block_by_func(meet->start_c, meeting_start_changed, meet);
690 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c), start_lt.tm_mday);
692 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
694 start_lt.tm_year + 1900);
696 num = get_list_item_num(start_lt.tm_hour, start_lt.tm_min);
698 gchar *time_text = g_strdup_printf("%02d:%02d", start_lt.tm_hour, start_lt.tm_min);
699 combobox_select_by_text(GTK_COMBO_BOX(meet->start_time), time_text);
702 gchar *tmp = g_strdup_printf("%02d:%02d",
705 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(meet->start_time))),
710 g_signal_handlers_unblock_by_func(gtk_bin_get_child(GTK_BIN(meet->start_time)), meeting_start_changed, meet);
711 g_signal_handlers_unblock_by_func(meet->start_c, meeting_start_changed, meet);
714 static void att_update_icon(VCalMeeting *meet, VCalAttendee *attendee, gint avail, gchar *text)
716 const gchar *icon = GTK_STOCK_DIALOG_INFO;
717 #if !(GTK_CHECK_VERSION(2,12,0))
718 GtkTooltips *tips = meet->tips;
722 case 0: icon = GTK_STOCK_DIALOG_WARNING; break;
723 case 1: icon = GTK_STOCK_DIALOG_INFO; break;
724 default: icon = GTK_STOCK_DIALOG_QUESTION; break;
726 if (!gtk_entry_get_text(GTK_ENTRY(attendee->address))
727 || strlen(gtk_entry_get_text(GTK_ENTRY(attendee->address)))==0) {
728 if (attendee->avail_img) {
729 gtk_widget_hide(attendee->avail_img);
731 CLAWS_SET_TIP(attendee->avail_evtbox, NULL);
732 } else if (attendee->avail_img) {
733 gtk_image_set_from_stock
734 (GTK_IMAGE(attendee->avail_img),
736 GTK_ICON_SIZE_SMALL_TOOLBAR);
737 gtk_widget_show(attendee->avail_img);
738 CLAWS_SET_TIP(attendee->avail_evtbox, text);
742 gboolean attendee_available(VCalAttendee *attendee, const gchar *dtstart, const gchar *dtend, const gchar *contents)
744 icalcomponent *toplvl, *vfreebusy;
745 icalproperty *busyprop;
746 struct icaltimetype start = icaltime_from_string(dtstart);
747 struct icaltimetype end = icaltime_from_string(dtend);
748 gboolean result = TRUE;
751 if (contents == NULL)
754 toplvl = icalcomponent_new_from_string((gchar *)contents);
759 vfreebusy = icalcomponent_get_first_component(toplvl, ICAL_VFREEBUSY_COMPONENT);
760 while (vfreebusy && icalcomponent_isa(vfreebusy) != ICAL_VFREEBUSY_COMPONENT)
761 vfreebusy = icalcomponent_get_next_component(toplvl, ICAL_VFREEBUSY_COMPONENT);
764 busyprop = icalcomponent_get_first_property(vfreebusy, ICAL_FREEBUSY_PROPERTY);
766 struct icalperiodtype ipt = icalproperty_get_freebusy(busyprop);
768 if ( icaltime_compare(start, ipt.end) >= 0 || icaltime_compare(end, ipt.start) <= 0 ) {
774 busyprop = icalcomponent_get_next_property(vfreebusy, ICAL_FREEBUSY_PROPERTY);
778 icalcomponent_free(toplvl);
782 static gchar *get_avail_msg(const gchar *unavailable_persons, gboolean multiple,
783 gboolean short_version, gint offset_before, gint offset_after)
785 gchar *msg, *intro = NULL, *outro = NULL, *before = NULL, *after = NULL;
788 intro = g_strdup(_("The following person(s) are busy at the time of your planned meeting:\n- "));
789 else if (!strcmp(unavailable_persons, _("You")))
790 intro = g_strdup(_("You are busy at the time of your planned meeting"));
792 intro = g_strdup_printf(_("%s is busy at the time of your planned meeting"), unavailable_persons);
793 if (offset_before == 3600)
794 before = g_strdup_printf(_("%d hour sooner"), offset_before/3600);
795 else if (offset_before > 3600 && offset_before%3600 == 0)
796 before = g_strdup_printf(_("%d hours sooner"), offset_before/3600);
797 else if (offset_before > 3600)
798 before = g_strdup_printf(_("%d hours and %d minutes sooner"), offset_before/3600, (offset_before%3600)/60);
799 else if (offset_before == 1800)
800 before = g_strdup_printf(_("%d minutes sooner"), offset_before/60);
804 if (offset_after == 3600)
805 after = g_strdup_printf(_("%d hour later"), offset_after/3600);
806 else if (offset_after > 3600 && offset_after%3600 == 0)
807 after = g_strdup_printf(_("%d hours later"), offset_after/3600);
808 else if (offset_after > 3600)
809 after = g_strdup_printf(_("%d hours and %d minutes later"), offset_after/3600, (offset_after%3600)/60);
810 else if (offset_after == 1800)
811 after = g_strdup_printf(_("%d minutes later"), offset_after/60);
817 outro = g_strdup_printf(_("\n\nEveryone would be available %s or %s."), before, after);
818 else if (before || after)
819 outro = g_strdup_printf(_("\n\nEveryone would be available %s."), before?before:after);
821 outro = g_strdup_printf(_("\n\nIt isn't possible to have this meeting with everyone "
822 "in the previous or next 6 hours."));
826 outro = g_markup_printf_escaped(_("would be available %s or %s"), before, after);
827 else if (before || after)
828 outro = g_markup_printf_escaped(_("would be available %s"), before?before:after);
830 outro = g_strdup_printf(_("not available"));
833 outro = g_markup_printf_escaped(_(", but would be available %s or %s."), before, after);
834 else if (before || after)
835 outro = g_markup_printf_escaped(_(", but would be available %s."), before?before:after);
837 outro = g_strdup_printf(_(", and isn't available "
838 "in the previous or next 6 hours."));
841 if (multiple && short_version)
842 msg = g_strconcat(outro+2, NULL);
844 msg = g_strconcat(intro, unavailable_persons, outro, NULL);
845 else if (short_version)
846 msg = g_strdup(outro);
848 msg = g_strconcat(intro, outro, NULL);
856 static gboolean find_availability(const gchar *dtstart, const gchar *dtend, GSList *attendees, gboolean for_send, VCalMeeting *meet)
859 gint offset = -1800, offset_before = 0, offset_after = 0;
860 gboolean found = FALSE;
861 gchar *unavailable_persons = NULL;
863 struct icaltimetype start = icaltime_from_string(dtstart);
864 struct icaltimetype end = icaltime_from_string(dtend);
865 AlertValue val = G_ALERTALTERNATE;
867 GHashTable *avail_table_avail = g_hash_table_new(NULL, g_direct_equal);
868 GHashTable *avail_table_before = g_hash_table_new(NULL, g_direct_equal);
869 GHashTable *avail_table_after = g_hash_table_new(NULL, g_direct_equal);
870 #if !(GTK_CHECK_VERSION(2,12,0))
871 GtkTooltips *tips = meet->tips;
874 for (cur = attendees; cur; cur = cur->next) {
875 VCalAttendee *attendee = (VCalAttendee *)cur->data;
876 if (!attendee_available(attendee, icaltime_as_ical_string(start), icaltime_as_ical_string(end),
877 attendee->cached_contents)) {
881 mail = g_strdup(_("You"));
883 mail = gtk_editable_get_chars(GTK_EDITABLE(attendee->address), 0, -1);
885 if (unavailable_persons == NULL) {
886 unavailable_persons = g_markup_printf_escaped("%s", mail);
888 gchar *tmp = g_markup_printf_escaped("%s,\n- %s", unavailable_persons, mail);
889 g_free(unavailable_persons);
890 unavailable_persons = tmp;
894 att_update_icon(meet, attendee, 0, _("not available"));
896 if (attendee->cached_contents != NULL)
897 att_update_icon(meet, attendee, 1, _("available"));
899 att_update_icon(meet, attendee, 2, _("Free/busy retrieval failed"));
901 g_hash_table_insert(avail_table_avail, attendee, GINT_TO_POINTER(1));
906 while (!found && offset >= -3600*6) {
908 struct icaltimetype new_start = icaltime_from_timet(icaltime_as_timet(start)+offset, FALSE);
909 struct icaltimetype new_end = icaltime_from_timet(icaltime_as_timet(end)+offset, FALSE);
910 for (cur = attendees; cur; cur = cur->next) {
911 VCalAttendee *attendee = (VCalAttendee *)cur->data;
912 debug_print("trying %s - %s (offset %d)\n",
913 icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end), offset);
914 if (!attendee_available(attendee, icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end),
915 attendee->cached_contents)) {
919 if (!g_hash_table_lookup(avail_table_before, attendee)
920 && !g_hash_table_lookup(avail_table_avail, attendee))
921 g_hash_table_insert(avail_table_before, attendee, GINT_TO_POINTER(-offset));
926 offset_before = -offset;
932 while (!found && offset <= 3600*6) {
934 struct icaltimetype new_start = icaltime_from_timet(icaltime_as_timet(start)+offset, FALSE);
935 struct icaltimetype new_end = icaltime_from_timet(icaltime_as_timet(end)+offset, FALSE);
936 for (cur = attendees; cur; cur = cur->next) {
937 VCalAttendee *attendee = (VCalAttendee *)cur->data;
938 debug_print("trying %s - %s (offset %d)\n",
939 icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end), offset);
940 if (!attendee_available(attendee, icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end),
941 attendee->cached_contents)) {
945 if (!g_hash_table_lookup(avail_table_after, attendee)
946 && !g_hash_table_lookup(avail_table_avail, attendee))
947 g_hash_table_insert(avail_table_after, attendee, GINT_TO_POINTER(offset));
952 offset_after = offset;
958 for (cur = attendees; cur; cur = cur->next) {
959 VCalAttendee *attendee = (VCalAttendee *)cur->data;
960 gint ok = GPOINTER_TO_INT(g_hash_table_lookup(avail_table_avail, attendee));
961 gint o_before = GPOINTER_TO_INT(g_hash_table_lookup(avail_table_before, attendee));
962 gint o_after = GPOINTER_TO_INT(g_hash_table_lookup(avail_table_after, attendee));
963 if (!o_before && !o_after && !ok) {
964 att_update_icon(meet, attendee, 0, _("not available"));
965 } else if ((o_before != 0 || o_after != 0) && !ok) {
967 msg = get_avail_msg(_("You"), FALSE, TRUE, o_before, o_after);
969 msg = get_avail_msg(gtk_entry_get_text(GTK_ENTRY(attendee->address)), FALSE, TRUE, o_before, o_after);
970 att_update_icon(meet, attendee, 0, msg);
975 g_hash_table_destroy(avail_table_before);
976 g_hash_table_destroy(avail_table_after);
979 msg = get_avail_msg(unavailable_persons, (total > 1), FALSE, offset_before, offset_after);
981 val = alertpanel_full(_("Not everyone is available"), msg,
982 GTK_STOCK_CANCEL, _("Send anyway"), NULL, FALSE,
983 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
986 msg = get_avail_msg(unavailable_persons, TRUE, TRUE, offset_before, offset_after);
987 g_free(unavailable_persons);
988 gtk_image_set_from_stock
989 (GTK_IMAGE(meet->total_avail_img),
990 GTK_STOCK_DIALOG_WARNING,
991 GTK_ICON_SIZE_SMALL_TOOLBAR);
992 gtk_widget_show(meet->total_avail_img);
993 gtk_label_set_text(GTK_LABEL(meet->total_avail_msg), _("Not everyone is available. "
994 "See tooltips for more info..."));
995 CLAWS_SET_TIP(meet->total_avail_evtbox, msg);
997 return (val == G_ALERTALTERNATE);
1000 static gboolean check_attendees_availability(VCalMeeting *meet, gboolean tell_if_ok, gboolean for_send)
1004 gchar *real_url = NULL;
1005 gint num_format = 0;
1006 gchar *change_user = NULL, *change_dom = NULL;
1007 gchar *dtstart = NULL;
1008 gchar *dtend = NULL;
1009 gboolean find_avail = FALSE;
1010 gboolean res = TRUE, uncertain = FALSE;
1011 gchar *organizer = NULL;
1012 VCalAttendee *dummy_org = NULL;
1013 gchar *internal_ifb = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1014 "vcalendar", G_DIR_SEPARATOR_S,
1015 "internal.ifb", NULL);
1016 gboolean local_only = FALSE;
1018 #if !(GTK_CHECK_VERSION(2,12,0))
1019 GtkTooltips *tips = meet->tips;
1022 if (vcalprefs.freebusy_get_url == NULL
1023 || *vcalprefs.freebusy_get_url == '\0') {
1026 real_url = g_strdup(vcalprefs.freebusy_get_url);
1029 while (strchr(tmp, '%')) {
1030 tmp = strchr(tmp, '%')+1;
1033 if (num_format > 2) {
1034 g_warning("wrong format in %s!\n", real_url);
1040 if (strstr(real_url, "%u") != NULL) {
1041 change_user = strstr(real_url, "%u");
1042 *(strstr(real_url, "%u")+1) = 's';
1044 if (strstr(real_url, "%d") != NULL) {
1045 change_dom = strstr(real_url, "%d");
1046 *(strstr(real_url, "%d")+1) = 's';
1048 debug_print("url format %s\n", real_url);
1050 dtstart = get_date(meet, TRUE);
1051 dtend = get_date(meet, FALSE);
1053 /* hack to check our own avail. */
1054 organizer = get_organizer(meet);
1055 dummy_org = g_new0(VCalAttendee, 1);
1056 dummy_org->address = gtk_entry_new();
1057 dummy_org->avail_img = meet->avail_img;
1058 dummy_org->avail_evtbox = meet->avail_evtbox;
1059 dummy_org->org = TRUE;
1060 gtk_entry_set_text(GTK_ENTRY(dummy_org->address), organizer);
1062 dummy_org->cached_contents = file_read_to_str(internal_ifb);
1063 g_free(internal_ifb);
1066 meet->attendees = g_slist_prepend(meet->attendees, dummy_org);
1067 attlist = meet->attendees;
1069 attlist = g_slist_prepend(NULL, dummy_org);
1072 gtk_widget_set_sensitive(meet->save_btn, FALSE);
1073 gtk_widget_set_sensitive(meet->avail_btn, FALSE);
1075 if (meet->window->window)
1076 gdk_window_set_cursor(meet->window->window, watch_cursor);
1078 for (cur = attlist; cur && cur->data; cur = cur->next) {
1079 VCalAttendee *attendee = (VCalAttendee *)cur->data;
1080 gchar *email = gtk_editable_get_chars(GTK_EDITABLE(attendee->address), 0, -1);
1081 gchar *remail, *user, *domain;
1082 gchar *contents = NULL;
1084 if (*email == '\0') {
1086 att_update_icon(meet, attendee, 0, NULL);
1091 remail = g_strdup(email);
1093 extract_address(remail);
1094 if (strrchr(remail, ' '))
1095 user = g_strdup(strrchr(remail, ' ')+1);
1097 user = g_strdup(remail);
1098 if (strchr(user, '@')) {
1099 domain = g_strdup(strchr(user, '@')+1);
1100 *(strchr(user, '@')) = '\0';
1102 domain = g_strdup("");
1105 if (change_user && change_dom) {
1106 if (change_user < change_dom)
1107 tmp = g_strdup_printf(real_url, user, domain);
1109 tmp = g_strdup_printf(real_url, domain, user);
1110 } else if (change_user) {
1111 tmp = g_strdup_printf(real_url, user);
1112 } else if (change_dom) {
1113 tmp = g_strdup_printf(real_url, domain);
1115 tmp = g_strdup(real_url);
1119 debug_print("url to get %s\n", tmp);
1122 if (attendee->cached_contents != NULL) {
1123 contents = attendee->cached_contents;
1124 attendee->cached_contents = NULL;
1125 } else if (!local_only) {
1126 if (strncmp(tmp, "http://", 7)
1127 && strncmp(tmp, "https://", 8)
1128 && strncmp(tmp, "webcal://", 9)
1129 && strncmp(tmp, "webcals://", 10)
1130 && strncmp(tmp, "ftp://", 6))
1131 contents = file_read_to_str(tmp);
1133 gchar *label = g_strdup_printf(_("Fetching planning for %s..."), email);
1134 if (!strncmp(tmp, "webcal", 6)) {
1135 gchar *tmp2 = g_strdup_printf("http%s", tmp+6);
1139 contents = vcal_curl_read(tmp, label, FALSE, NULL);
1149 if (contents == NULL) {
1151 att_update_icon(meet, attendee, 2, _("Free/busy retrieval failed"));
1155 if (!attendee_available(attendee, dtstart, dtend, contents)) {
1157 debug_print("not available!\n");
1159 debug_print("available!\n");
1160 att_update_icon(meet, attendee, 1, _("Available"));
1162 attendee->cached_contents = contents;
1168 res = find_availability((dtstart), (dtend), attlist, for_send, meet);
1173 alertpanel_notice(_("Everyone is available."));
1174 else if (!uncertain) {
1175 gtk_image_set_from_stock
1176 (GTK_IMAGE(meet->total_avail_img),
1177 GTK_STOCK_DIALOG_INFO,
1178 GTK_ICON_SIZE_SMALL_TOOLBAR);
1179 gtk_widget_show(meet->total_avail_img);
1180 gtk_label_set_text(GTK_LABEL(meet->total_avail_msg), _("Everyone is available."));
1181 CLAWS_SET_TIP(meet->total_avail_evtbox, NULL);
1183 gtk_image_set_from_stock
1184 (GTK_IMAGE(meet->total_avail_img),
1185 GTK_STOCK_DIALOG_QUESTION,
1186 GTK_ICON_SIZE_SMALL_TOOLBAR);
1187 gtk_widget_show(meet->total_avail_img);
1188 gtk_label_set_text(GTK_LABEL(meet->total_avail_msg), _("Everyone is available."));
1189 CLAWS_SET_TIP(meet->total_avail_evtbox, _("Everyone seems available, but some free/busy information failed to be retrieved."));
1194 for (cur = attlist; cur && cur->data; cur = cur->next) {
1195 VCalAttendee *attendee = (VCalAttendee *)cur->data;
1196 g_free(attendee->cached_contents);
1197 attendee->cached_contents = NULL;
1199 gtk_widget_set_sensitive(meet->save_btn, TRUE);
1200 gtk_widget_set_sensitive(meet->avail_btn, avail_btn_can_be_sensitive());
1201 if (meet->window->window)
1202 gdk_window_set_cursor(meet->window->window, NULL);
1205 meet->attendees = g_slist_remove(meet->attendees, dummy_org);
1207 g_slist_free(attlist);
1208 gtk_widget_destroy(dummy_org->address);
1219 static gboolean check_avail_cb(GtkButton *widget, gpointer data)
1221 VCalMeeting *meet = (VCalMeeting *)data;
1222 check_attendees_availability(meet, TRUE, FALSE);
1226 static gboolean send_meeting_cb(GtkButton *widget, gpointer data)
1228 VCalMeeting *meet = (VCalMeeting *)data;
1230 gchar *organizer = NULL;
1231 gchar *organizer_name = NULL;
1232 gchar *dtstart = NULL;
1233 gchar *dtend = NULL;
1235 gchar *location = NULL;
1236 gchar *summary = NULL;
1237 gchar *description = NULL;
1238 VCalEvent *event = NULL;
1241 PrefsAccount *account = NULL;
1242 gboolean res = FALSE;
1243 gboolean found_att = FALSE;
1244 Folder *folder = folder_find_from_name ("vCalendar", vcal_folder_get_class());
1245 gboolean redisp = FALSE;
1247 if (meet->uid == NULL && meet->visible &&
1248 !check_attendees_availability(meet, FALSE, TRUE)) {
1253 MainWindow *mainwin = mainwindow_get_mainwindow();
1254 if (mainwin->summaryview->folder_item == folder->inbox) {
1256 summary_show(mainwin->summaryview, NULL);
1259 gtk_widget_set_sensitive(meet->save_btn, FALSE);
1260 gtk_widget_set_sensitive(meet->avail_btn, FALSE);
1261 if (meet->window->window)
1262 gdk_window_set_cursor(meet->window->window, watch_cursor);
1264 organizer = get_organizer(meet);
1265 organizer_name = get_organizer_name(meet);
1266 account = account_find_from_address(organizer, FALSE);
1268 if (account && account->set_domain && account->domain) {
1269 g_snprintf(buf, sizeof(buf), "%s", account->domain);
1270 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
1271 g_snprintf(buf, sizeof(buf), "%s",
1272 strchr(account->address, '@') ?
1273 strchr(account->address, '@')+1 :
1276 g_snprintf(buf, sizeof(buf), "%s", "");
1278 generate_msgid(buf, 255, account->address);
1281 uid = g_strdup(meet->uid);
1283 uid = g_strdup(buf);
1286 dtstart = get_date(meet, TRUE);
1287 dtend = get_date(meet, FALSE);
1288 location = get_location(meet);
1289 summary = get_summary(meet);
1290 description = get_description(meet);
1292 event = vcal_manager_new_event(uid, organizer, organizer_name, location, summary, description,
1293 dtstart, dtend, NULL, tzid, NULL, meet->method,
1295 ICAL_VEVENT_COMPONENT);
1297 vcal_manager_update_answer(event, organizer, organizer_name,
1298 ICAL_PARTSTAT_ACCEPTED,
1299 ICAL_CUTYPE_INDIVIDUAL);
1301 for (cur = meet->attendees; cur && cur->data; cur = cur->next) {
1302 VCalAttendee *attendee = (VCalAttendee *)cur->data;
1303 gchar *email = gtk_editable_get_chars(GTK_EDITABLE(attendee->address), 0, -1);
1305 gchar *orig_email = email;
1307 enum icalparameter_cutype cutype = ICAL_CUTYPE_INDIVIDUAL;
1308 enum icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION;
1310 index = gtk_combo_box_get_active(GTK_COMBO_BOX(attendee->cutype));
1312 cutype = ICAL_CUTYPE_INDIVIDUAL + index;
1313 if (attendee->status) {
1314 if(!strcmp(attendee->status, "accepted"))
1315 status = ICAL_PARTSTAT_ACCEPTED;
1316 if(!strcmp(attendee->status, "tentatively accepted"))
1317 status = ICAL_PARTSTAT_TENTATIVE;
1318 if(!strcmp(attendee->status, "declined"))
1319 status = ICAL_PARTSTAT_DECLINED;
1320 g_free(attendee->status);
1322 if (strlen(email)) {
1323 if (strstr(email, " <")) {
1325 email = strstr(email," <") + 2;
1326 *(strstr(name," <")) = '\0';
1327 if (strstr(email, ">"))
1328 *(strstr(email, ">")) = '\0';
1331 vcal_manager_update_answer(event, email, name,
1334 found_att = strcmp(email, organizer);
1340 res = vcal_manager_request(account, event);
1345 g_free(organizer_name);
1348 g_free(description);
1351 vcal_manager_free_event(event);
1353 gtk_widget_set_sensitive(meet->save_btn, TRUE);
1354 gtk_widget_set_sensitive(meet->avail_btn, avail_btn_can_be_sensitive());
1355 if (meet->window->window)
1356 gdk_window_set_cursor(meet->window->window, NULL);
1361 alertpanel_error(_("Could not send the meeting invitation.\n"
1362 "Check the recipients."));
1366 folder_item_scan(folder->inbox);
1368 if (folder && redisp) {
1369 MainWindow *mainwin = mainwindow_get_mainwindow();
1370 summary_show(mainwin->summaryview, folder->inbox);
1376 static GList *get_predefined_times(void)
1379 GList *times = NULL;
1380 for (h = 0; h < 24; h++) {
1381 for (m = 0; m < 60; m += 15) {
1382 gchar *tmp = g_strdup_printf("%02d:%02d", h, m);
1383 times = g_list_append(times, tmp);
1389 static VCalMeeting *vcal_meeting_create_real(VCalEvent *event, gboolean visible)
1391 VCalMeeting *meet = g_new0(VCalMeeting, 1);
1392 GtkTextBuffer *buffer = NULL;
1393 GtkWidget *date_hbox, *date_vbox, *save_hbox, *label, *hbox;
1395 GtkObject *start_h_adj, *start_m_adj, *end_h_adj, *end_m_adj;
1397 GtkWidget *scrolledwin;
1398 GList *times = NULL;
1401 gchar *time_text = NULL;
1403 GtkWidget *notebook;
1404 GtkWidget *maemo_vbox0;
1408 watch_cursor = gdk_cursor_new(GDK_WATCH);
1410 #if !(GTK_CHECK_VERSION(2,12,0))
1413 meet->visible = visible;
1414 start_h_adj = gtk_adjustment_new (0, 0, 23, 1, 10, 10);
1415 start_m_adj = gtk_adjustment_new (0, 0, 59, 1, 10, 10);
1416 end_h_adj = gtk_adjustment_new (0, 0, 23, 1, 10, 10);
1417 end_m_adj = gtk_adjustment_new (0, 0, 59, 1, 10, 10);
1419 meet->window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "vcal_meeting_gtk");
1420 #ifndef GENERIC_UMPC
1421 meet->table = gtk_table_new(7, 2, FALSE);
1423 meet->table1 = gtk_table_new(4, 2, FALSE);
1424 meet->table2 = gtk_table_new(2, 2, FALSE);
1426 meet->who = gtk_combo_box_new_text();
1428 meet->start_c = gtk_calendar_new();
1429 meet->end_c = gtk_calendar_new();
1431 meet->avail_evtbox = gtk_event_box_new();
1432 meet->avail_img = gtk_image_new_from_stock
1433 (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
1435 times = get_predefined_times();
1437 #if !GTK_CHECK_VERSION(2, 24, 0)
1438 meet->start_time = gtk_combo_box_entry_new_text();
1440 meet->start_time = gtk_combo_box_text_new_with_entry();
1442 gtk_combo_box_set_active(GTK_COMBO_BOX(meet->start_time), -1);
1443 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(meet->start_time)));
1444 #if !GTK_CHECK_VERSION(2, 24, 0)
1445 combobox_set_popdown_strings(GTK_COMBO_BOX(meet->start_time), times);
1447 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(meet->start_time), times);
1450 #if !GTK_CHECK_VERSION(2, 24, 0)
1451 meet->end_time = gtk_combo_box_entry_new_text();
1453 meet->end_time = gtk_combo_box_text_new_with_entry();
1455 gtk_combo_box_set_active(GTK_COMBO_BOX(meet->end_time), -1);
1456 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(meet->end_time)));
1457 #if !GTK_CHECK_VERSION(2, 24, 0)
1458 combobox_set_popdown_strings(GTK_COMBO_BOX(meet->end_time), times);
1460 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(meet->end_time), times);
1463 list_free_strings(times);
1466 meet->location = gtk_entry_new();
1467 meet->summary = gtk_entry_new();
1468 meet->description = gtk_text_view_new();
1469 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(meet->description));
1470 gtk_text_view_set_editable(GTK_TEXT_VIEW(meet->description), TRUE);
1471 gtk_text_buffer_add_selection_clipboard(buffer, gtk_clipboard_get(GDK_SELECTION_PRIMARY));
1473 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
1474 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
1475 GTK_POLICY_AUTOMATIC,
1476 GTK_POLICY_AUTOMATIC);
1477 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
1479 gtk_container_add(GTK_CONTAINER(scrolledwin), meet->description);
1482 meet->uid = g_strdup(event->uid);
1483 meet->sequence = event->sequence + 1;
1484 meet->method = (event->method == ICAL_METHOD_CANCEL ?
1485 ICAL_METHOD_CANCEL:ICAL_METHOD_REQUEST);
1487 gtk_entry_set_text(GTK_ENTRY(meet->location), event->location);
1488 gtk_entry_set_text(GTK_ENTRY(meet->summary), event->summary);
1489 gtk_text_buffer_set_text(buffer, event->description, -1);
1491 meet->method = ICAL_METHOD_REQUEST;
1493 meet->save_btn = gtk_button_new_with_label(_("Save & Send"));
1494 meet->avail_btn = gtk_button_new_with_label(_("Check availability"));
1496 meet->total_avail_evtbox = gtk_event_box_new();
1497 meet->total_avail_img = gtk_image_new_from_stock
1498 (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
1499 meet->total_avail_msg = gtk_label_new("");
1501 gtk_widget_set_size_request(meet->total_avail_evtbox, 18, 16);
1502 gtk_event_box_set_visible_window(GTK_EVENT_BOX(meet->total_avail_evtbox), FALSE);
1503 gtk_container_add (GTK_CONTAINER(meet->total_avail_evtbox), meet->total_avail_img);
1505 g_signal_connect(G_OBJECT(meet->save_btn), "clicked",
1506 G_CALLBACK(send_meeting_cb), meet);
1508 g_signal_connect(G_OBJECT(meet->avail_btn), "clicked",
1509 G_CALLBACK(check_avail_cb), meet);
1511 g_signal_connect(G_OBJECT(meet->window), "destroy",
1512 G_CALLBACK(destroy_meeting_cb), meet);
1513 g_signal_connect(G_OBJECT(meet->window), "key_press_event",
1514 G_CALLBACK(meeting_key_pressed), meet);
1517 gtk_widget_set_size_request(meet->description, -1, 100);
1518 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(meet->description), GTK_WRAP_WORD);
1520 if (!event || (event && !event->dtstart && !event->dtend)) {
1521 time_t t = time (NULL)+ 3600;
1522 struct tm buft1, buft2;
1523 struct tm *lt = localtime_r (&t, &buft1);
1525 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c),
1527 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
1528 lt->tm_mon, lt->tm_year + 1900);
1530 time_text = g_strdup_printf("%02d:%02d", lt->tm_hour, 0);
1531 combobox_select_by_text(GTK_COMBO_BOX(meet->start_time), time_text);
1535 lt = localtime_r(&t, &buft2);
1537 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1539 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1540 lt->tm_mon, lt->tm_year + 1900);
1542 time_text = g_strdup_printf("%02d:%02d", lt->tm_hour, 0);
1543 combobox_select_by_text(GTK_COMBO_BOX(meet->end_time), time_text);
1547 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c),
1548 get_dtdate(event->dtstart, DAY));
1549 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1550 get_dtdate(event->dtend, DAY));
1552 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
1553 get_dtdate(event->dtstart, MONTH)-1,
1554 get_dtdate(event->dtstart, YEAR));
1555 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1556 get_dtdate(event->dtend, MONTH)-1,
1557 get_dtdate(event->dtend, YEAR));
1559 num = get_list_item_num(get_dtdate(event->dtstart, HOUR),
1560 get_dtdate(event->dtstart, MINUTE));
1562 time_text = g_strdup_printf("%02d:%02d", get_dtdate(event->dtstart, HOUR),
1563 get_dtdate(event->dtstart, MINUTE));
1564 combobox_select_by_text(GTK_COMBO_BOX(meet->start_time), time_text);
1567 gchar *tmp = g_strdup_printf("%02d:%02d",
1568 get_dtdate(event->dtstart, HOUR),
1569 get_dtdate(event->dtstart, MINUTE));
1570 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(meet->start_time))),
1575 num = get_list_item_num(get_dtdate(event->dtend, HOUR),
1576 get_dtdate(event->dtend, MINUTE));
1578 time_text = g_strdup_printf("%02d:%02d", get_dtdate(event->dtend, HOUR),
1579 get_dtdate(event->dtend, MINUTE));
1580 combobox_select_by_text(GTK_COMBO_BOX(meet->end_time), time_text);
1583 gchar *tmp = g_strdup_printf("%02d:%02d",
1584 get_dtdate(event->dtend, HOUR),
1585 get_dtdate(event->dtend, MINUTE));
1586 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(meet->end_time))),
1592 g_signal_connect(G_OBJECT(meet->start_c), "day-selected",
1593 G_CALLBACK(meeting_start_changed), meet);
1594 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(meet->start_time))),
1596 G_CALLBACK(meeting_start_changed),
1599 g_signal_connect(G_OBJECT(meet->end_c), "day-selected",
1600 G_CALLBACK(meeting_end_changed), meet);
1601 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(meet->end_time))),
1603 G_CALLBACK(meeting_end_changed),
1606 #ifndef GENERIC_UMPC
1607 gtk_widget_set_size_request(meet->start_time, 80, -1);
1608 gtk_widget_set_size_request(meet->end_time, 80, -1);
1610 gtk_widget_set_size_request(meet->start_time, 120, -1);
1611 gtk_widget_set_size_request(meet->end_time, 120, -1);
1614 date_hbox = gtk_hbox_new(FALSE, 6);
1615 date_vbox = gtk_vbox_new(FALSE, 6);
1616 hbox = gtk_hbox_new(FALSE, 6);
1617 label = gtk_label_new(_("<b>Starts at:</b> "));
1618 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1619 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1621 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1622 gtk_box_pack_start(GTK_BOX(hbox), meet->start_time, FALSE, FALSE, 0);
1623 label = gtk_label_new(_("<b> on:</b>"));
1624 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1625 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1626 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1627 gtk_box_pack_start(GTK_BOX(date_vbox), hbox, FALSE, FALSE, 0);
1628 gtk_box_pack_start(GTK_BOX(date_vbox), meet->start_c, FALSE, FALSE, 0);
1629 gtk_box_pack_start(GTK_BOX(date_hbox), date_vbox, FALSE, FALSE, 0);
1631 #ifndef GENERIC_UMPC
1632 label = gtk_label_new(" ");
1634 label = gtk_label_new("");
1636 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1637 gtk_box_pack_start(GTK_BOX(date_hbox), label, TRUE, TRUE, 0);
1639 date_vbox = gtk_vbox_new(FALSE, 6);
1640 hbox = gtk_hbox_new(FALSE, 6);
1641 label = gtk_label_new(_("<b>Ends at:</b> "));
1642 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1643 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1645 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1646 gtk_box_pack_start(GTK_BOX(hbox), meet->end_time, FALSE, FALSE, 0);
1647 label = gtk_label_new(_("<b> on:</b>"));
1648 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1649 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1650 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1651 gtk_box_pack_start(GTK_BOX(date_vbox), hbox, FALSE, FALSE, 0);
1652 gtk_box_pack_start(GTK_BOX(date_vbox), meet->end_c, FALSE, FALSE, 0);
1653 gtk_box_pack_start(GTK_BOX(date_hbox), date_vbox, FALSE, FALSE, 0);
1655 meet->attendees_vbox = gtk_vbox_new(FALSE, 6);
1656 gtk_widget_show_all(meet->attendees_vbox);
1658 attendee_add(meet, NULL, NULL, NULL, NULL, TRUE);
1660 gboolean firstadd = TRUE;
1661 GSList *list = vcal_manager_get_answers_emails(event);
1662 while (list && list->data) {
1663 gchar *address = (gchar *)list->data;
1664 gchar *name = vcal_manager_get_attendee_name(event, address);
1665 gchar *answer = vcal_manager_get_reply_text_for_attendee(event, address);
1666 gchar *type = vcal_manager_get_cutype_text_for_attendee(event, address);
1667 if (strcmp(event->organizer, address)) {
1668 attendee_add(meet, address, name, answer, type, firstadd);
1677 if (firstadd == TRUE)
1678 attendee_add(meet, NULL, NULL, NULL, NULL, TRUE);
1682 gtk_window_set_title(GTK_WINDOW(meet->window), _("New meeting"));
1684 gchar *title = g_strdup_printf(_("%s - Edit meeting"),
1686 gtk_window_set_title(GTK_WINDOW(meet->window), title);
1689 address_completion_start(meet->window);
1691 accounts = account_get_list();
1692 g_return_val_if_fail(accounts != NULL, NULL);
1694 for (i = 0; accounts != NULL; accounts = accounts->next) {
1695 PrefsAccount *ac = (PrefsAccount *)accounts->data;
1697 if (ac->protocol == A_NNTP) {
1700 if (!event && ac == account_get_cur_account()) {
1703 else if (event && !strcmp(ac->address, event->organizer))
1706 meet->avail_accounts = g_slist_append(meet->avail_accounts, ac);
1709 s = g_strdup_printf("%s: %s <%s>",
1711 ac->name, ac->address);
1713 s = g_strdup_printf("%s: %s",
1714 ac->account_name, ac->address);
1716 gtk_combo_box_append_text(GTK_COMBO_BOX(meet->who), s);
1720 gtk_combo_box_set_active(GTK_COMBO_BOX(meet->who), num);
1722 save_hbox = gtk_hbox_new(FALSE, 6);
1723 gtk_box_pack_start(GTK_BOX(save_hbox), meet->save_btn, FALSE, FALSE, 0);
1724 gtk_box_pack_start(GTK_BOX(save_hbox), meet->avail_btn, FALSE, FALSE, 0);
1725 gtk_box_pack_start(GTK_BOX(save_hbox), meet->total_avail_evtbox, FALSE, FALSE, 0);
1726 gtk_box_pack_start(GTK_BOX(save_hbox), meet->total_avail_msg, FALSE, FALSE, 0);
1728 hbox = gtk_hbox_new(FALSE, 6);
1729 gtk_box_pack_start(GTK_BOX(hbox), meet->avail_evtbox, FALSE, FALSE, 0);
1730 gtk_box_pack_start(GTK_BOX(hbox), meet->who, TRUE, TRUE, 0);
1732 gtk_widget_set_size_request(meet->avail_evtbox, 18, 16);
1733 gtk_event_box_set_visible_window(GTK_EVENT_BOX(meet->avail_evtbox), FALSE);
1734 gtk_container_add (GTK_CONTAINER(meet->avail_evtbox), meet->avail_img);
1736 #ifndef GENERIC_UMPC
1737 TABLE_ADD_LINE(_("Organizer:"), hbox, FALSE);
1738 TABLE_ADD_LINE(_("Summary:"), meet->summary, TRUE);
1739 TABLE_ADD_LINE(_("Time:"), date_hbox, TRUE);
1740 TABLE_ADD_LINE(_("Location:"), meet->location, TRUE);
1741 TABLE_ADD_LINE(_("Description:"), scrolledwin, TRUE);
1742 TABLE_ADD_LINE(_("Attendees:"), meet->attendees_vbox, FALSE);
1743 TABLE_ADD_LINE("", save_hbox, TRUE);
1745 gtk_widget_set_size_request(meet->window, -1, -1);
1746 gtk_container_add(GTK_CONTAINER(meet->window), meet->table);
1748 TABLE_ADD_LINE(_("Organizer:"), hbox, FALSE, TRUE);
1749 TABLE_ADD_LINE(_("Summary:"), meet->summary, TRUE, TRUE);
1750 TABLE_ADD_LINE(_("Location:"), meet->location, FALSE, TRUE);
1751 TABLE_ADD_LINE(_("Description:"), scrolledwin, TRUE, TRUE);
1752 TABLE_ADD_LINE(_("Attendees:"), meet->attendees_vbox, FALSE, TRUE);
1753 TABLE_ADD_LINE("", date_hbox, TRUE, FALSE);
1755 notebook = gtk_notebook_new ();
1756 gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE);
1757 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
1759 gtk_label_new_with_mnemonic(_("Event:")));
1761 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
1763 gtk_label_new_with_mnemonic(_("Time:")));
1764 gtk_widget_show (notebook);
1766 maemo_vbox0 = gtk_vbox_new(FALSE, 3);
1767 gtk_box_pack_start(GTK_BOX(maemo_vbox0), notebook, TRUE, TRUE, 0);
1768 gtk_box_pack_start(GTK_BOX(maemo_vbox0), save_hbox, FALSE, FALSE, 0);
1770 gtk_widget_set_size_request(meet->window, -1, -1);
1771 gtk_container_add (GTK_CONTAINER (meet->window), maemo_vbox0);
1773 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(meet->window));
1777 gtk_widget_show_all(meet->window);
1778 for (cur = meet->attendees; cur; cur = cur->next) {
1779 gtk_widget_hide(((VCalAttendee *)cur->data)->avail_img);
1781 gtk_widget_hide(meet->avail_img);
1782 gtk_widget_hide(meet->total_avail_img);
1783 gtk_widget_set_sensitive(meet->avail_btn, avail_btn_can_be_sensitive());
1788 VCalMeeting *vcal_meeting_create(VCalEvent *event)
1790 return vcal_meeting_create_real(event, TRUE);
1793 VCalMeeting *vcal_meeting_create_with_start(VCalEvent *event, struct tm *sdate)
1795 VCalMeeting *meet = vcal_meeting_create(event);
1797 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c),
1799 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1802 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
1803 sdate->tm_mon, sdate->tm_year+1900);
1804 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1805 sdate->tm_mon, sdate->tm_year+1900);
1807 if (sdate->tm_hour != 0) {
1808 num = get_list_item_num(sdate->tm_hour, 0);
1810 gchar *time_text = g_strdup_printf("%02d:%02d", sdate->tm_hour, 0);
1811 combobox_select_by_text(GTK_COMBO_BOX(meet->start_time), time_text);
1814 if (sdate->tm_hour < 23) {
1815 num = get_list_item_num(sdate->tm_hour+1, 0);
1817 gchar *time_text = g_strdup_printf("%02d:%02d", sdate->tm_hour+1, 0);
1818 combobox_select_by_text(GTK_COMBO_BOX(meet->end_time), time_text);
1822 struct tm tm_tomorrow;
1824 tm_tomorrow.tm_mday = sdate->tm_mday;
1825 tm_tomorrow.tm_mon = sdate->tm_mon;
1826 tm_tomorrow.tm_wday = sdate->tm_wday;
1827 tm_tomorrow.tm_year = sdate->tm_year+1900;
1828 tm_tomorrow.tm_hour = sdate->tm_hour;
1829 orage_move_day(&tm_tomorrow, +1);
1830 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1831 tm_tomorrow.tm_mday);
1832 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1833 tm_tomorrow.tm_mon, tm_tomorrow.tm_year);
1835 time_text = g_strdup_printf("%02d:%02d", 0, 0);
1836 combobox_select_by_text(GTK_COMBO_BOX(meet->end_time), time_text);
1843 VCalMeeting *vcal_meeting_create_hidden(VCalEvent *event)
1845 return vcal_meeting_create_real(event, FALSE);
1848 gboolean vcal_meeting_send(VCalMeeting *meet)
1850 return send_meeting_cb(NULL, meet);
1853 gboolean vcal_meeting_alert_check(gpointer data)
1855 GSList *events = NULL, *cur = NULL;
1857 if (!vcalprefs.alert_enable)
1860 events = vcal_folder_get_waiting_events();
1862 for (cur = events; cur; cur = cur->next) {
1863 VCalEvent *event = (VCalEvent *)cur->data;
1864 time_t start, end, current;
1865 gboolean warn = FALSE;
1869 start = icaltime_as_timet(icaltime_from_string(event->dtstart));
1870 end = icaltime_as_timet(icaltime_from_string(event->dtend));
1871 current = time(NULL);
1873 if (start - current <= (vcalprefs.alert_delay*60)
1874 && start - current + 60 > (vcalprefs.alert_delay*60)) {
1876 } else if (event->postponed - current <= (vcalprefs.alert_delay*60)
1877 && event->postponed - current + 60 > (vcalprefs.alert_delay*60)) {
1881 time_t tmpt = icaltime_as_timet((icaltime_from_string(event->dtstart)));
1882 gchar *estart = NULL;
1884 int length = (end - start) / 60;
1885 gchar *duration = NULL, *hours = NULL, *minutes = NULL;
1886 gchar *message = NULL;
1887 gchar *title = NULL;
1888 gchar *label = NULL;
1889 int postpone_min = 0;
1893 estart = g_strdup(ctime(&tmpt));
1896 hours = g_strdup_printf(ngettext("%d hour", "%d hours",
1897 (length/60) > 1 ? 2 : 1), length/60);
1899 minutes = g_strdup_printf(ngettext("%d minute", "%d minutes",
1900 length%60), length%60);
1902 duration = g_strdup_printf("%s%s%s",
1904 hours && minutes ? " ":"",
1905 minutes?minutes:"");
1910 title = g_strdup_printf(_("Upcoming event: %s"), event->summary);
1911 message = g_strdup_printf(_("You have a meeting or event soon.\n"
1912 "It starts at %s and ends %s later.\n"
1914 "More information:\n\n"
1918 event->location?event->location:"",
1919 event->description);
1924 postpone_min = (vcalprefs.alert_delay/2 > 15) ? 15: (vcalprefs.alert_delay/2);
1925 if (postpone_min == 0)
1928 label = g_strdup_printf(ngettext("Remind me in %d minute", "Remind me in %d minutes",
1929 postpone_min > 1 ? 2:1),
1931 aval = alertpanel_full(title, message,
1932 label, GTK_STOCK_OK, NULL, FALSE,
1933 NULL, ALERT_NOTICE, G_ALERTDEFAULT);
1939 if (aval == G_ALERTDEFAULT) {
1940 if (event->postponed == 0)
1941 event->postponed = start + (postpone_min*60);
1943 event->postponed += (postpone_min*60);
1945 event->postponed = (time_t)0;
1947 vcal_manager_save_event(event, FALSE);
1950 vcal_manager_free_event((VCalEvent *)cur->data);
1953 g_slist_free(events);
1958 void multisync_export(void)
1960 GSList *list = NULL;
1961 gchar *path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1962 "vcalendar", G_DIR_SEPARATOR_S,
1964 GSList *files = NULL;
1969 icalcomponent *calendar = NULL;
1972 if (is_dir_exist(path))
1973 remove_dir_recursive(path);
1974 if (!is_dir_exist(path))
1976 if (!is_dir_exist(path)) {
1982 list = vcal_folder_get_waiting_events();
1983 for (cur = list; cur; cur = cur->next) {
1984 VCalEvent *event = (VCalEvent *)cur->data;
1985 file = g_strdup_printf("multisync%lu-%d", time(NULL), i);
1990 icalcomponent_vanew(
1991 ICAL_VCALENDAR_COMPONENT,
1992 icalproperty_new_version("2.0"),
1993 icalproperty_new_prodid(
1994 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
1995 icalproperty_new_calscale("GREGORIAN"),
1998 vcal_manager_event_dump(event, FALSE, FALSE, calendar, FALSE);
1999 tmp = g_strconcat(path, G_DIR_SEPARATOR_S, file, NULL);
2000 str_write_to_file(icalcomponent_as_ical_string(calendar), tmp);
2002 files = g_slist_append(files, file);
2003 vcal_manager_free_event(event);
2004 icalcomponent_free(calendar);
2009 file = g_strconcat(path, G_DIR_SEPARATOR_S, "backup_entries", NULL);
2010 fp = g_fopen(file, "wb");
2013 for (cur = files; cur; cur = cur->next) {
2014 file = (char *)cur->data;
2015 if (fprintf(fp, "1 1 %s\n", file) < 0)
2019 if (fclose(fp) == EOF)
2025 g_slist_free(files);
2028 gboolean vcal_meeting_export_calendar(const gchar *path,
2029 const gchar *user, const gchar *pass,
2032 GSList *list = vcal_folder_get_waiting_events();
2033 GSList *subs = NULL;
2035 icalcomponent *calendar = NULL;
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.ics", NULL);
2042 gboolean res = TRUE;
2047 if (vcalprefs.export_subs && vcalprefs.export_enable)
2048 subs = vcal_folder_get_webcal_events();
2050 if (g_slist_length(list) == 0 && g_slist_length(subs) == 0) {
2054 alertpanel_full(_("Empty calendar"),
2055 _("There is nothing to export."),
2056 GTK_STOCK_OK, NULL, NULL, FALSE,
2057 NULL, ALERT_NOTICE, G_ALERTDEFAULT);
2060 str_write_to_file("", tmpfile);
2066 icalcomponent_vanew(
2067 ICAL_VCALENDAR_COMPONENT,
2068 icalproperty_new_version("2.0"),
2069 icalproperty_new_prodid(
2070 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
2071 icalproperty_new_calscale("GREGORIAN"),
2075 for (cur = list; cur; cur = cur->next) {
2076 VCalEvent *event = (VCalEvent *)cur->data;
2077 vcal_manager_event_dump(event, FALSE, FALSE, calendar, FALSE);
2078 vcal_manager_free_event(event);
2081 if (str_write_to_file(icalcomponent_as_ical_string(calendar), internal_file) < 0) {
2082 g_warning("can't export internal cal\n");
2085 g_free(internal_file);
2087 for (cur = subs; cur; cur = cur->next) {
2088 /* Not to be freed */
2089 icalcomponent *event = (icalcomponent *)cur->data;
2090 vcal_manager_icalevent_dump(event, NULL, calendar);
2093 if (vcalprefs.export_enable || path == NULL) {
2094 if (str_write_to_file(icalcomponent_as_ical_string(calendar), tmpfile) < 0) {
2095 alertpanel_error(_("Could not export the calendar."));
2097 icalcomponent_free(calendar);
2102 filesize = strlen(icalcomponent_as_ical_string(calendar));
2105 icalcomponent_free(calendar);
2111 if (!path && !automatic)
2112 file = filesel_select_file_save(_("Export calendar to ICS"), NULL);
2114 file = g_strdup(path);
2116 if (automatic && (!path || strlen(path) == 0 || !vcalprefs.export_enable)) {
2123 && strncmp(file, "http://", 7)
2124 && strncmp(file, "https://", 8)
2125 && strncmp(file, "webcal://", 9)
2126 && strncmp(file, "webcals://", 10)
2127 && strncmp(file, "ftp://", 6)) {
2128 gchar *afile = NULL;
2129 if (file[0] != G_DIR_SEPARATOR)
2130 afile=g_strdup_printf("%s%s%s", get_home_dir(),
2131 G_DIR_SEPARATOR_S, file);
2133 afile=g_strdup(file);
2134 if (move_file(tmpfile, afile, TRUE) != 0) {
2135 log_error(LOG_PROTOCOL, _("Couldn't export calendar to '%s'\n"),
2142 FILE *fp = g_fopen(tmpfile, "rb");
2143 if (!strncmp(file, "webcal", 6)) {
2144 gchar *tmp = g_strdup_printf("http%s", file+6);
2149 res = vcal_curl_put(file, fp, filesize, user, pass);
2158 gboolean vcal_meeting_export_freebusy(const gchar *path, const gchar *user,
2161 GSList *list = vcal_folder_get_waiting_events();
2163 icalcomponent *calendar = NULL, *timezone = NULL, *tzc = NULL, *vfreebusy = NULL;
2165 gchar *tmpfile = get_tmp_file();
2166 gchar *internal_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2167 "vcalendar", G_DIR_SEPARATOR_S,
2168 "internal.ifb", NULL);
2169 time_t whole_start = time(NULL);
2170 time_t whole_end = whole_start + (60*60*24*365);
2171 gboolean res = TRUE;
2172 struct icaltimetype itt_start, itt_end;
2178 icalcomponent_vanew(
2179 ICAL_VCALENDAR_COMPONENT,
2180 icalproperty_new_version("2.0"),
2181 icalproperty_new_prodid(
2182 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
2183 icalproperty_new_calscale("GREGORIAN"),
2187 timezone = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
2189 icalcomponent_add_property(timezone,
2190 icalproperty_new_tzid("UTC"));
2192 tzc = icalcomponent_new(ICAL_XSTANDARD_COMPONENT);
2193 icalcomponent_add_property(tzc,
2194 icalproperty_new_dtstart(
2195 icaltime_from_string("19700101T000000")));
2196 icalcomponent_add_property(tzc,
2197 icalproperty_new_tzoffsetfrom(0.0));
2198 icalcomponent_add_property(tzc,
2199 icalproperty_new_tzoffsetto(0.0));
2200 icalcomponent_add_property(tzc,
2201 icalproperty_new_tzname("Greenwich meridian time"));
2203 icalcomponent_add_component(timezone, tzc);
2205 icalcomponent_add_component(calendar, timezone);
2207 itt_start = icaltime_from_timet(whole_start, FALSE);
2208 itt_end = icaltime_from_timet(whole_end, FALSE);
2209 itt_start.second = itt_start.minute = itt_start.hour = 0;
2210 itt_end.second = 59; itt_end.minute = 59; itt_end.hour = 23;
2214 icalcomponent_vanew(
2215 ICAL_VFREEBUSY_COMPONENT,
2216 icalproperty_vanew_dtstart(itt_start, 0),
2217 icalproperty_vanew_dtend(itt_end, 0),
2221 debug_print("DTSTART:%s\nDTEND:%s\n",
2222 icaltime_as_ical_string(itt_start),
2223 icaltime_as_ical_string(itt_end));
2225 for (cur = list; cur; cur = cur->next) {
2226 VCalEvent *event = (VCalEvent *)cur->data;
2228 struct icalperiodtype ipt;
2230 ipt.start = icaltime_from_string(event->dtstart);
2231 ipt.end = icaltime_from_string(event->dtend);
2232 ipt.duration = icaltime_subtract(ipt.end, ipt.start);
2233 if (icaltime_as_timet(ipt.start) <= icaltime_as_timet(itt_end)
2234 && icaltime_as_timet(ipt.end) >= icaltime_as_timet(itt_start)) {
2235 prop = icalproperty_new_freebusy(ipt);
2236 icalcomponent_add_property(vfreebusy, prop);
2238 vcal_manager_free_event(event);
2241 icalcomponent_add_component(calendar, vfreebusy);
2243 if (str_write_to_file(icalcomponent_as_ical_string(calendar), internal_file) < 0) {
2244 g_warning("can't export freebusy\n");
2247 g_free(internal_file);
2249 if (vcalprefs.export_freebusy_enable) {
2250 if (str_write_to_file(icalcomponent_as_ical_string(calendar), tmpfile) < 0) {
2251 alertpanel_error(_("Could not export the freebusy info."));
2253 icalcomponent_free(calendar);
2257 filesize = strlen(icalcomponent_as_ical_string(calendar));
2260 icalcomponent_free(calendar);
2263 if ((!path || strlen(path) == 0 || !vcalprefs.export_freebusy_enable)) {
2267 file = g_strdup(path);
2271 && strncmp(file, "http://", 7)
2272 && strncmp(file, "https://", 8)
2273 && strncmp(file, "webcal://", 9)
2274 && strncmp(file, "webcals://", 10)
2275 && strncmp(file, "ftp://", 6)) {
2276 gchar *afile = NULL;
2277 if (file[0] != G_DIR_SEPARATOR)
2278 afile=g_strdup_printf("%s%s%s", get_home_dir(),
2279 G_DIR_SEPARATOR_S, file);
2281 afile=g_strdup(file);
2282 if (move_file(tmpfile, file, TRUE) != 0) {
2283 log_error(LOG_PROTOCOL, _("Couldn't export free/busy to '%s'\n"),
2290 FILE *fp = g_fopen(tmpfile, "rb");
2291 if (!strncmp(file, "webcal", 6)) {
2292 gchar *tmp = g_strdup_printf("http%s", file+6);
2297 res = vcal_curl_put(file, fp, filesize, user, pass);