* persistantly handle the bufferlengths for dates; snprintf doesn't terminate strings...
[citadel.git] / webcit / calendar_view.c
1 /*
2  * $Id$
3  *
4  * Handles the HTML display of calendar items.
5  */
6
7 #include "webcit.h"
8 #include "webserver.h"
9
10 /* These define how high the hour rows are in the day view */
11 #define TIMELINE        30
12 #define EXTRATIMELINE   (TIMELINE / 2)
13
14 void embeddable_mini_calendar(int year, int month)
15 {
16         struct tm starting_tm;
17         struct tm tm;
18         time_t thetime;
19         int i;
20         time_t previous_month;
21         time_t next_month;
22         time_t colheader_time;
23         struct tm colheader_tm;
24         char colheader_label[32];
25         long weekstart = 0;
26         char url[256];
27         char div_id[256];
28         
29         snprintf(div_id, sizeof div_id, "mini_calendar_%d", rand() );
30
31         /* Determine what day to start.  If an impossible value is found, start on Sunday.
32         */
33         get_pref_long("weekstart", &weekstart, 17);
34         if (weekstart > 6) weekstart = 0;
35
36         /*
37         * Now back up to the 1st of the month...
38         */
39         memset(&starting_tm, 0, sizeof(struct tm));
40
41         starting_tm.tm_year = year - 1900;
42         starting_tm.tm_mon = month - 1;
43         starting_tm.tm_mday = 1;
44         thetime = mktime(&starting_tm);
45
46         memcpy(&tm, &starting_tm, sizeof(struct tm));
47         while (tm.tm_mday != 1) {
48                 thetime = thetime - (time_t)86400;      /* go back 24 hours */
49                 localtime_r(&thetime, &tm);
50         }
51
52         /* Determine previous and next months ... for links */
53         previous_month = thetime - (time_t)864000L;     /* back 10 days */
54         next_month = thetime + (time_t)(31L * 86400L);  /* ahead 31 days */
55
56         /* Now back up until we're on the user's preferred start day */
57         localtime_r(&thetime, &tm);
58         while (tm.tm_wday != weekstart) {
59                 thetime = thetime - (time_t)86400;      /* go back 24 hours */
60                 localtime_r(&thetime, &tm);
61         }
62
63         wprintf("<div class=\"mini_calendar\" id=\"%s\">\n", div_id);
64
65         /* Previous month link */
66         localtime_r(&previous_month, &tm);
67         wprintf("<a href=\"javascript:minical_change_month(%d,%d);\">&laquo;</a>", 
68                 (int)(tm.tm_year)+1900, tm.tm_mon + 1);
69
70         wc_strftime(colheader_label, sizeof colheader_label, "%B", &starting_tm);
71         wprintf("&nbsp;&nbsp;"
72                 "<span class=\"mini_calendar_month_label\">"
73                 "%s %d"
74                 "</span>"
75                 "&nbsp;&nbsp;", colheader_label, year);
76
77         /* Next month link */
78         localtime_r(&next_month, &tm);
79         wprintf("<a href=\"javascript:minical_change_month(%d,%d);\">&raquo;</a>",
80                 (int)(tm.tm_year)+1900, tm.tm_mon + 1);
81
82         wprintf("<table border=0 cellpadding=1 cellspacing=1 class=\"mini_calendar_days\">"
83                 "<tr>");
84         colheader_time = thetime;
85         for (i=0; i<7; ++i) {
86                 colheader_time = thetime + (i * 86400) ;
87                 localtime_r(&colheader_time, &colheader_tm);
88                 wc_strftime(colheader_label, sizeof colheader_label, "%A", &colheader_tm);
89                 wprintf("<th>%c</th>", colheader_label[0]);
90
91         }
92         wprintf("</tr>\n");
93
94
95         /* Now do 35 or 42 days */
96         for (i = 0; i < 42; ++i) {
97                 localtime_r(&thetime, &tm);
98
99                 if (i < 35) {
100
101                         /* Before displaying Sunday, start a new row */
102                         if ((i % 7) == 0) {
103                                 wprintf("<tr>");
104                         }
105
106                         if (tm.tm_mon == month-1) {
107                                 snprintf(url, sizeof url, "readfwd?calview=day&year=%d&month=%d&day=%d", 
108                                         tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday);
109                                 wprintf("<td><a href=\"%s\">%d</a></td>", url, tm.tm_mday);
110                         }
111                         else {
112                                 wprintf("<td> </td>");
113                         }
114
115                         /* After displaying one week, end the row */
116                         if ((i % 7) == 6) {
117                                 wprintf("</tr>\n");
118                         }
119
120                 }
121
122                 thetime += (time_t)86400;               /* ahead 24 hours */
123         }
124
125         wprintf("</table>"                      /* end of inner table */
126                 "</div>\n");
127
128         StrBufAppendPrintf(WC->trailing_javascript,
129                 "       function minical_change_month(year, month) {                                    \n"
130                 "               p = 'year=' + year + '&month=' + month                                  \n"
131                 "                       + '&r=' + CtdlRandomString();                                   \n"
132                 "               new Ajax.Updater('%s', 'mini_calendar',                                 \n"
133                 "                       { method: 'get', parameters: p, evalScripts: true } );          \n"
134                 "       }                                                                               \n"
135                 "",
136                 div_id
137         );
138
139 }
140
141 /*
142  * ajax embedder for the above mini calendar 
143  */
144 void ajax_mini_calendar(void) 
145 {
146         embeddable_mini_calendar( ibstr("year"), ibstr("month"));
147 }
148
149
150 /*
151  * Display one day of a whole month view of a calendar
152  */
153 void calendar_month_view_display_events(int year, int month, int day)
154 {
155         long hklen;
156         const char *HashKey;
157         void *vCal;
158         HashPos *Pos;
159         disp_cal *Cal;
160         icalproperty *p = NULL;
161         icalproperty *q = NULL;
162         struct icaltimetype t;
163         struct icaltimetype end_t;
164         struct icaltimetype today_start_t;
165         struct icaltimetype today_end_t;
166         struct icaltimetype today_t;
167         struct tm starting_tm;
168         struct tm ending_tm;
169         int all_day_event = 0;
170         int show_event = 0;
171         char buf[256];
172         wcsession *WCC = WC;
173         time_t tt;
174
175         if (GetCount(WCC->disp_cal_items) == 0) {
176                 wprintf("<br /><br /><br />\n");
177                 return;
178         }
179
180         /*
181          * Create an imaginary event which spans the 24 hours of today.  Any events which
182          * overlap with this one take place at least partially in this day.  We have to
183          * convert it from a struct tm in order to make it UTC.
184          */
185         memset(&starting_tm, 0, sizeof(struct tm));
186         starting_tm.tm_year = year - 1900;
187         starting_tm.tm_mon = month - 1;
188         starting_tm.tm_mday = day;
189         starting_tm.tm_hour = 0;
190         starting_tm.tm_min = 0;
191         today_start_t = icaltime_from_timet_with_zone(mktime(&starting_tm), 0, icaltimezone_get_utc_timezone());
192         today_start_t.is_utc = 1;
193
194         memset(&ending_tm, 0, sizeof(struct tm));
195         ending_tm.tm_year = year - 1900;
196         ending_tm.tm_mon = month - 1;
197         ending_tm.tm_mday = day;
198         ending_tm.tm_hour = 23;
199         ending_tm.tm_min = 59;
200         today_end_t = icaltime_from_timet_with_zone(mktime(&ending_tm), 0, icaltimezone_get_utc_timezone());
201         today_end_t.is_utc = 1;
202
203         /*
204          * Create another one without caring about the timezone for all day events.
205          */
206         today_t = icaltime_null_date();
207         today_t.year = year;
208         today_t.month = month;
209         today_t.day = day;
210
211         /*
212          * Now loop through our list of events to see which ones occur today.
213          */
214         Pos = GetNewHashPos(WCC->disp_cal_items, 0);
215         while (GetNextHashPos(WCC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
216                 Cal = (disp_cal*)vCal;
217                 all_day_event = 0;
218                 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
219                 if (q != NULL) {
220                         t = icalproperty_get_dtstart(q);
221                 }
222                 else {
223                         memset(&t, 0, sizeof t);
224                 }
225                 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTEND_PROPERTY);
226                 if (q != NULL) {
227                         end_t = icalproperty_get_dtend(q);
228                 }
229                 else {
230                         memset(&end_t, 0, sizeof end_t);
231                 }
232                 if (t.is_date) all_day_event = 1;
233
234                 if (all_day_event)
235                 {
236                         show_event = ical_ctdl_is_overlap(t, end_t, today_t, icaltime_null_time());
237                 }
238                 else
239                 {
240                         show_event = ical_ctdl_is_overlap(t, end_t, today_start_t, today_end_t);
241                 }
242
243                 /*
244                  * If we determined that this event occurs today, then display it.
245                  */
246                 if (show_event) {
247                         p = icalcomponent_get_first_property(Cal->cal, ICAL_SUMMARY_PROPERTY);
248                         if (p == NULL) {
249                                 p = icalproperty_new_summary(_("Untitled Event"));
250                                 icalcomponent_add_property(Cal->cal, p);
251                         }
252                         if (p != NULL) {
253
254                                 if (all_day_event) {
255                                         wprintf("<table border=0 cellpadding=2><TR>"
256                                                 "<td bgcolor=\"#CCCCDD\">"
257                                                 );
258                                 }
259
260                                 wprintf("<font size=\"-1\">"
261                                         "<a class=\"event%s\" href=\"display_edit_event?"
262                                         "msgnum=%ld?calview=month?year=%d?month=%d?day=%d\""
263                                         " btt_tooltext=\"",
264                                         (Cal->unread)?"_unread":"_read",
265                                         Cal->cal_msgnum,
266                                         year, month, day
267                                         );
268
269                                 wprintf("<i>%s: %s</i><br />", _("From"), Cal->from);
270                                 wprintf("<i>%s</i> ",          _("Summary:"));
271                                 escputs((char *)icalproperty_get_comment(p));
272                                 wprintf("<br />");
273                                 
274                                 q = icalcomponent_get_first_property(
275                                         Cal->cal,
276                                         ICAL_LOCATION_PROPERTY);
277                                 if (q) {
278                                         wprintf("<i>%s</i> ", _("Location:"));
279                                         escputs((char *)icalproperty_get_comment(q));
280                                         wprintf("<br />");
281                                 }
282                                 
283                                 /*
284                                  * Only show start/end times if we're actually looking at the VEVENT
285                                  * component.  Otherwise it shows bogus dates for e.g. timezones
286                                  */
287                                 if (icalcomponent_isa(Cal->cal) == ICAL_VEVENT_COMPONENT) {
288                                         
289                                         q = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
290                                         if (q != NULL) {
291                                                 int no_end = 0;
292
293                                                 t = icalproperty_get_dtstart(q);
294                                                 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTEND_PROPERTY);
295                                                 if (q != NULL) {
296                                                         end_t = icalproperty_get_dtend(q);
297                                                 }
298                                                 else {
299                                                         /*
300                                                          * events with starting date/time equal
301                                                          * ending date/time might get only
302                                                          * DTSTART but no DTEND
303                                                          */
304                                                         no_end = 1;
305                                                 }
306
307                                                 if (t.is_date) {
308                                                         /* all day event */
309                                                         struct tm d_tm;
310
311                                                         if (!no_end) {
312                                                                 /* end given, have to adjust it */
313                                                                 icaltime_adjust(&end_t, -1, 0, 0, 0);
314                                                         }
315                                                         memset(&d_tm, 0, sizeof d_tm);
316                                                         d_tm.tm_year = t.year - 1900;
317                                                         d_tm.tm_mon = t.month - 1;
318                                                         d_tm.tm_mday = t.day;
319                                                         wc_strftime(buf, sizeof buf, "%x", &d_tm);
320
321                                                         if (no_end || !icaltime_compare(t, end_t)) {
322                                                                 wprintf("<i>%s</i> %s<br>",
323                                                                         _("Date:"), buf);
324                                                         }
325                                                         else {
326                                                                 wprintf("<i>%s</i> %s<br>",
327                                                                         _("Starting date:"), buf);
328                                                                 d_tm.tm_year = end_t.year - 1900;
329                                                                 d_tm.tm_mon = end_t.month - 1;
330                                                                 d_tm.tm_mday = end_t.day;
331                                                                 wc_strftime(buf, sizeof buf, "%x", &d_tm);
332                                                                 wprintf("<i>%s</i> %s<br>",
333                                                                         _("Ending date:"), buf);
334                                                         }
335                                                 }
336                                                 else {
337                                                         tt = icaltime_as_timet(t);
338                                                         webcit_fmt_date(buf, 256, tt, DATEFMT_BRIEF);
339                                                         if (no_end || !icaltime_compare(t, end_t)) {
340                                                                 wprintf("<i>%s</i> %s<br>",
341                                                                         _("Date/time:"), buf);
342                                                         }
343                                                         else {
344                                                                 wprintf("<i>%s</i> %s<br>",
345                                                                         _("Starting date/time:"), buf);
346                                                                 tt = icaltime_as_timet(end_t);
347                                                                 webcit_fmt_date(buf, 256, tt, DATEFMT_BRIEF);
348                                                                 wprintf("<i>%s</i> %s<br>", _("Ending date/time:"), buf);
349                                                         }
350                                                         
351                                                 }
352                                         }
353                                         
354                                 }
355                                 
356                                 q = icalcomponent_get_first_property(Cal->cal, ICAL_DESCRIPTION_PROPERTY);
357                                 if (q) {
358                                         wprintf("<i>%s</i> ", _("Notes:"));
359                                         escputs((char *)icalproperty_get_comment(q));
360                                         wprintf("<br />");
361                                 }
362                                 
363                                 wprintf("\">");
364                                 escputs((char *)
365                                         icalproperty_get_comment(p));
366                                 wprintf("</a></font><br />\n");
367                                 
368                                 if (all_day_event) {
369                                         wprintf("</td></tr></table>");
370                                 }
371                                 
372                         }
373                         
374                 }
375                 
376                 
377         }
378         DeleteHashPos(&Pos);
379 }
380
381
382 /*
383  * Display one day of a whole month view of a calendar
384  */
385 void calendar_month_view_brief_events(time_t thetime, const char *daycolor) {
386         long hklen;
387         const char *HashKey;
388         void *vCal;
389         HashPos *Pos;
390         time_t event_tt;
391         time_t event_tts;
392         time_t event_tte;
393         wcsession *WCC = WC;
394         struct tm event_tms;
395         struct tm event_tme;
396         struct tm today_tm;
397         icalproperty *p;
398         icalproperty *e;
399         struct icaltimetype t;
400         disp_cal *Cal;
401         int month, day, year;
402         int all_day_event = 0;
403         char *timeformat;
404         int time_format;
405         
406         time_format = get_time_format_cached ();
407
408         if (time_format == WC_TIMEFORMAT_24) timeformat="%k:%M";
409         else timeformat="%I:%M %p";
410
411         localtime_r(&thetime, &today_tm);
412         month = today_tm.tm_mon + 1;
413         day = today_tm.tm_mday;
414         year = today_tm.tm_year + 1900;
415
416         Pos = GetNewHashPos(WCC->disp_cal_items, 0);
417         while (GetNextHashPos(WCC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
418                 Cal = (disp_cal*)vCal;
419                 p = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
420                 if (p != NULL) {
421                         t = icalproperty_get_dtstart(p);
422                         event_tt = icaltime_as_timet(t);
423                         event_tts=event_tt;
424                         if (t.is_date) all_day_event = 1;
425                         else all_day_event = 0;
426
427                         if (all_day_event) {
428                                 gmtime_r(&event_tts, &event_tms);
429                         }
430                         else {
431                                 localtime_r(&event_tts, &event_tms);
432                         }
433                         /* \todo epoch &! daymask */
434                         if ((event_tms.tm_year == today_tm.tm_year)
435                                 && (event_tms.tm_mon == today_tm.tm_mon)
436                         && (event_tms.tm_mday == today_tm.tm_mday)) {
437                         
438                         
439                         char sbuf[255];
440                         char ebuf[255];
441                         
442                         p = icalcomponent_get_first_property(
443                                 Cal->cal,
444                                 ICAL_SUMMARY_PROPERTY);
445                         if (p == NULL) {
446                                 p = icalproperty_new_summary(_("Untitled Event"));
447                                 icalcomponent_add_property(Cal->cal, p);
448                         }
449                         e = icalcomponent_get_first_property(
450                                 Cal->cal, 
451                                 ICAL_DTEND_PROPERTY);
452                         if ((p != NULL) && (e != NULL)) {
453                                 time_t difftime;
454                                 int hours, minutes;
455                                 t = icalproperty_get_dtend(e);
456                                 event_tte = icaltime_as_timet(t);
457                                 localtime_r(&event_tte, &event_tme);
458                                 difftime=(event_tte-event_tts)/60;
459                                 hours=(int)(difftime / 60);
460                                 minutes=difftime % 60;
461                                 wprintf("<tr><td bgcolor='%s'>%i:%2i</td><td bgcolor='%s'>"
462                                         "<font size=\"-1\">"
463                                         "<a class=\"event%s\" href=\"display_edit_event?msgnum=%ld?calview=calbrief?year=%s?month=%s?day=%s\">",
464                                         daycolor,
465                                         hours, minutes,
466                                         (Cal->unread)?"_unread":"_read",                                                
467                                         daycolor,
468                                         Cal->cal_msgnum,
469                                         bstr("year"),
470                                         bstr("month"),
471                                         bstr("day")
472                                         );
473                                 
474                                 escputs((char *)
475                                         icalproperty_get_comment(p));
476                                 /* \todo: allso ammitime format */
477                                 wc_strftime(&sbuf[0], sizeof(sbuf), timeformat, &event_tms);
478                                 wc_strftime(&ebuf[0], sizeof(sbuf), timeformat, &event_tme);
479                                 
480                                 wprintf("</a></font></td>"
481                                         "<td bgcolor='%s'>%s</td><td bgcolor='%s'>%s</td></tr>",
482                                         daycolor,
483                                         sbuf,
484                                         daycolor,
485                                         ebuf);
486                                 }
487                         
488                         }
489                         
490                         
491                 }
492         }
493         DeleteHashPos(&Pos);
494 }
495
496
497 /*
498  * view one month. pretty view
499  */
500 void calendar_month_view(int year, int month, int day) {
501         struct tm starting_tm;
502         struct tm tm;
503         time_t thetime;
504         int i;
505         time_t previous_month;
506         time_t next_month;
507         time_t colheader_time;
508         struct tm colheader_tm;
509         char colheader_label[32];
510         long weekstart = 0;
511
512         /*
513          * Determine what day to start.  If an impossible value is found, start on Sunday.
514          */
515         get_pref_long("weekstart", &weekstart, 17);
516         if (weekstart > 6) weekstart = 0;
517
518         /*
519          * Now back up to the 1st of the month...
520          */
521         memset(&starting_tm, 0, sizeof(struct tm));
522
523         starting_tm.tm_year = year - 1900;
524         starting_tm.tm_mon = month - 1;
525         starting_tm.tm_mday = day;
526         thetime = mktime(&starting_tm);
527
528         memcpy(&tm, &starting_tm, sizeof(struct tm));
529         while (tm.tm_mday != 1) {
530                 thetime = thetime - (time_t)86400;      /* go back 24 hours */
531                 localtime_r(&thetime, &tm);
532         }
533
534         /* Determine previous and next months ... for links */
535         previous_month = thetime - (time_t)864000L;     /* back 10 days */
536         next_month = thetime + (time_t)(31L * 86400L);  /* ahead 31 days */
537
538         /* Now back up until we're on the user's preferred start day */
539         localtime_r(&thetime, &tm);
540         while (tm.tm_wday != weekstart) {
541                 thetime = thetime - (time_t)86400;      /* go back 24 hours */
542                 localtime_r(&thetime, &tm);
543         }
544
545         /* Outer table (to get the background color) */
546         wprintf("<div class=\"fix_scrollbar_bug\">"
547                 "<table class=\"calendar\"> \n <tr><td>"); 
548
549         wprintf("<table width=100%% border=0 cellpadding=0 cellspacing=0><tr>\n");
550
551         wprintf("<td align=center>");
552
553         localtime_r(&previous_month, &tm);
554         wprintf("<a href=\"readfwd?calview=month?year=%d?month=%d?day=1\">",
555                 (int)(tm.tm_year)+1900, tm.tm_mon + 1);
556         wprintf("<img align=middle src=\"static/prevdate_32x.gif\" border=0></A>\n");
557
558         wc_strftime(colheader_label, sizeof colheader_label, "%B", &starting_tm);
559         wprintf("&nbsp;&nbsp;"
560                 "<font size=+1 color=\"#FFFFFF\">"
561                 "%s %d"
562                 "</font>"
563                 "&nbsp;&nbsp;", colheader_label, year);
564
565         localtime_r(&next_month, &tm);
566         wprintf("<a href=\"readfwd?calview=month?year=%d?month=%d?day=1\">",
567                 (int)(tm.tm_year)+1900, tm.tm_mon + 1);
568         wprintf("<img align=middle src=\"static/nextdate_32x.gif\" border=0></A>\n");
569
570         wprintf("</td></tr></table>\n");
571
572         /* Inner table (the real one) */
573         wprintf("<table width=100%% border=0 cellpadding=1 cellspacing=1 "
574                 "bgcolor=#204B78 id=\"inner_month\"><tr>");
575         colheader_time = thetime;
576         for (i=0; i<7; ++i) {
577                 colheader_time = thetime + (i * 86400) ;
578                 localtime_r(&colheader_time, &colheader_tm);
579                 wc_strftime(colheader_label, sizeof colheader_label, "%A", &colheader_tm);
580                 wprintf("<th align=center width=14%%>"
581                         "<font color=\"#FFFFFF\">%s</font></th>", colheader_label);
582
583         }
584         wprintf("</tr>\n");
585
586
587         /* Now do 35 or 42 days */
588         localtime_r(&thetime, &tm);
589         for (i = 0; i<42; ++i) {
590
591                 /* Before displaying the first day of the week, start a new row */
592                 if ((i % 7) == 0) {
593                         wprintf("<tr>");
594                 }
595
596                 wprintf("<td class=\"cal%s\"><div class=\"day\">",
597                         ((tm.tm_mon != month-1) ? "out" :
598                                 ((tm.tm_wday==0 || tm.tm_wday==6) ? "weekend" :
599                                         "day"))
600                         );
601                 if ((i==0) || (tm.tm_mday == 1)) {
602                         wc_strftime(colheader_label, sizeof colheader_label, "%B", &tm);
603                         wprintf("%s ", colheader_label);
604                 }
605                 wprintf("<a href=\"readfwd?calview=day?year=%d?month=%d?day=%d\">"
606                         "%d</a></div>",
607                         tm.tm_year + 1900,
608                         tm.tm_mon + 1,
609                         tm.tm_mday,
610                         tm.tm_mday);
611
612                 /* put the data here, stupid */
613                 calendar_month_view_display_events(
614                         tm.tm_year + 1900,
615                         tm.tm_mon + 1,
616                         tm.tm_mday
617                         );
618
619                 wprintf("</td>");
620
621                 /* After displaying the last day of the week, end the row */
622                 if ((i % 7) == 6) {
623                         wprintf("</tr>\n");
624                 }
625
626                 thetime += (time_t)86400;               /* ahead 24 hours */
627                 localtime_r(&thetime, &tm);
628
629                 if ( ((i % 7) == 6) && (tm.tm_mon != month-1) && (tm.tm_mday < 15) ) {
630                         i = 100;        /* break out of the loop */
631                 }
632         }
633
634         wprintf("</table>"                      /* end of inner table */
635                 "</td></tr></table>"            /* end of outer table */
636                 "</div>\n");
637
638         /*
639          * Initialize the bubble tooltips.
640          *
641          * Yes, this is as stupid as it looks.  Instead of just making the call
642          * to btt_enableTooltips() straight away, we have to create a timer event
643          * and let it initialize as an event after 1 millisecond.  This is to
644          * work around a bug in Internet Explorer that causes it to crash if we
645          * manipulate the innerHTML of various DOM nodes while the page is still
646          * being rendered.  See http://www.shaftek.org/blog/archives/000212.html
647          * for more information.
648          */ 
649         StrBufAppendPrintf(WC->trailing_javascript,
650                 " setTimeout(\"btt_enableTooltips('inner_month')\", 1); \n"
651         );
652 }
653
654 /*
655  * view one month. brief view
656  */
657 void calendar_brief_month_view(int year, int month, int day) {
658         struct tm starting_tm;
659         struct tm tm;
660         time_t thetime;
661         int i;
662         time_t previous_month;
663         time_t next_month;
664         char month_label[32];
665
666         /* Determine what day to start.
667          * First, back up to the 1st of the month...
668          */
669         memset(&starting_tm, 0, sizeof(struct tm));
670         starting_tm.tm_year = year - 1900;
671         starting_tm.tm_mon = month - 1;
672         starting_tm.tm_mday = day;
673         thetime = mktime(&starting_tm);
674
675         memcpy(&tm, &starting_tm, sizeof(struct tm));
676         while (tm.tm_mday != 1) {
677                 thetime = thetime - (time_t)86400;      /* go back 24 hours */
678                 localtime_r(&thetime, &tm);
679         }
680
681         /* Determine previous and next months ... for links */
682         previous_month = thetime - (time_t)864000L;     /* back 10 days */
683         next_month = thetime + (time_t)(31L * 86400L);  /* ahead 31 days */
684
685         /* Now back up until we're on a Sunday */
686         localtime_r(&thetime, &tm);
687         while (tm.tm_wday != 0) {
688                 thetime = thetime - (time_t)86400;      /* go back 24 hours */
689                 localtime_r(&thetime, &tm);
690         }
691
692         /* Outer table (to get the background color) */
693         wprintf("<div class=\"fix_scrollbar_bug\">"
694                 "<table width=100%% border=0 cellpadding=0 cellspacing=0 "
695                 "bgcolor=#204B78><TR><TD>\n");
696
697         wprintf("<table width=100%% border=0 cellpadding=0 cellspacing=0><tr>\n");
698
699         wprintf("<td align=center>");
700
701         localtime_r(&previous_month, &tm);
702         wprintf("<a href=\"readfwd?calview=month?year=%d?month=%d?day=1\">",
703                 (int)(tm.tm_year)+1900, tm.tm_mon + 1);
704         wprintf("<img align=middle src=\"static/prevdate_32x.gif\" border=0></A>\n");
705
706         wc_strftime(month_label, sizeof month_label, "%B", &tm);
707         wprintf("&nbsp;&nbsp;"
708                 "<font size=+1 color=\"#FFFFFF\">"
709                 "%s %d"
710                 "</font>"
711                 "&nbsp;&nbsp;", month_label, year);
712
713         localtime_r(&next_month, &tm);
714         wprintf("<a href=\"readfwd?calview=month?year=%d?month=%d?day=1\">",
715                 (int)(tm.tm_year)+1900, tm.tm_mon + 1);
716         wprintf("<img align=middle src=\"static/nextdate_32x.gif\" border=0></A>\n");
717
718         wprintf("</td></tr></table>\n");
719
720         /* Inner table (the real one) */
721         wprintf("<table width=100%% border=0 cellpadding=1 cellspacing=1 "
722                 "bgcolor=#EEEECC><TR>");
723         wprintf("</tr>\n");
724         wprintf("<tr><td colspan=\"100%%\">\n");
725
726         /* Now do 35 days */
727         for (i = 0; i < 35; ++i) {
728                 char weeknumber[255];
729                 char weekday_name[32];
730                 char *daycolor;
731                 localtime_r(&thetime, &tm);
732
733
734                 /* Before displaying Sunday, start a new CELL */
735                 if ((i % 7) == 0) {
736                         wc_strftime(&weeknumber[0], sizeof(weeknumber), "%U", &tm);
737                         wprintf("<table border='0' bgcolor=\"#EEEECC\" width='100%%'> <tr><th colspan='4'>%s %s</th></tr>"
738                                 "   <tr><td>%s</td><td width=70%%>%s</td><td>%s</td><td>%s</td></tr>\n",
739                                 _("Week"), 
740                                 weeknumber,
741                                 _("Hours"),
742                                 _("Subject"),
743                                 _("Start"),
744                                 _("End")
745                                 );
746                 }
747                 
748                 daycolor=((tm.tm_mon != month-1) ? "DDDDDD" :
749                         ((tm.tm_wday==0 || tm.tm_wday==6) ? "EEEECC" :
750                                 "FFFFFF"));
751                 
752                 /* Day Header */
753                 wc_strftime(weekday_name, sizeof weekday_name, "%A", &tm);
754                 wprintf("<tr><td bgcolor='%s' colspan='1' align='left'> %s,%i."
755                         "</td><td bgcolor='%s' colspan='3'><hr></td></tr>\n",
756                         daycolor,
757                         weekday_name,tm.tm_mday,
758                         daycolor);
759
760                 /* put the data of one day  here, stupid */
761                 calendar_month_view_brief_events(thetime, daycolor);
762
763
764                 /* After displaying Saturday, end the row */
765                 if ((i % 7) == 6) {
766                         wprintf("</td></tr></table>\n");
767                 }
768
769                 thetime += (time_t)86400;               /* ahead 24 hours */
770         }
771
772         wprintf("</table>"                      /* end of inner table */
773                 "</td></tr></table>"            /* end of outer table */
774                 "</div>\n");
775 }
776
777 /*
778  * Calendar week view -- not implemented yet, this is a stub function
779  */
780 void calendar_week_view(int year, int month, int day) {
781         wprintf("<center><i>week view FIXME</i></center><br />\n");
782 }
783
784
785 /*
786  * display one day
787  * Display events for a particular hour of a particular day.
788  * (Specify hour < 0 to show "all day" events)
789  *
790  * dstart and dend indicate which hours our "daytime" begins and end
791  */
792 void calendar_day_view_display_events(time_t thetime,
793         int year,
794         int month,
795         int day,
796         int notime_events,
797         int dstart,
798         int dend)
799 {
800         long hklen;
801         const char *HashKey;
802         void *vCal;
803         HashPos *Pos;
804         icalproperty *p = NULL;
805         icalproperty *q = NULL;
806         time_t event_tt;
807         time_t event_tte;
808         struct tm event_te;
809         struct tm event_tm;
810         int show_event = 0;
811         int all_day_event = 0;
812         int ongoing_event = 0;
813         wcsession *WCC = WC;
814         disp_cal *Cal;
815         struct icaltimetype t;
816         struct icaltimetype end_t;
817         struct icaltimetype today_start_t;
818         struct icaltimetype today_end_t;
819         struct icaltimetype today_t;
820         struct tm starting_tm;
821         struct tm ending_tm;
822         int top = 0;
823         int bottom = 0;
824         int gap = 1;
825         int startmin = 0;
826         int diffmin = 0;
827         int endmin = 0;
828
829         char buf[256];
830
831         if (GetCount(WCC->disp_cal_items) == 0) {
832                 /* nothing to display */
833                 return;
834         }
835
836         /* Create an imaginary event which spans the current day.  Any events which
837          * overlap with this one take place at least partially in this day.
838          */
839         memset(&starting_tm, 0, sizeof(struct tm));
840         starting_tm.tm_year = year - 1900;
841         starting_tm.tm_mon = month - 1;
842         starting_tm.tm_mday = day;
843         starting_tm.tm_hour = 0;
844         starting_tm.tm_min = 0;
845         today_start_t = icaltime_from_timet_with_zone(mktime(&starting_tm), 0, icaltimezone_get_utc_timezone());
846         today_start_t.is_utc = 1;
847
848         memset(&ending_tm, 0, sizeof(struct tm));
849         ending_tm.tm_year = year - 1900;
850         ending_tm.tm_mon = month - 1;
851         ending_tm.tm_mday = day;
852         ending_tm.tm_hour = 23;
853         ending_tm.tm_min = 59;
854         today_end_t = icaltime_from_timet_with_zone(mktime(&ending_tm), 0, icaltimezone_get_utc_timezone());
855         today_end_t.is_utc = 1;
856
857         /*
858          * Create another one without caring about the timezone for all day events.
859          */
860         today_t = icaltime_null_date();
861         today_t.year = year;
862         today_t.month = month;
863         today_t.day = day;
864
865         /* Now loop through our list of events to see which ones occur today.
866          */
867         Pos = GetNewHashPos(WCC->disp_cal_items, 0);
868         while (GetNextHashPos(WCC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
869                 Cal = (disp_cal*)vCal;
870
871                 all_day_event = 0;
872                 ongoing_event=0;
873
874                 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
875                 if (q != NULL) {
876                         t = icalproperty_get_dtstart(q);
877                         event_tt = icaltime_as_timet(t);
878                         localtime_r(&event_tt, &event_te);
879                 }
880                 else {
881                         memset(&t, 0, sizeof t);
882                 }
883
884                 if (t.is_date) all_day_event = 1;
885
886                 q = icalcomponent_get_first_property(Cal->cal, ICAL_DTEND_PROPERTY);
887                 if (q != NULL) {
888                         end_t = icalproperty_get_dtend(q);
889                 }
890                 else {
891                         /* no end given means end = start */
892                         memcpy(&end_t, &t, sizeof(struct icaltimetype));
893                 }
894
895                 if (all_day_event)
896                 {
897                         show_event = ical_ctdl_is_overlap(t, end_t, today_t, icaltime_null_time());
898                         if (icaltime_compare(t, end_t)) {
899                                 /*
900                                  * the end date is non-inclusive so adjust it by one
901                                  * day because our test is inclusive, note that a day is
902                                  * not to much because we are talking about all day
903                                  * events
904                                  */
905                                 icaltime_adjust(&end_t, -1, 0, 0, 0);
906                         }
907                 }
908                 else
909                 {
910                         show_event = ical_ctdl_is_overlap(t, end_t, today_start_t, today_end_t);
911                 }
912
913                 event_tte = icaltime_as_timet(end_t);
914                 localtime_r(&event_tte, &event_tm);
915
916                 /* If we determined that this event occurs today, then display it.
917                  */
918                 p = icalcomponent_get_first_property(Cal->cal,ICAL_SUMMARY_PROPERTY);
919                 if (p == NULL) {
920                         p = icalproperty_new_summary(_("Untitled Event"));
921                         icalcomponent_add_property(Cal->cal, p);
922                 }
923
924                 if ((show_event) && (p != NULL)) {
925
926                         if ((event_te.tm_mday != day) || (event_tm.tm_mday != day)) ongoing_event = 1; 
927
928                         if (all_day_event && notime_events)
929                         {
930                                 wprintf("<li class=\"event_framed%s\"> "
931                                         "<a href=\"display_edit_event?"
932                                         "msgnum=%ld?calview=day?year=%d?month=%d?day=%d\" "
933                                         " class=\"event_title\" "
934                                         " btt_tooltext=\"",
935                                         (Cal->unread)?"_unread":"_read",
936                                         Cal->cal_msgnum, year, month, day);
937                                 wprintf("<i>%s</i><br />",      _("All day event"));
938                                 wprintf("<i>%s: %s</i><br />",  _("From"), Cal->from);
939                                 wprintf("<i>%s</i> ",           _("Summary:"));
940                                 escputs((char *) icalproperty_get_comment(p));
941                                 wprintf("<br />");
942                                 q = icalcomponent_get_first_property(Cal->cal,ICAL_LOCATION_PROPERTY);
943                                 if (q) {
944                                         wprintf("<i>%s</i> ", _("Location:"));
945                                         escputs((char *)icalproperty_get_comment(q));
946                                         wprintf("<br />");
947                                 }
948                                 if (!icaltime_compare(t, end_t)) { /* one day only */
949                                         webcit_fmt_date(buf, 256, event_tt, DATEFMT_LOCALEDATE);
950                                         wprintf("<i>%s</i> %s<br>", _("Date:"), buf);
951                                 }
952                                 else {
953                                         webcit_fmt_date(buf, 256, event_tt, DATEFMT_LOCALEDATE);
954                                         wprintf("<i>%s</i> %s<br>", _("Starting date:"), buf);
955                                         webcit_fmt_date(buf, 256, event_tte, DATEFMT_LOCALEDATE);
956                                         wprintf("<i>%s</i> %s<br>", _("Ending date:"), buf);
957                                 }
958                                 q = icalcomponent_get_first_property(Cal->cal,ICAL_DESCRIPTION_PROPERTY);
959                                 if (q) {
960                                         wprintf("<i>%s</i> ", _("Notes:"));
961                                         escputs((char *)icalproperty_get_comment(q));
962                                         wprintf("<br />");
963                                 }
964                                 wprintf("\">");
965                                 escputs((char *) icalproperty_get_comment(p));
966                                 wprintf("</a> <span>(");
967                                 wprintf(_("All day event"));
968                                 wprintf(")</span></li>\n");
969                         }
970                         else if (ongoing_event && notime_events) 
971                         {
972                                 wprintf("<li class=\"event_framed%s\"> "
973                                         "<a href=\"display_edit_event?"
974                                         "msgnum=%ld&calview=day?year=%d?month=%d?day=%d\" "
975                                         " class=\"event_title\" " 
976                                         "btt_tooltext=\"",
977                                         (Cal->unread)?"_unread":"_read",
978                                         Cal->cal_msgnum, year, month, day);
979                                 wprintf("<i>%s</i><br />",     _("Ongoing event"));
980                                 wprintf("<i>%s: %s</i><br />", _("From"), Cal->from);
981                                 wprintf("<i>%s</i> ",          _("Summary:"));
982                                 escputs((char *) icalproperty_get_comment(p));
983                                 wprintf("<br />");
984                                 q = icalcomponent_get_first_property(Cal->cal,ICAL_LOCATION_PROPERTY);
985                                 if (q) {
986                                         wprintf("<i>%s</i> ", _("Location:"));
987                                         escputs((char *)icalproperty_get_comment(q));
988                                         wprintf("<br />");
989                                                                 }
990                                 webcit_fmt_date(buf, 256, event_tt, DATEFMT_BRIEF);
991                                 wprintf("<i>%s</i> %s<br>", _("Starting date/time:"), buf);
992                                 webcit_fmt_date(buf, 256, event_tte, DATEFMT_BRIEF);
993                                 wprintf("<i>%s</i> %s<br>", _("Ending date/time:"), buf);
994                                 q = icalcomponent_get_first_property(Cal->cal,ICAL_DESCRIPTION_PROPERTY);
995                                 if (q) {
996                                         wprintf("<i>%s</i> ", _("Notes:"));
997                                         escputs((char *)icalproperty_get_comment(q));
998                                         wprintf("<br />");
999                                 }
1000                                 wprintf("\">");
1001                                 escputs((char *) icalproperty_get_comment(p));
1002                                 wprintf("</a> <span>(");
1003                                 wprintf(_("Ongoing event"));
1004                                 wprintf(")</span></li>\n");
1005                         }
1006                         else if (!all_day_event && !notime_events)
1007                         {
1008                                 gap++;
1009
1010                                 if (event_te.tm_mday != day) event_te.tm_hour = 0;
1011                                 if (event_tm.tm_mday != day) event_tm.tm_hour = 24;
1012
1013                                 /* Calculate the location of the top of the box */
1014                                 if (event_te.tm_hour < dstart) {
1015                                         startmin = diffmin = event_te.tm_min / 6;
1016                                         top = (event_te.tm_hour * EXTRATIMELINE) + startmin;
1017                                 }
1018                                 else if ((event_te.tm_hour >= dstart) && (event_te.tm_hour <= dend)) {
1019                                         startmin = diffmin = (event_te.tm_min / 2);
1020                                         top = (dstart * EXTRATIMELINE) + ((event_te.tm_hour - dstart) * TIMELINE) + startmin;
1021                                 }
1022                                 else if (event_te.tm_hour >dend) {
1023                                         startmin = diffmin = event_te.tm_min / 6;
1024                                         top = (dstart * EXTRATIMELINE) + ((dend - dstart - 1) * TIMELINE) + ((event_tm.tm_hour - dend + 1) * EXTRATIMELINE) + startmin ;
1025                                 }
1026                                 else {
1027                                         /* should never get here */
1028                                 }
1029
1030                                 /* Calculate the location of the bottom of the box */
1031                                 if (event_tm.tm_hour < dstart) {
1032                                         endmin = diffmin = event_tm.tm_min / 6;
1033                                         bottom = (event_tm.tm_hour * EXTRATIMELINE) + endmin;
1034                                 }
1035                                 else if ((event_tm.tm_hour >= dstart) && (event_tm.tm_hour <= dend)) {
1036                                         endmin = diffmin = (event_tm.tm_min / 2);
1037                                         bottom = (dstart * EXTRATIMELINE) + ((event_tm.tm_hour - dstart) * TIMELINE) + endmin ;
1038                                 }
1039                                 else if (event_tm.tm_hour >dend) {
1040                                         endmin = diffmin = event_tm.tm_min / 6;
1041                                         bottom = (dstart * EXTRATIMELINE) + ((dend - dstart + 1) * TIMELINE) + ((event_tm.tm_hour - dend - 1) * EXTRATIMELINE) + endmin;
1042                                 }
1043                                 else {
1044                                         /* should never get here */
1045                                 }
1046
1047                                 wprintf("<dd  class=\"event_framed%s\" "
1048                                         "style=\"position: absolute; "
1049                                         "top:%dpx; left:%dpx; "
1050                                         "height:%dpx; \" >",
1051                                         (Cal->unread)?"_unread":"_read",
1052                                         top, (gap * 40), (bottom-top)
1053                                         );
1054                                 wprintf("<a href=\"display_edit_event?"
1055                                         "msgnum=%ld?calview=day?year=%d?month=%d?day=%d?hour=%d\" "
1056                                         "class=\"event_title\" "
1057                                         "btt_tooltext=\"",
1058                                         Cal->cal_msgnum, year, month, day, t.hour);
1059                                 wprintf("<i>%s: %s</i><br />", _("From"), Cal->from);
1060                                 wprintf("<i>%s</i> ",          _("Summary:"));
1061                                 escputs((char *) icalproperty_get_comment(p));
1062                                 wprintf("<br />");
1063                                 q = icalcomponent_get_first_property(Cal->cal,ICAL_LOCATION_PROPERTY);
1064                                 if (q) {
1065                                         wprintf("<i>%s</i> ", _("Location:"));
1066                                         escputs((char *)icalproperty_get_comment(q));
1067                                         wprintf("<br />");
1068                                                                 }
1069                                 if (!icaltime_compare(t, end_t)) { /* one day only */
1070                                         webcit_fmt_date(buf, 256, event_tt, DATEFMT_BRIEF);
1071                                         wprintf("<i>%s</i> %s<br>", _("Date/time:"), buf);
1072                                 }
1073                                 else {
1074                                         webcit_fmt_date(buf, 256, event_tt, DATEFMT_BRIEF);
1075                                         wprintf("<i>%s</i> %s<br>", _("Starting date/time:"), buf);
1076                                         webcit_fmt_date(buf, 256, event_tte, DATEFMT_BRIEF);
1077                                         wprintf("<i>%s</i> %s<br>", _("Ending date/time:"), buf);
1078                                 }
1079                                 q = icalcomponent_get_first_property(Cal->cal,ICAL_DESCRIPTION_PROPERTY);
1080                                 if (q) {
1081                                         wprintf("<i>%s</i> ", _("Notes:"));
1082                                         escputs((char *)icalproperty_get_comment(q));
1083                                         wprintf("<br />");
1084                                 }
1085                                 wprintf("\">");
1086
1087                                 escputs((char *) icalproperty_get_comment(p));
1088                                 wprintf("</a></dd>\n");
1089                         }
1090                 }
1091         }
1092         DeleteHashPos(&Pos);
1093 }
1094
1095 /*
1096  * view one day
1097  */
1098 void calendar_day_view(int year, int month, int day) {
1099         int hour;
1100         struct icaltimetype today, yesterday, tomorrow;
1101         long daystart;
1102         long dayend;
1103         struct tm d_tm;
1104         char d_str[128];
1105         int time_format;
1106         time_t today_t;
1107         int timeline = TIMELINE;
1108         int extratimeline = EXTRATIMELINE;
1109         int gap = 0;
1110         int hourlabel;
1111         int extrahourlabel;
1112
1113         time_format = get_time_format_cached ();
1114         get_pref_long("daystart", &daystart, 8);
1115         get_pref_long("dayend", &dayend, 17);
1116
1117         /* when loading daystart/dayend, replace missing, corrupt, or impossible values with defaults */
1118         if ((daystart < 0) || (dayend < 2) || (daystart >= 23) || (dayend > 23) || (dayend <= daystart)) {
1119                 daystart = 9;
1120                 dayend = 17;
1121         }
1122         
1123         /* Today's date */
1124         memset(&d_tm, 0, sizeof d_tm);
1125         d_tm.tm_year = year - 1900;
1126         d_tm.tm_mon = month - 1;
1127         d_tm.tm_mday = day;
1128         today_t = mktime(&d_tm); 
1129
1130         /* Figure out the dates for "yesterday" and "tomorrow" links */
1131
1132         memset(&today, 0, sizeof(struct icaltimetype));
1133         today.year = year;
1134         today.month = month;
1135         today.day = day;
1136         today.is_date = 1;
1137
1138         memcpy(&yesterday, &today, sizeof(struct icaltimetype));
1139         --yesterday.day;
1140         yesterday = icaltime_normalize(yesterday);
1141
1142         memcpy(&tomorrow, &today, sizeof(struct icaltimetype));
1143         ++tomorrow.day;
1144         tomorrow = icaltime_normalize(tomorrow);
1145
1146         wprintf("<div class=\"fix_scrollbar_bug\">");
1147
1148         /* Inner table (the real one) */
1149         wprintf("<table class=\"calendar\" id=\"inner_day\"><tr> \n");
1150
1151         /* Innermost cell (contains hours etc.) */
1152         wprintf("<td class=\"events_of_the_day\" >");
1153         wprintf("<dl class=\"events\" >");
1154
1155         /* Now the middle of the day... */
1156
1157         extrahourlabel = extratimeline - 2;
1158         hourlabel = extrahourlabel * 150 / 100;
1159         if (hourlabel > (timeline - 2)) hourlabel = timeline - 2;
1160
1161         for (hour = 0; hour < daystart; ++hour) {       /* could do HEIGHT=xx */
1162                 wprintf("<dt class=\"extrahour\"        "
1163                         "style=\"               "
1164                         "position: absolute;    "
1165                         "top: %dpx; left: 0px;  "
1166                         "height: %dpx;          "
1167                         "font-size: %dpx;       "
1168                         "\" >                   "
1169                         "<a href=\"display_edit_event?msgnum=0"
1170                         "?calview=day?year=%d?month=%d?day=%d?hour=%d?minute=0\">",
1171                         (hour * extratimeline ),
1172                         extratimeline,
1173                         extrahourlabel,
1174                         year, month, day, hour
1175                         );
1176
1177                 if (time_format == WC_TIMEFORMAT_24) {
1178                         wprintf("%2d:00</a> ", hour);
1179                 }
1180                 else {
1181                         wprintf("%d:00%s</a> ",
1182                                 ((hour == 0) ? 12 : (hour <= 12 ? hour : hour-12)),
1183                                 (hour < 12 ? "am" : "pm")
1184                                 );
1185                 }
1186
1187                 wprintf("</dt>");
1188         }
1189
1190         gap = daystart * extratimeline;
1191
1192         for (hour = daystart; hour <= dayend; ++hour) {       /* could do HEIGHT=xx */
1193                 wprintf("<dt class=\"hour\"     "
1194                         "style=\"               "
1195                         "position: absolute;    "
1196                         "top: %ldpx; left: 0px; "
1197                         "height: %dpx;          "
1198                         "font-size: %dpx;       "
1199                         "\" >                   "
1200                         "<a href=\"display_edit_event?msgnum=0?calview=day"
1201                         "?year=%d?month=%d?day=%d?hour=%d?minute=0\">",
1202                         gap + ((hour - daystart) * timeline ),
1203                         timeline,
1204                         hourlabel,
1205                         year, month, day, hour
1206                         );
1207
1208                 if (time_format == WC_TIMEFORMAT_24) {
1209                         wprintf("%2d:00</a> ", hour);
1210                 }
1211                 else {
1212                         wprintf("%d:00%s</a> ",
1213                                 (hour <= 12 ? hour : hour-12),
1214                                 (hour < 12 ? "am" : "pm")
1215                                                 );
1216                 }
1217
1218                 wprintf("</dt>");
1219         }
1220
1221         gap = gap + ((dayend - daystart + 1) * timeline);
1222
1223         for (hour = (dayend + 1); hour < 24; ++hour) {       /* could do HEIGHT=xx */
1224                 wprintf("<dt class=\"extrahour\"     "
1225                         "style=\"               "
1226                         "position: absolute;    "
1227                         "top: %ldpx; left: 0px; "
1228                         "height: %dpx;          "
1229                         "font-size: %dpx;       "
1230                         "\" >                   "
1231                         "<a href=\"display_edit_event?msgnum=0?calview=day"
1232                         "?year=%d?month=%d?day=%d?hour=%d?minute=0\">",
1233                         gap + ((hour - dayend - 1) * extratimeline ),
1234                         extratimeline,
1235                         extrahourlabel,
1236                         year, month, day, hour
1237                 );
1238
1239                 if (time_format == WC_TIMEFORMAT_24) {
1240                         wprintf("%2d:00</a> ", hour);
1241                 }
1242                 else {
1243                         wprintf("%d:00%s</a> ",
1244                                 (hour <= 12 ? hour : hour-12),
1245                                 (hour < 12 ? "am" : "pm")
1246                         );
1247                 }
1248
1249                 wprintf("</dt>");
1250         }
1251
1252         /* Display events with start and end times on this day */
1253         calendar_day_view_display_events(today_t, year, month, day, 0, daystart, dayend);
1254
1255         wprintf("</dl>");
1256         wprintf("</td>");                       /* end of innermost table */
1257
1258         /* Display extra events (start/end times not present or not today) in the middle column */
1259         wprintf("<td class=\"extra_events\">");
1260
1261         wprintf("<ul>");
1262
1263         /* Display all-day events */
1264         calendar_day_view_display_events(today_t, year, month, day, 1, daystart, dayend);
1265
1266         wprintf("</ul>");
1267
1268         wprintf("</td>");       /* end extra on the middle */
1269
1270         wprintf("<td width=20%% align=center valign=top>");     /* begin stuff-on-the-right */
1271
1272         /* Begin todays-date-with-left-and-right-arrows */
1273         wprintf("<table border=0 width=100%% "
1274                 "cellspacing=0 cellpadding=0 bgcolor=\"#FFFFFF\">\n");
1275         wprintf("<tr>");
1276
1277         /* Left arrow */        
1278         wprintf("<td align=center>");
1279         wprintf("<a href=\"readfwd?calview=day?year=%d?month=%d?day=%d\">",
1280                 yesterday.year, yesterday.month, yesterday.day);
1281         wprintf("<img align=middle src=\"static/prevdate_32x.gif\" border=0></A>");
1282         wprintf("</td>");
1283
1284         wc_strftime(d_str, sizeof d_str,
1285                 "<td align=center>"
1286                 "<font size=+2>%B</font><br />"
1287                 "<font size=+3>%d</font><br />"
1288                 "<font size=+2>%Y</font><br />"
1289                 "</td>",
1290                 &d_tm
1291                 );
1292         wprintf("%s", d_str);
1293
1294         /* Right arrow */
1295         wprintf("<td align=center>");
1296         wprintf("<a href=\"readfwd?calview=day?year=%d?month=%d?day=%d\">",
1297                 tomorrow.year, tomorrow.month, tomorrow.day);
1298         wprintf("<img align=middle src=\"static/nextdate_32x.gif\""
1299                 " border=0></a>\n");
1300         wprintf("</td>");
1301
1302         wprintf("</tr></table>\n");
1303         /* End todays-date-with-left-and-right-arrows */
1304
1305         /* Embed a mini month calendar in this space */
1306         wprintf("<br />\n");
1307         embeddable_mini_calendar(year, month);
1308
1309         wprintf("</font></center>\n");
1310
1311         wprintf("</td></tr>");                  /* end stuff-on-the-right */
1312
1313         wprintf("</table>"                      /* end of inner table */
1314                 "</div>");
1315
1316         StrBufAppendPrintf(WC->trailing_javascript,
1317                 " setTimeout(\"btt_enableTooltips('inner_day')\", 1);   \n"
1318         );
1319 }
1320
1321
1322 /*
1323  * Display today's events.  Returns the number of items displayed.
1324  */
1325 int calendar_summary_view(void) {
1326         long hklen;
1327         const char *HashKey;
1328         void *vCal;
1329         HashPos *Pos;
1330         disp_cal *Cal;
1331         icalproperty *p;
1332         struct icaltimetype t;
1333         time_t event_tt;
1334         struct tm event_tm;
1335         struct tm today_tm;
1336         time_t now;
1337         int all_day_event = 0;
1338         char timestring[SIZ];
1339         wcsession *WCC = WC;
1340         int num_displayed = 0;
1341
1342         if (GetCount(WC->disp_cal_items) == 0) {
1343                 return(0);
1344         }
1345
1346         now = time(NULL);
1347         localtime_r(&now, &today_tm);
1348
1349         Pos = GetNewHashPos(WCC->disp_cal_items, 0);
1350         while (GetNextHashPos(WCC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
1351                 Cal = (disp_cal*)vCal;
1352                 p = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
1353                 if (p != NULL) {
1354                         t = icalproperty_get_dtstart(p);
1355                         event_tt = icaltime_as_timet(t);
1356                         if (t.is_date) {
1357                                 all_day_event = 1;
1358                         }
1359                         else {
1360                                 all_day_event = 0;
1361                         }
1362                         fmt_time(timestring, SIZ, event_tt);
1363
1364                         if (all_day_event) {
1365                                 gmtime_r(&event_tt, &event_tm);
1366                         }
1367                         else {
1368                                 localtime_r(&event_tt, &event_tm);
1369                         }
1370
1371                         if ( (event_tm.tm_year == today_tm.tm_year)
1372                                 && (event_tm.tm_mon == today_tm.tm_mon)
1373                                 && (event_tm.tm_mday == today_tm.tm_mday)
1374                         ) {
1375
1376                                 p = icalcomponent_get_first_property(Cal->cal, ICAL_SUMMARY_PROPERTY);
1377                                 if (p == NULL) {
1378                                         p = icalproperty_new_summary(_("Untitled Task"));
1379                                         icalcomponent_add_property(Cal->cal, p);
1380                                 }
1381                                 if (p != NULL) {
1382
1383
1384                                         if (WCC->wc_view == VIEW_TASKS) {
1385                                                 wprintf("<a href=\"display_edit_task"
1386                                                         "?msgnum=%ld"
1387                                                         "?return_to_summary=1"
1388                                                         "?gotofirst=",
1389                                                         Cal->cal_msgnum
1390                                                 );
1391                                                 escputs(ChrPtr(WCC->wc_roomname));
1392                                                 wprintf("\">");
1393                                         }
1394                                         else {
1395                                                 wprintf("<a href=\"display_edit_event"
1396                                                         "?msgnum=%ld"
1397                                                         "?calview=summary"
1398                                                         "?year=%d"
1399                                                         "?month=%d"
1400                                                         "?day=%d"
1401                                                         "?gotofirst=",
1402                                                         Cal->cal_msgnum,
1403                                                         today_tm.tm_year + 1900,
1404                                                         today_tm.tm_mon + 1,
1405                                                         today_tm.tm_mday
1406                                                 );
1407                                                 escputs(ChrPtr(WCC->wc_roomname));
1408                                                 wprintf("\">");
1409                                         }
1410                                         escputs((char *) icalproperty_get_comment(p));
1411                                         if (!all_day_event) {
1412                                                 wprintf(" (%s)", timestring);
1413                                         }
1414                                         wprintf("</a><br />\n");
1415                                         ++num_displayed;
1416                                 }
1417                         }
1418                 }
1419         }
1420         DeleteHashPos(&Pos);
1421         DeleteHash(&WC->disp_cal_items);
1422         return(num_displayed);
1423 }
1424
1425 /*
1426  * Parse the URL variables in order to determine the scope and display of a calendar view
1427  */
1428 void parse_calendar_view_request(struct calview *c) {
1429         time_t now;
1430         struct tm tm;
1431         char calview[32];
1432         int span = 3888000;
1433
1434         /* In case no date was specified, go with today */
1435         now = time(NULL);
1436         localtime_r(&now, &tm);
1437         c->year = tm.tm_year + 1900;
1438         c->month = tm.tm_mon + 1;
1439         c->day = tm.tm_mday;
1440
1441         /* Now see if a date was specified */
1442         if (havebstr("year")) c->year = ibstr("year");
1443         if (havebstr("month")) c->month = ibstr("month");
1444         if (havebstr("day")) c->day = ibstr("day");
1445
1446         /* How would you like that cooked? */
1447         if (havebstr("calview")) {
1448                 strcpy(calview, bstr("calview"));
1449         }
1450         else {
1451                 strcpy(calview, "month");
1452         }
1453
1454         /* Display the selected view */
1455         if (!strcasecmp(calview, "day")) {
1456                 c->view = calview_day;
1457         }
1458         else if (!strcasecmp(calview, "week")) {
1459                 c->view = calview_week;
1460         }
1461         else if (!strcasecmp(calview, "summary")) {     /* shouldn't ever happen, but just in case */
1462                 c->view = calview_day;
1463         }
1464         else {
1465                 if (WC->wc_view == VIEW_CALBRIEF) {
1466                         c->view = calview_brief;
1467                 }
1468                 else {
1469                         c->view = calview_month;
1470                 }
1471         }
1472
1473         /* Now try and set the lower and upper bounds so that we don't
1474          * burn too many cpu cycles parsing data way in the past or future
1475          */
1476
1477         tm.tm_year = c->year - 1900;
1478         tm.tm_mon = c->month - 1;
1479         tm.tm_mday = c->day;
1480         now = mktime(&tm);
1481
1482         if (c->view == calview_month)   span = 3888000;
1483         if (c->view == calview_brief)   span = 3888000;
1484         if (c->view == calview_week)    span = 604800;
1485         if (c->view == calview_day)     span = 86400;
1486         if (c->view == calview_summary) span = 86400;
1487
1488         c->lower_bound = now - span;
1489         c->upper_bound = now + span;
1490 }
1491
1492
1493
1494 /*
1495  * Render a calendar view from data previously loaded into memory
1496  */
1497 void render_calendar_view(struct calview *c)
1498 {
1499         if (c->view == calview_day) {
1500                 calendar_day_view(c->year, c->month, c->day);
1501         }
1502         else if (c->view == calview_week) {
1503                 calendar_week_view(c->year, c->month, c->day);
1504         }
1505         else {
1506                 if (WC->wc_view == VIEW_CALBRIEF) {
1507                         calendar_brief_month_view(c->year, c->month, c->day);
1508                 }
1509                 else {
1510                         calendar_month_view(c->year, c->month, c->day);
1511                 }
1512         }
1513
1514         /* Free the in-memory list of calendar items */
1515         DeleteHash(&WC->disp_cal_items);
1516 }
1517
1518
1519 /*
1520  * Helper function for do_tasks_view().  Returns the due date/time of a vtodo.
1521  */
1522 time_t get_task_due_date(icalcomponent *vtodo, int *is_date) {
1523         icalproperty *p;
1524
1525         if (vtodo == NULL) {
1526                 return(0L);
1527         }
1528
1529         /*
1530          * If we're looking at a fully encapsulated VCALENDAR
1531          * rather than a VTODO component, recurse into the data
1532          * structure until we get a VTODO.
1533          */
1534         if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) {
1535                 return get_task_due_date(
1536                         icalcomponent_get_first_component(
1537                                 vtodo, ICAL_VTODO_COMPONENT
1538                                 ), is_date
1539                         );
1540         }
1541
1542         p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY);
1543         if (p != NULL) {
1544                 struct icaltimetype t = icalproperty_get_due(p);
1545
1546                 if (is_date)
1547                         *is_date = t.is_date;
1548                 return(icaltime_as_timet(t));
1549         }
1550         else {
1551                 return(0L);
1552         }
1553 }
1554
1555
1556 /*
1557  * Compare the due dates of two tasks (this is for sorting)
1558  */
1559 int task_due_cmp(const void *vtask1, const void *vtask2) {
1560         disp_cal * Task1 = (disp_cal *)GetSearchPayload(vtask1);
1561         disp_cal * Task2 = (disp_cal *)GetSearchPayload(vtask2);
1562
1563         time_t t1;
1564         time_t t2;
1565
1566         t1 =  get_task_due_date(Task1->cal, NULL);
1567         t2 =  get_task_due_date(Task2->cal, NULL);
1568         if (t1 < t2) return(-1);
1569         if (t1 > t2) return(1);
1570         return(0);
1571 }
1572
1573 /*
1574  * qsort filter to move completed tasks to bottom of task list
1575  */
1576 int task_completed_cmp(const void *vtask1, const void *vtask2) {
1577         disp_cal * Task1 = (disp_cal *)GetSearchPayload(vtask1);
1578 /*      disp_cal * Task2 = (disp_cal *)GetSearchPayload(vtask2); */
1579
1580         icalproperty_status t1 = icalcomponent_get_status((Task1)->cal);
1581         /* icalproperty_status t2 = icalcomponent_get_status(((struct disp_cal *)task2)->cal); */
1582         
1583         if (t1 == ICAL_STATUS_COMPLETED) 
1584                 return 1;
1585         return 0;
1586 }
1587
1588
1589
1590 /*
1591  * do the whole task view stuff
1592  */
1593 void do_tasks_view(void) {
1594         long hklen;
1595         const char *HashKey;
1596         void *vCal;
1597         disp_cal *Cal;
1598         HashPos *Pos;
1599         int nItems;
1600         time_t due;
1601         char buf[SIZ];
1602         icalproperty *p;
1603         wcsession *WCC = WC;
1604
1605         wprintf("<div class=\"fix_scrollbar_bug\">"
1606                 "<table class=\"calendar_view_background\"><tbody id=\"taskview\">\n<tr>\n"
1607                 "<th>");
1608         wprintf(_("Completed?"));
1609         wprintf("</th><th>");
1610         wprintf(_("Name of task"));
1611         wprintf("</th><th>");
1612         wprintf(_("Date due"));
1613         wprintf("</th><th>");
1614         wprintf(_("Category"));
1615         wprintf(" (<select id=\"selectcategory\"><option value=\"showall\">%s</option></select>)</th></tr>\n",
1616                 _("Show All"));
1617
1618         nItems = GetCount(WC->disp_cal_items);
1619
1620         /* Sort them if necessary
1621         if (nItems > 1) {
1622                 SortByPayload(WC->disp_cal_items, task_due_cmp);
1623         }
1624         * this shouldn't be neccessary, since we sort by the start time.
1625         */
1626
1627         /* And then again, by completed */
1628         if (nItems > 1) {
1629                 SortByPayload(WC->disp_cal_items, 
1630                               task_completed_cmp);
1631         }
1632
1633         Pos = GetNewHashPos(WCC->disp_cal_items, 0);
1634         while (GetNextHashPos(WCC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
1635                 icalproperty_status todoStatus;
1636                 int is_date;
1637
1638                 Cal = (disp_cal*)vCal;
1639                 wprintf("<tr><td>");
1640                 todoStatus = icalcomponent_get_status(Cal->cal);
1641                 wprintf("<input type=\"checkbox\" name=\"completed\" value=\"completed\" ");
1642                 if (todoStatus == ICAL_STATUS_COMPLETED) {
1643                         wprintf("checked=\"checked\" ");
1644                 }
1645                 wprintf("disabled=\"disabled\">\n</td><td>");
1646                 p = icalcomponent_get_first_property(Cal->cal,
1647                         ICAL_SUMMARY_PROPERTY);
1648                 wprintf("<a href=\"display_edit_task?msgnum=%ld?taskrm=", Cal->cal_msgnum);
1649                 urlescputs(ChrPtr(WC->wc_roomname));
1650                 wprintf("\">");
1651                 /* wprintf("<img align=middle "
1652                 "src=\"static/taskmanag_16x.gif\" border=0>&nbsp;"); */
1653                 if (p != NULL) {
1654                         escputs((char *)icalproperty_get_comment(p));
1655                 }
1656                 wprintf("</a>\n");
1657                 wprintf("</td>\n");
1658
1659                 due = get_task_due_date(Cal->cal, &is_date);
1660                 wprintf("<td><span");
1661                 if (due > 0) {
1662                         webcit_fmt_date(buf, SIZ, due, is_date ? DATEFMT_RAWDATE : DATEFMT_FULL);
1663                         wprintf(">%s",buf);
1664                 }
1665                 else {
1666                         wprintf(">");
1667                 }
1668                 wprintf("</span></td>");
1669                 wprintf("<td>");
1670                 p = icalcomponent_get_first_property(Cal->cal,
1671                         ICAL_CATEGORIES_PROPERTY);
1672                 if (p != NULL) {
1673                         escputs((char *)icalproperty_get_categories(p));
1674                 }
1675                 wprintf("</td>");
1676                 wprintf("</tr>");
1677         }
1678
1679         wprintf("</tbody></table></div>\n");
1680
1681         /* Free the list */
1682         DeleteHash(&WC->disp_cal_items);
1683         DeleteHashPos(&Pos);
1684 }
1685