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