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