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