* Calendar day view: in am/pm mode, display midnight as 12:00am, not 0:00am
[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, 1);
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, 1);
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, 1);
917                                 wprintf("<i>%s</i> %s<br>", _("Starting date/time:"), buf);
918                                 webcit_fmt_date(buf, event_tte, 1);
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, 1);
996                                 wprintf("<i>%s</i> %s<br>", _("Starting date/time:"), buf);
997                                 webcit_fmt_date(buf, event_tte, 1);
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
1031         time_format = get_time_format_cached ();
1032         get_pref_long("daystart", &daystart, 8);
1033         get_pref_long("dayend", &dayend, 17);
1034
1035         /* when loading daystart/dayend, replace missing, corrupt, or impossible values with defaults */
1036         if ((daystart < 0) || (dayend < 2) || (daystart >= 23) || (dayend > 23) || (dayend <= daystart)) {
1037                 daystart = 9;
1038                 dayend = 17;
1039         }
1040         
1041         /* Today's date */
1042         memset(&d_tm, 0, sizeof d_tm);
1043         d_tm.tm_year = year - 1900;
1044         d_tm.tm_mon = month - 1;
1045         d_tm.tm_mday = day;
1046         today_t = mktime(&d_tm); 
1047
1048         /* Figure out the dates for "yesterday" and "tomorrow" links */
1049
1050         memset(&today, 0, sizeof(struct icaltimetype));
1051         today.year = year;
1052         today.month = month;
1053         today.day = day;
1054         today.is_date = 1;
1055
1056         memcpy(&yesterday, &today, sizeof(struct icaltimetype));
1057         --yesterday.day;
1058         yesterday = icaltime_normalize(yesterday);
1059
1060         memcpy(&tomorrow, &today, sizeof(struct icaltimetype));
1061         ++tomorrow.day;
1062         tomorrow = icaltime_normalize(tomorrow);
1063
1064         wprintf("<div class=\"fix_scrollbar_bug\">");
1065
1066         /* Inner table (the real one) */
1067         wprintf("<table class=\"calendar\" id=\"inner_day\"><tr> \n");
1068
1069         /* Innermost cell (contains hours etc.) */
1070         wprintf("<td class=\"events_of_the_day\" >");
1071         wprintf("<dl class=\"events\" >");
1072
1073         /* Now the middle of the day... */
1074
1075         for (hour = 0; hour < daystart; ++hour) {       /* could do HEIGHT=xx */
1076                 wprintf("<dt class=\"extrahour\"        "
1077                         "style=\"               "
1078                         "position: absolute;    "
1079                         "top: %dpx; left: 0px;  "
1080                         "height: %dpx;          "
1081                         "font-size: %dpx;       "
1082                         "\" >                   "
1083                         "<a href=\"display_edit_event?msgnum=0"
1084                         "?calview=day?year=%d?month=%d?day=%d?hour=%d?minute=0\">",
1085                         (hour * extratimeline ),
1086                         extratimeline,
1087                         extratimeline - 2,
1088                         year, month, day, hour
1089                         );
1090
1091                 if (time_format == WC_TIMEFORMAT_24) {
1092                         wprintf("%2d:00</a> ", hour);
1093                 }
1094                 else {
1095                         wprintf("%d:00%s</a> ",
1096                                 ((hour == 0) ? 12 : (hour <= 12 ? hour : hour-12)),
1097                                 (hour < 12 ? "am" : "pm")
1098                                 );
1099                 }
1100
1101                 wprintf("</dt>");
1102         }
1103
1104         gap = daystart * extratimeline;
1105
1106         for (hour = daystart; hour <= dayend; ++hour) {       /* could do HEIGHT=xx */
1107                 wprintf("<dt class=\"hour\"     "
1108                         "style=\"               "
1109                         "position: absolute;    "
1110                         "top: %ldpx; left: 0px; "
1111                         "height: %dpx;          "
1112                         "font-size: %dpx;       "
1113                         "\" >                   "
1114                         "<a href=\"display_edit_event?msgnum=0?calview=day"
1115                         "?year=%d?month=%d?day=%d?hour=%d?minute=0\">",
1116                         gap + ((hour - daystart) * timeline ),
1117                         timeline,
1118                         timeline - 2,
1119                         year, month, day, hour
1120                         );
1121
1122                 if (time_format == WC_TIMEFORMAT_24) {
1123                         wprintf("%2d:00</a> ", hour);
1124                 }
1125                 else {
1126                         wprintf("%d:00%s</a> ",
1127                                 (hour <= 12 ? hour : hour-12),
1128                                 (hour < 12 ? "am" : "pm")
1129                                                 );
1130                 }
1131
1132                 wprintf("</dt>");
1133         }
1134
1135         gap = gap + ((dayend - daystart + 1) * timeline);
1136
1137         for (hour = (dayend + 1); hour < 24; ++hour) {       /* could do HEIGHT=xx */
1138                 wprintf("<dt class=\"extrahour\"     "
1139                         "style=\"               "
1140                         "position: absolute;    "
1141                         "top: %ldpx; left: 0px; "
1142                         "height: %dpx;          "
1143                         "font-size: %dpx;       "
1144                         "\" >                   "
1145                         "<a href=\"display_edit_event?msgnum=0?calview=day"
1146                         "?year=%d?month=%d?day=%d?hour=%d?minute=0\">",
1147                         gap + ((hour - dayend - 1) * extratimeline ),
1148                         extratimeline,
1149                         extratimeline - 2,
1150                         year, month, day, hour
1151                 );
1152
1153                 if (time_format == WC_TIMEFORMAT_24) {
1154                         wprintf("%2d:00</a> ", hour);
1155                 }
1156                 else {
1157                         wprintf("%d:00%s</a> ",
1158                                 (hour <= 12 ? hour : hour-12),
1159                                 (hour < 12 ? "am" : "pm")
1160                         );
1161                 }
1162
1163                 wprintf("</dt>");
1164         }
1165
1166         /* Display events with start and end times on this day */
1167         calendar_day_view_display_events(today_t, year, month, day, 0, daystart, dayend);
1168
1169         wprintf("</dl>");
1170         wprintf("</td>");                       /* end of innermost table */
1171
1172         /* Display extra events (start/end times not present or not today) in the middle column */
1173         wprintf("<td class=\"extra_events\">");
1174
1175         wprintf("<ul>");
1176
1177         /* Display all-day events */
1178         calendar_day_view_display_events(today_t, year, month, day, 1, daystart, dayend);
1179
1180         wprintf("</ul>");
1181
1182         wprintf("</td>");       /* end extra on the middle */
1183
1184         wprintf("<td width=20%% align=center valign=top>");     /* begin stuff-on-the-right */
1185
1186         /* Begin todays-date-with-left-and-right-arrows */
1187         wprintf("<table border=0 width=100%% "
1188                 "cellspacing=0 cellpadding=0 bgcolor=\"#FFFFFF\">\n");
1189         wprintf("<tr>");
1190
1191         /* Left arrow */        
1192         wprintf("<td align=center>");
1193         wprintf("<a href=\"readfwd?calview=day?year=%d?month=%d?day=%d\">",
1194                 yesterday.year, yesterday.month, yesterday.day);
1195         wprintf("<img align=middle src=\"static/prevdate_32x.gif\" border=0></A>");
1196         wprintf("</td>");
1197
1198         wc_strftime(d_str, sizeof d_str,
1199                 "<td align=center>"
1200                 "<font size=+2>%B</font><br />"
1201                 "<font size=+3>%d</font><br />"
1202                 "<font size=+2>%Y</font><br />"
1203                 "</td>",
1204                 &d_tm
1205                 );
1206         wprintf("%s", d_str);
1207
1208         /* Right arrow */
1209         wprintf("<td align=center>");
1210         wprintf("<a href=\"readfwd?calview=day?year=%d?month=%d?day=%d\">",
1211                 tomorrow.year, tomorrow.month, tomorrow.day);
1212         wprintf("<img align=middle src=\"static/nextdate_32x.gif\""
1213                 " border=0></a>\n");
1214         wprintf("</td>");
1215
1216         wprintf("</tr></table>\n");
1217         /* End todays-date-with-left-and-right-arrows */
1218
1219         /* Embed a mini month calendar in this space */
1220         wprintf("<br />\n");
1221         embeddable_mini_calendar(year, month);
1222
1223         wprintf("</font></center>\n");
1224
1225         wprintf("</td></tr>");                  /* end stuff-on-the-right */
1226
1227         wprintf("</table>"                      /* end of inner table */
1228                 "</div>");
1229
1230         StrBufAppendPrintf(WC->trailing_javascript,
1231                 " setTimeout(\"btt_enableTooltips('inner_day')\", 1);   \n"
1232         );
1233 }
1234
1235
1236 /*
1237  * Display today's events.  Returns the number of items displayed.
1238  */
1239 int calendar_summary_view(void) {
1240         long hklen;
1241         const char *HashKey;
1242         void *vCal;
1243         HashPos *Pos;
1244         disp_cal *Cal;
1245         icalproperty *p;
1246         struct icaltimetype t;
1247         time_t event_tt;
1248         struct tm event_tm;
1249         struct tm today_tm;
1250         time_t now;
1251         int all_day_event = 0;
1252         char timestring[SIZ];
1253         wcsession *WCC = WC;
1254         int num_displayed = 0;
1255
1256         if (GetCount(WC->disp_cal_items) == 0) {
1257                 return(0);
1258         }
1259
1260         now = time(NULL);
1261         localtime_r(&now, &today_tm);
1262
1263         Pos = GetNewHashPos(WCC->disp_cal_items, 0);
1264         while (GetNextHashPos(WCC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
1265                 Cal = (disp_cal*)vCal;
1266                 p = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
1267                 if (p != NULL) {
1268                         t = icalproperty_get_dtstart(p);
1269                         event_tt = icaltime_as_timet(t);
1270                         if (t.is_date) {
1271                                 all_day_event = 1;
1272                         }
1273                         else {
1274                                 all_day_event = 0;
1275                         }
1276                         fmt_time(timestring, event_tt);
1277
1278                         if (all_day_event) {
1279                                 gmtime_r(&event_tt, &event_tm);
1280                         }
1281                         else {
1282                                 localtime_r(&event_tt, &event_tm);
1283                         }
1284
1285                         if ( (event_tm.tm_year == today_tm.tm_year)
1286                                 && (event_tm.tm_mon == today_tm.tm_mon)
1287                                 && (event_tm.tm_mday == today_tm.tm_mday)
1288                         ) {
1289
1290                                 p = icalcomponent_get_first_property(Cal->cal, ICAL_SUMMARY_PROPERTY);
1291                                 if (p != NULL) {
1292
1293
1294                                         if (WCC->wc_view == VIEW_TASKS) {
1295                                                 wprintf("<a href=\"display_edit_task"
1296                                                         "?msgnum=%ld"
1297                                                         "?return_to_summary=1"
1298                                                         "?gotofirst=",
1299                                                         Cal->cal_msgnum
1300                                                 );
1301                                                 escputs(ChrPtr(WCC->wc_roomname));
1302                                                 wprintf("\">");
1303                                         }
1304                                         else {
1305                                                 wprintf("<a href=\"display_edit_event"
1306                                                         "?msgnum=%ld"
1307                                                         "?calview=summary"
1308                                                         "?year=%d"
1309                                                         "?month=%d"
1310                                                         "?day=%d"
1311                                                         "?gotofirst=",
1312                                                         Cal->cal_msgnum,
1313                                                         today_tm.tm_year + 1900,
1314                                                         today_tm.tm_mon + 1,
1315                                                         today_tm.tm_mday
1316                                                 );
1317                                                 escputs(ChrPtr(WCC->wc_roomname));
1318                                                 wprintf("\">");
1319                                         }
1320                                         escputs((char *) icalproperty_get_comment(p));
1321                                         if (!all_day_event) {
1322                                                 wprintf(" (%s)", timestring);
1323                                         }
1324                                         wprintf("</a><br />\n");
1325                                         ++num_displayed;
1326                                 }
1327                         }
1328                 }
1329         }
1330         DeleteHashPos(&Pos);
1331         DeleteHash(&WC->disp_cal_items);
1332         return(num_displayed);
1333 }
1334
1335 /*
1336  * Parse the URL variables in order to determine the scope and display of a calendar view
1337  */
1338 void parse_calendar_view_request(struct calview *c) {
1339         time_t now;
1340         struct tm tm;
1341         char calview[32];
1342         int span = 3888000;
1343
1344         /* In case no date was specified, go with today */
1345         now = time(NULL);
1346         localtime_r(&now, &tm);
1347         c->year = tm.tm_year + 1900;
1348         c->month = tm.tm_mon + 1;
1349         c->day = tm.tm_mday;
1350
1351         /* Now see if a date was specified */
1352         if (havebstr("year")) c->year = ibstr("year");
1353         if (havebstr("month")) c->month = ibstr("month");
1354         if (havebstr("day")) c->day = ibstr("day");
1355
1356         /* How would you like that cooked? */
1357         if (havebstr("calview")) {
1358                 strcpy(calview, bstr("calview"));
1359         }
1360         else {
1361                 strcpy(calview, "month");
1362         }
1363
1364         /* Display the selected view */
1365         if (!strcasecmp(calview, "day")) {
1366                 c->view = calview_day;
1367         }
1368         else if (!strcasecmp(calview, "week")) {
1369                 c->view = calview_week;
1370         }
1371         else if (!strcasecmp(calview, "summary")) {     /* shouldn't ever happen, but just in case */
1372                 c->view = calview_day;
1373         }
1374         else {
1375                 if (WC->wc_view == VIEW_CALBRIEF) {
1376                         c->view = calview_brief;
1377                 }
1378                 else {
1379                         c->view = calview_month;
1380                 }
1381         }
1382
1383         /* Now try and set the lower and upper bounds so that we don't
1384          * burn too many cpu cycles parsing data way in the past or future
1385          */
1386
1387         tm.tm_year = c->year - 1900;
1388         tm.tm_mon = c->month - 1;
1389         tm.tm_mday = c->day;
1390         now = mktime(&tm);
1391
1392         if (c->view == calview_month)   span = 3888000;
1393         if (c->view == calview_brief)   span = 3888000;
1394         if (c->view == calview_week)    span = 604800;
1395         if (c->view == calview_day)     span = 86400;
1396         if (c->view == calview_summary) span = 86400;
1397
1398         c->lower_bound = now - span;
1399         c->upper_bound = now + span;
1400 }
1401
1402
1403
1404 /*
1405  * Render a calendar view from data previously loaded into memory
1406  */
1407 void render_calendar_view(struct calview *c)
1408 {
1409         if (c->view == calview_day) {
1410                 calendar_day_view(c->year, c->month, c->day);
1411         }
1412         else if (c->view == calview_week) {
1413                 calendar_week_view(c->year, c->month, c->day);
1414         }
1415         else {
1416                 if (WC->wc_view == VIEW_CALBRIEF) {
1417                         calendar_brief_month_view(c->year, c->month, c->day);
1418                 }
1419                 else {
1420                         calendar_month_view(c->year, c->month, c->day);
1421                 }
1422         }
1423
1424         /* Free the in-memory list of calendar items */
1425         DeleteHash(&WC->disp_cal_items);
1426 }
1427
1428
1429 /*
1430  * Helper function for do_tasks_view().  Returns the due date/time of a vtodo.
1431  */
1432 time_t get_task_due_date(icalcomponent *vtodo) {
1433         icalproperty *p;
1434
1435         if (vtodo == NULL) {
1436                 return(0L);
1437         }
1438
1439         /*
1440          * If we're looking at a fully encapsulated VCALENDAR
1441          * rather than a VTODO component, recurse into the data
1442          * structure until we get a VTODO.
1443          */
1444         if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) {
1445                 return get_task_due_date(
1446                         icalcomponent_get_first_component(
1447                                 vtodo, ICAL_VTODO_COMPONENT
1448                                 )
1449                         );
1450         }
1451
1452         p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY);
1453         if (p != NULL) {
1454                 return(icaltime_as_timet(icalproperty_get_due(p)));
1455         }
1456         else {
1457                 return(0L);
1458         }
1459 }
1460
1461
1462 /*
1463  * Compare the due dates of two tasks (this is for sorting)
1464  */
1465 int task_due_cmp(const void *vtask1, const void *vtask2) {
1466         disp_cal * Task1 = (disp_cal *)GetSearchPayload(vtask1);
1467         disp_cal * Task2 = (disp_cal *)GetSearchPayload(vtask2);
1468
1469         time_t t1;
1470         time_t t2;
1471
1472         t1 =  get_task_due_date(Task1->cal);
1473         t2 =  get_task_due_date(Task2->cal);
1474         if (t1 < t2) return(-1);
1475         if (t1 > t2) return(1);
1476         return(0);
1477 }
1478
1479 /*
1480  * qsort filter to move completed tasks to bottom of task list
1481  */
1482 int task_completed_cmp(const void *vtask1, const void *vtask2) {
1483         disp_cal * Task1 = (disp_cal *)GetSearchPayload(vtask1);
1484 /*      disp_cal * Task2 = (disp_cal *)GetSearchPayload(vtask2); */
1485
1486         icalproperty_status t1 = icalcomponent_get_status((Task1)->cal);
1487         /* icalproperty_status t2 = icalcomponent_get_status(((struct disp_cal *)task2)->cal); */
1488         
1489         if (t1 == ICAL_STATUS_COMPLETED) 
1490                 return 1;
1491         return 0;
1492 }
1493
1494
1495
1496 /*
1497  * do the whole task view stuff
1498  */
1499 void do_tasks_view(void) {
1500         long hklen;
1501         const char *HashKey;
1502         void *vCal;
1503         disp_cal *Cal;
1504         HashPos *Pos;
1505         int nItems;
1506         time_t due;
1507         char buf[SIZ];
1508         icalproperty *p;
1509         wcsession *WCC = WC;
1510
1511         wprintf("<div class=\"fix_scrollbar_bug\">"
1512                 "<table class=\"calendar_view_background\"><tbody id=\"taskview\">\n<tr>\n"
1513                 "<th>");
1514         wprintf(_("Completed?"));
1515         wprintf("</th><th>");
1516         wprintf(_("Name of task"));
1517         wprintf("</th><th>");
1518         wprintf(_("Date due"));
1519         wprintf("</th><th>");
1520         wprintf(_("Category"));
1521         wprintf(" (<select id=\"selectcategory\"><option value=\"showall\">%s</option></select>)</th></tr>\n",
1522                 _("Show All"));
1523
1524         nItems = GetCount(WC->disp_cal_items);
1525
1526         /* Sort them if necessary
1527         if (nItems > 1) {
1528                 SortByPayload(WC->disp_cal_items, task_due_cmp);
1529         }
1530         * this shouldn't be neccessary, since we sort by the start time.
1531         */
1532
1533         /* And then again, by completed */
1534         if (nItems > 1) {
1535                 SortByPayload(WC->disp_cal_items, 
1536                               task_completed_cmp);
1537         }
1538
1539         Pos = GetNewHashPos(WCC->disp_cal_items, 0);
1540         while (GetNextHashPos(WCC->disp_cal_items, Pos, &hklen, &HashKey, &vCal)) {
1541                 icalproperty_status todoStatus;
1542
1543                 Cal = (disp_cal*)vCal;
1544                 wprintf("<tr><td>");
1545                 todoStatus = icalcomponent_get_status(Cal->cal);
1546                 wprintf("<input type=\"checkbox\" name=\"completed\" value=\"completed\" ");
1547                 if (todoStatus == ICAL_STATUS_COMPLETED) {
1548                         wprintf("checked=\"checked\" ");
1549                 }
1550                 wprintf("disabled=\"disabled\">\n</td><td>");
1551                 p = icalcomponent_get_first_property(Cal->cal,
1552                         ICAL_SUMMARY_PROPERTY);
1553                 wprintf("<a href=\"display_edit_task?msgnum=%ld?taskrm=", Cal->cal_msgnum);
1554                 urlescputs(ChrPtr(WC->wc_roomname));
1555                 wprintf("\">");
1556                 /* wprintf("<img align=middle "
1557                 "src=\"static/taskmanag_16x.gif\" border=0>&nbsp;"); */
1558                 if (p != NULL) {
1559                         escputs((char *)icalproperty_get_comment(p));
1560                 }
1561                 wprintf("</a>\n");
1562                 wprintf("</td>\n");
1563
1564                 due = get_task_due_date(Cal->cal);
1565                 wprintf("<td><span");
1566                 if (due > 0) {
1567                         webcit_fmt_date(buf, due, 0);
1568                         wprintf(">%s",buf);
1569                 }
1570                 else {
1571                         wprintf(">");
1572                 }
1573                 wprintf("</span></td>");
1574                 wprintf("<td>");
1575                 p = icalcomponent_get_first_property(Cal->cal,
1576                         ICAL_CATEGORIES_PROPERTY);
1577                 if (p != NULL) {
1578                         escputs((char *)icalproperty_get_categories(p));
1579                 }
1580                 wprintf("</td>");
1581                 wprintf("</tr>");
1582         }
1583
1584         wprintf("</tbody></table></div>\n");
1585
1586         /* Free the list */
1587         DeleteHash(&WC->disp_cal_items);
1588         DeleteHashPos(&Pos);
1589 }
1590