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