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