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