Drop old unsupported GTK+ conditionals too
[claws.git] / src / plugins / vcalendar / month-view.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  *
4  * Copyright (c) 2007-2008 Juha Kautto (juha at xfce.org)
5  * Copyright (c) 2008 Colin Leroy (colin@colino.net)
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #include "claws-features.h"
25 #endif
26
27 #include <stddef.h>
28 #include <glib.h>
29 #include <glib/gi18n.h>
30 #include "defs.h"
31
32 #ifdef USE_PTHREAD
33 #include <pthread.h>
34 #endif
35 #include <string.h>
36 #include <time.h>
37
38 #include <glib.h>
39 #include <glib/gprintf.h>
40 #include <gdk/gdkkeysyms.h>
41 #include <gdk/gdk.h>
42 #include <gtk/gtk.h>
43
44 #include "summaryview.h"
45 #include "vcalendar.h"
46 #include "vcal_folder.h"
47 #include "vcal_prefs.h"
48 #include "vcal_manager.h"
49 #include "common-views.h"
50 #include "vcal_meeting_gtk.h"
51
52 #define MAX_DAYS 40
53 struct _month_win
54 {
55     GtkAccelGroup *accel_group;
56
57     GtkWidget *Window;
58     GtkWidget *Vbox;
59
60     GtkWidget *Menubar;
61     GtkWidget *File_menu;
62     GtkWidget *File_menu_new;
63     GtkWidget *File_menu_close;
64     GtkWidget *View_menu;
65     GtkWidget *View_menu_refresh;
66     GtkWidget *Go_menu;
67     GtkWidget *Go_menu_today;
68     GtkWidget *Go_menu_prev;
69     GtkWidget *Go_menu_next;
70
71     GtkWidget *Toolbar;
72     GtkWidget *Create_toolbutton;
73     GtkWidget *Previous_toolbutton;
74     GtkWidget *Today_toolbutton;
75     GtkWidget *Next_toolbutton;
76     GtkWidget *Refresh_toolbutton;
77     GtkWidget *Close_toolbutton;
78
79     GtkWidget *StartDate_button;
80     GtkRequisition StartDate_button_req;
81     GtkWidget *day_spin;
82
83     GtkWidget *month_view_vbox;
84     GtkWidget *scroll_win_h;
85     GtkWidget *dtable_h; /* header of day table */
86     GtkWidget *scroll_win;
87     GtkWidget *dtable;   /* day table */
88     GtkRequisition hour_req;
89
90     GtkWidget *header[MAX_DAYS];
91     GtkWidget *element[6][MAX_DAYS];
92     GtkWidget *line[6][MAX_DAYS];
93
94     guint upd_timer;
95     gdouble scroll_pos; /* remember the scroll position */
96
97     GdkColor bg1, bg2, line_color, bg_today, fg_sunday;
98     GList    *apptw_list; /* keep track of appointments being updated */
99     struct tm startdate;
100     FolderItem *item;
101     gulong selsig;
102     GtkWidget *view_menu;
103     GtkWidget *event_menu;
104     GtkActionGroup *event_group;
105     GtkUIManager *ui_manager;
106 };
107
108 gchar *dayname[7] = {
109         N_("Monday"),
110         N_("Tuesday"),
111         N_("Wednesday"),
112         N_("Thursday"),
113         N_("Friday"),
114         N_("Saturday"),
115         N_("Sunday")
116         };
117 gchar *monthname[12] = {
118         N_("January"),
119         N_("February"),
120         N_("March"),
121         N_("April"),
122         N_("May"),
123         N_("June"),
124         N_("July"),
125         N_("August"),
126         N_("September"),
127         N_("October"),
128         N_("November"),
129         N_("December")
130         };
131
132 static gchar *get_locale_date(struct tm *tmdate)
133 {
134         gchar *d = g_malloc(100);
135         strftime(d, 99, "%x", tmdate);
136         return d;
137 }
138
139 void mw_close_window(month_win *mw)
140 {
141     vcal_view_set_summary_page(mw->Vbox, mw->selsig);
142     
143
144     g_free(mw);
145     mw = NULL;
146 }
147
148 static char *orage_tm_date_to_i18_date(struct tm *tm_date)
149 {
150     static char i18_date[32];
151     struct tm t;
152     t.tm_mday = tm_date->tm_mday;
153     t.tm_mon = tm_date->tm_mon - 1;
154     t.tm_year = tm_date->tm_year - 1900;
155     if (strftime(i18_date, 32, "%x", &t) == 0)
156         g_error("Orage: orage_tm_date_to_i18_date too long string in strftime");
157     return(i18_date);
158 }
159
160 static void changeSelectedDate(month_win *mw, gint month)
161 {
162     gint curmon = mw->startdate.tm_mon;
163     if (month > 0) {
164      do { /* go to first of next month */
165       orage_move_day(&(mw->startdate), 1);
166      } while (curmon == mw->startdate.tm_mon);
167     } else {
168      do { /* go to last day of last month */
169       orage_move_day(&(mw->startdate), -1);
170      } while (curmon == mw->startdate.tm_mon);
171      do { /* go to first of last month */
172       orage_move_day(&(mw->startdate), -1);
173      } while (mw->startdate.tm_mday > 1);
174     }
175 }
176
177 static gint on_Previous_clicked(GtkWidget *button, GdkEventButton *event,
178                                     month_win *mw)
179 {
180     changeSelectedDate(mw, -1);
181     refresh_month_win(mw);
182     return TRUE;
183 }
184
185 static gint on_Next_clicked(GtkWidget *button, GdkEventButton *event,
186                                     month_win *mw)
187 {
188     changeSelectedDate(mw, +1);
189     refresh_month_win(mw);
190     return TRUE;
191 }
192
193 static void mw_summary_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
194                              gint column, month_win *mw)
195 {
196         MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, row);
197         
198         if (msginfo && msginfo->msgid) {
199                 VCalEvent *event = vcal_manager_load_event(msginfo->msgid);
200                 if (event) {
201                         struct tm tm_start;
202                         time_t t_start = icaltime_as_timet(icaltime_from_string(event->dtstart));
203                         gboolean changed = FALSE;
204
205 #ifdef G_OS_WIN32
206                         if (t_start < 0)
207                                 t_start = 1;
208 #endif
209                         localtime_r(&t_start, &tm_start);
210                         while (tm_start.tm_year < mw->startdate.tm_year) {
211                                 changeSelectedDate(mw, -1);
212                                 changed = TRUE;
213                         }
214                         while (tm_start.tm_year > mw->startdate.tm_year) {
215                                 changeSelectedDate(mw, +1);
216                                 changed = TRUE;
217                         }
218                         while (tm_start.tm_mon < mw->startdate.tm_mon) {
219                                 changeSelectedDate(mw, -1);
220                                 changed = TRUE;
221                         }
222                         while (tm_start.tm_mon > mw->startdate.tm_mon) {
223                                 changeSelectedDate(mw, +1);
224                                 changed = TRUE;
225                         }
226                         if (changed)
227                                 refresh_month_win(mw);
228                 }
229                 vcal_manager_free_event(event);
230         }
231 }
232
233 static void month_view_new_meeting_cb(month_win *mw, gpointer data_i, gpointer data_s)
234 {
235     int offset = GPOINTER_TO_INT(data_i);
236     struct tm tm_date = mw->startdate;
237
238     while (offset > tm_date.tm_mday) {
239             orage_move_day(&tm_date, 1);
240     }
241     while (offset < tm_date.tm_mday) {
242             orage_move_day(&tm_date, -1);
243     }
244     tm_date.tm_hour = 0;
245     vcal_meeting_create_with_start(NULL, &tm_date);
246 }
247
248 static void month_view_edit_meeting_cb(month_win *mw, gpointer data_i, gpointer data_s)
249 {
250         const gchar *uid = (gchar *)data_s;
251         vcal_view_select_event (uid, mw->item, TRUE,
252                             G_CALLBACK(mw_summary_selected), mw);
253 }
254
255 static void month_view_cancel_meeting_cb(month_win *mw, gpointer data_i, gpointer data_s)
256 {
257         const gchar *uid = (gchar *)data_s;
258         vcalendar_cancel_meeting(mw->item, uid);
259 }
260
261 static void month_view_today_cb(month_win *mw, gpointer data_i, gpointer data_s)
262 {
263     time_t now = time(NULL);
264     struct tm tm_today;
265     localtime_r(&now, &tm_today);
266
267     while (tm_today.tm_mday != 1)
268         orage_move_day(&tm_today, -1);
269     
270     mw->startdate = tm_today;
271     refresh_month_win(mw);
272 }
273
274 static void header_button_clicked_cb(GtkWidget *button
275         , GdkEventButton *event, gpointer *user_data)
276 {
277     month_win *mw = (month_win *)user_data;
278     int offset = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "day"));
279
280     if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
281             month_view_new_meeting_cb(mw, GINT_TO_POINTER(offset), NULL);
282     }
283     if (event->button == 3) {
284                 g_object_set_data(G_OBJECT(mw->Vbox), "menu_win",
285                           mw);
286                 g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_i",
287                           GINT_TO_POINTER(offset));
288                 g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_s",
289                           NULL);
290                 g_object_set_data(G_OBJECT(mw->Vbox), "new_meeting_cb",
291                           month_view_new_meeting_cb);
292                 g_object_set_data(G_OBJECT(mw->Vbox), "go_today_cb",
293                           month_view_today_cb);
294                 gtk_menu_popup(GTK_MENU(mw->view_menu), 
295                                NULL, NULL, NULL, NULL, 
296                                event->button, event->time);
297     }
298 }
299
300 static void on_button_press_event_cb(GtkWidget *widget
301         , GdkEventButton *event, gpointer *user_data)
302 {
303     month_win *mw = (month_win *)user_data;
304     gchar *uid = g_object_get_data(G_OBJECT(widget), "UID");;
305     gpointer offset = g_object_get_data(G_OBJECT(widget), "offset");
306
307     if (event->button == 1) {
308         if (uid)
309             vcal_view_select_event (uid, mw->item, (event->type==GDK_2BUTTON_PRESS),
310                             G_CALLBACK(mw_summary_selected), mw);
311         else if (event->type == GDK_2BUTTON_PRESS) {
312             month_view_new_meeting_cb(mw, GINT_TO_POINTER(offset), NULL);
313         }
314     }
315     if (event->button == 3) {
316             g_object_set_data(G_OBJECT(mw->Vbox), "menu_win",
317                       mw);
318             g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_i",
319                       offset);
320             g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_s",
321                       uid);
322             g_object_set_data(G_OBJECT(mw->Vbox), "new_meeting_cb",
323                       month_view_new_meeting_cb);
324             g_object_set_data(G_OBJECT(mw->Vbox), "edit_meeting_cb",
325                       month_view_edit_meeting_cb);
326             g_object_set_data(G_OBJECT(mw->Vbox), "cancel_meeting_cb",
327                       month_view_cancel_meeting_cb);
328             g_object_set_data(G_OBJECT(mw->Vbox), "go_today_cb",
329                       month_view_today_cb);
330             gtk_menu_popup(GTK_MENU(mw->event_menu), 
331                            NULL, NULL, NULL, NULL, 
332                            event->button, event->time);    
333     }
334 }
335
336 static void add_row(month_win *mw, VCalEvent *event, gint days)
337 {
338     gint row, start_row, end_row, first_row, last_row;
339     gint col, start_col, end_col, first_col, last_col;
340     gint width, start_width, end_width;
341     gchar *text, *tip, *start_date, *end_date;
342     GtkWidget *ev = NULL, *lab = NULL, *hb;
343     time_t t_start, t_end;
344     struct tm tm_first, tm_start, tm_end;
345     guint monthdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
346     int weekoffset = -1;
347     gboolean pack = TRUE, update_tip = FALSE;
348     time_t now = time(NULL);
349     struct tm tm_today;
350     gboolean start_prev_mon = FALSE;
351
352     localtime_r(&now, &tm_today);
353
354     tm_today.tm_year += 1900;
355     tm_today.tm_mon++;
356
357     /* First clarify timings */
358     t_start = icaltime_as_timet(icaltime_from_string(event->dtstart));
359     if (event->dtend && *event->dtend)
360         t_end = icaltime_as_timet(icaltime_from_string(event->dtend));
361     else 
362         t_end = t_start;
363
364 #ifdef G_OS_WIN32
365         if (t_start < 0)
366                 t_start = 1;
367         if (t_end < 0)
368                 t_end = 1;
369 #endif
370     localtime_r(&t_start, &tm_start);
371     localtime_r(&t_end, &tm_end);
372     tm_first = mw->startdate;
373    
374     tm_first.tm_year += 1900;
375
376     if (((tm_first.tm_year%4) == 0) 
377     && (((tm_first.tm_year%100) != 0) || ((tm_first.tm_year%400) == 0)))
378         ++monthdays[1]; /* leap year, february has 29 days */
379
380
381     tm_first.tm_mon += 1;
382     tm_start.tm_year += 1900;
383     tm_start.tm_mon += 1;
384     tm_end.tm_year += 1900;
385     tm_end.tm_mon += 1;
386
387     start_col = orage_days_between(&tm_first, &tm_start)+1;
388     end_col   = orage_days_between(&tm_first, &tm_end)+1;
389
390     if (start_col < 0)
391         start_prev_mon = TRUE;
392
393     if (end_col < 1) {
394         return;
395     }
396     if (start_col > 0 && start_col > monthdays[tm_first.tm_mon-1]) {
397         return;
398     }
399     else {
400         GDate *edate = g_date_new_dmy(tm_end.tm_mday, tm_end.tm_mon, tm_end.tm_year);
401         GDate *fdate = g_date_new_dmy(1, tm_first.tm_mon, tm_first.tm_year);
402         GDate *sdate;
403         if (start_col >= 1) {
404           sdate = g_date_new_dmy(tm_start.tm_mday, tm_start.tm_mon, tm_start.tm_year);
405         } else {
406           sdate = g_date_new_dmy(1, tm_first.tm_mon, tm_first.tm_year);
407         } /* endif */
408
409         col = start_col = (int)g_date_get_weekday(sdate);
410         end_col = (int)g_date_get_weekday(edate);
411         weekoffset = (int)g_date_get_monday_week_of_year(fdate);
412         row = start_row = (int)g_date_get_monday_week_of_year(sdate) - weekoffset;
413         end_row = (int)g_date_get_monday_week_of_year(edate) - weekoffset;
414         g_date_free(fdate);
415         g_date_free(sdate);
416         g_date_free(edate);
417     }
418
419     if (end_col > 7)
420        end_col = 7;
421
422     /* then add the appointment */
423     text = g_strdup(event->summary?event->summary : _("Unknown"));
424
425     if (mw->element[row][col] == NULL) {
426         hb = gtk_vbox_new(TRUE, 1);
427         mw->element[row][col] = hb;
428     }
429     else {
430         GList *children = NULL;
431         hb = mw->element[row][col];
432         /* FIXME: set some real bar here to make it visible that we
433          * have more than 1 appointment here
434          */
435         children = gtk_container_get_children(GTK_CONTAINER(hb));
436         if (g_list_length(children) > 2) {
437                 pack = FALSE;
438                 update_tip = TRUE;
439         } else if (g_list_length(children) > 1) {
440                 pack = FALSE;
441                 update_tip = FALSE;
442         }
443         g_list_free(children);
444     }
445     if (pack || !update_tip) {
446        ev = gtk_event_box_new();
447        lab = gtk_label_new(text);
448        gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
449        gtk_label_set_ellipsize(GTK_LABEL(lab), PANGO_ELLIPSIZE_END);
450        if ((row % 2) == 1)
451            gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg1);
452        else
453            gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg2);
454     } else if (!pack && update_tip) {
455        GList *children = gtk_container_get_children(GTK_CONTAINER(hb));
456        ev = GTK_WIDGET(g_list_last(children)->data);
457        g_list_free(children);
458     }
459
460     if (ev && tm_start.tm_mday == tm_today.tm_mday && tm_start.tm_mon == tm_today.tm_mon 
461         && tm_start.tm_year == tm_today.tm_year)
462         gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg_today);
463
464     if (orage_days_between(&tm_start, &tm_end) == 0)
465         tip = g_strdup_printf("%s\n%02d:%02d-%02d:%02d\n%s"
466                 , text, tm_start.tm_hour, tm_start.tm_min
467                 , tm_end.tm_hour, tm_end.tm_min, event->description);
468     else {
469 /* we took the date in unnormalized format, so we need to do that now */
470         start_date = g_strdup(orage_tm_date_to_i18_date(&tm_start));
471         end_date = g_strdup(orage_tm_date_to_i18_date(&tm_end));
472         tip = g_strdup_printf("%s\n%s %02d:%02d - %s %02d:%02d\n%s"
473                 , text
474                 , start_date, tm_start.tm_hour, tm_start.tm_min
475                 , end_date, tm_end.tm_hour, tm_end.tm_min
476                 , event->description);
477         g_free(start_date);
478         g_free(end_date);
479     }
480     if (pack) {
481         gtk_container_add(GTK_CONTAINER(ev), lab);
482         CLAWS_SET_TIP(ev, tip);
483         gtk_box_pack_start(GTK_BOX(hb), ev, TRUE, TRUE, 0);
484     } else if (!update_tip) {
485         gtk_label_set_label(GTK_LABEL(lab), "...");
486         gtk_container_add(GTK_CONTAINER(ev), lab);
487         CLAWS_SET_TIP(ev, tip);
488         gtk_box_pack_start(GTK_BOX(hb), ev, TRUE, TRUE, 0);
489     } else {
490         gchar *old = gtk_widget_get_tooltip_text(ev);
491         gchar *new = g_strdup_printf("%s\n\n%s", old?old:"", tip);
492         g_free(old);
493
494         CLAWS_SET_TIP(ev, new);
495         g_free(new);
496     }
497     g_object_set_data_full(G_OBJECT(ev), "UID", g_strdup(event->uid), g_free);
498     g_object_set_data(G_OBJECT(ev), "offset", GINT_TO_POINTER(tm_start.tm_mday));
499     g_signal_connect((gpointer)ev, "button-press-event"
500             , G_CALLBACK(on_button_press_event_cb), mw);
501     g_free(tip);
502     g_free(text);
503
504     /* and finally draw the line to show how long the appointment is,
505      * but only if it is Busy type event (=availability != 0) 
506      * and it is not whole day event */
507     if (TRUE) {
508         width = mw->StartDate_button_req.width;
509         /*
510          * same_date = !strncmp(start_ical_time, end_ical_time, 8);
511          * */
512         if (start_row < 0)
513             first_row = 0;
514         else
515             first_row = start_row;
516         if (end_row > 5)
517             last_row = 5;
518         else
519             last_row = end_row;
520         for (row = first_row; row <= last_row; row++) {
521             if (row == start_row)
522                 first_col = start_col;
523             else
524                 first_col = 0;
525             if (row == end_row)
526                 last_col   = end_col;
527             else
528                 last_col   = 7;
529             for (col = first_col; col <= last_col; col++) {
530                 if (row == start_row && col == start_col && !start_prev_mon)
531                     start_width = ((tm_start.tm_hour*60)+(tm_start.tm_min))*width/(24*60);
532                 else
533                     start_width = 0;
534                 if (row == last_row && col == last_col)
535                     end_width = ((tm_end.tm_hour*60)+(tm_end.tm_min))*width/(24*60);
536                 else
537                     end_width = width;
538
539                 mw->line[row][col] = build_line(start_width, 0
540                         , end_width-start_width, 2, mw->line[row][col]
541                         , &mw->line_color);
542             }
543         }
544     }
545 }
546
547 static void app_rows(month_win *mw, FolderItem *item)
548 {
549    GSList *events = vcal_get_events_list(item);
550    GSList *cur = NULL;
551    int days = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mw->day_spin));
552    for (cur = events; cur ; cur = cur->next) {
553         VCalEvent *event = (VCalEvent *) (cur->data);
554         add_row(mw, event, days);
555         vcal_manager_free_event(event);
556    }
557    g_slist_free(events);
558 }
559
560 static void app_data(month_win *mw, FolderItem *item)
561 {
562     app_rows(mw, item);
563 }
564
565
566 static void fill_days(month_win *mw, gint days, FolderItem *item)
567 {
568     gint day, col, height, width;
569     GtkWidget *ev, *vb, *hb;
570     guint monthdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
571     struct tm t = mw->startdate;
572     int weekoffset = -1;
573     time_t now = time(NULL);
574     struct tm tm_today;
575     
576     localtime_r(&now, &tm_today);
577
578     t.tm_year += 1900;
579     t.tm_mon++;
580     tm_today.tm_year += 1900;
581     tm_today.tm_mon++;
582     
583     if (((t.tm_year%4) == 0) 
584     && (((t.tm_year%100) != 0) || ((t.tm_year%400) == 0)))
585         ++monthdays[1]; /* leap year, february has 29 days */
586
587     height = mw->StartDate_button_req.height;
588     width = mw->StartDate_button_req.width;
589
590     /* first clear the structure */
591     for (col = 1; col <  days+1; col++) {
592         mw->header[col] = NULL;
593     }
594     for (day = 0; day < MAX_DAYS; day++)
595         for (col = 0; col < 6; col++)
596                 mw->line[col][day] = NULL;
597     for (day = 1; day <= monthdays[t.tm_mon-1]; day++) {
598         GDate *date = g_date_new_dmy(day, t.tm_mon, t.tm_year);
599         int dcol = (int)g_date_get_weekday(date);
600         int row = (int)g_date_get_monday_week_of_year(date);
601         if (weekoffset == -1) {
602                 weekoffset = row;
603                 row = 0;
604         } else {
605                 row = row - weekoffset;
606         }
607         mw->element[row][dcol] = NULL;
608         mw->line[row][dcol] = build_line(0, 0, width, 3, NULL
609                         , &mw->line_color);
610         g_date_free(date);
611     }
612
613     app_data(mw, item);
614
615     /* check rows */
616     for (day = 1; day <= monthdays[t.tm_mon-1]; day++) {
617         GDate *date = g_date_new_dmy(day, t.tm_mon, t.tm_year);
618         int col = (int)g_date_get_weekday(date);
619         int row = (int)g_date_get_monday_week_of_year(date);
620         gchar *label;
621         gchar *tmp;
622         GtkWidget *name;
623
624         if (weekoffset == -1) {
625                 weekoffset = row;
626                 row = 0;
627         } else {
628                 row = row - weekoffset;
629         }
630         vb = gtk_vbox_new(FALSE, 0);
631         gtk_widget_set_size_request(vb, width, height);
632             if (g_date_get_day(date) == 1)
633                 label = g_strdup_printf("%d %s", g_date_get_day(date),
634                                 _(monthname[g_date_get_month(date)-1]));
635             else
636                 label = g_strdup_printf("%d", g_date_get_day(date));
637             tmp = g_strdup_printf("%s %d %s %d",
638                                 _(dayname[col-1]),
639                                 g_date_get_day(date),
640                                 _(monthname[g_date_get_month(date)-1]),
641                                 g_date_get_year(date));
642
643             ev = gtk_event_box_new();
644             g_object_set_data(G_OBJECT(ev), "day", GINT_TO_POINTER((int)g_date_get_day(date)));
645             g_signal_connect((gpointer)ev, "button-press-event"
646                     , G_CALLBACK(header_button_clicked_cb), mw);
647             name = gtk_label_new(label);
648             gtk_misc_set_alignment(GTK_MISC(name), 0.0, 0.0);
649
650             CLAWS_SET_TIP(ev, tmp);
651             gtk_container_add(GTK_CONTAINER(ev), name);
652             g_free(tmp);
653             g_free(label);
654
655             if ((row % 2) == 1)
656                 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg1);
657             else
658                 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg2);
659             if (col == 7)
660                 gtk_widget_modify_fg(name, GTK_STATE_NORMAL, &mw->fg_sunday);
661             if (day == tm_today.tm_mday && t.tm_mon == tm_today.tm_mon && t.tm_year == tm_today.tm_year)
662                 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg_today);
663
664             hb = gtk_hbox_new(FALSE, 0);
665             gtk_box_pack_start(GTK_BOX(hb), ev, TRUE, TRUE, 1);
666             gtk_box_pack_start(GTK_BOX(vb), hb, TRUE, TRUE, 0);
667             if (mw->element[row][col]) {
668                 gtk_box_pack_start(GTK_BOX(vb), mw->element[row][col], TRUE, TRUE, 0);
669             }
670             gtk_box_pack_start(GTK_BOX(vb), mw->line[row][col]
671                     , FALSE, FALSE, 0);
672
673         gtk_table_attach(GTK_TABLE(mw->dtable), vb, col, col+1, row, row+1
674                  , (GTK_FILL), (0), 0, 0);
675         g_date_free(date);
676     }
677 }
678
679 static void build_month_view_header(month_win *mw, char *start_date)
680 {
681     GtkWidget *hbox, *label, *space_label;
682
683     hbox = gtk_hbox_new(FALSE, 0);
684
685     label = gtk_label_new(_("Start"));
686     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
687
688     /* start date button */
689     mw->StartDate_button = gtk_button_new();
690     gtk_box_pack_start(GTK_BOX(hbox), mw->StartDate_button, FALSE, FALSE, 0);
691
692     space_label = gtk_label_new("  ");
693     gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
694
695     space_label = gtk_label_new("     ");
696     gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
697
698     label = gtk_label_new(_("Show"));
699     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
700
701     /* show days spin = how many days to show */
702     mw->day_spin = gtk_spin_button_new_with_range(1, MAX_DAYS, 1);
703     gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(mw->day_spin), TRUE);
704     gtk_widget_set_size_request(mw->day_spin, 40, -1);
705     gtk_box_pack_start(GTK_BOX(hbox), mw->day_spin, FALSE, FALSE, 0);
706     label = gtk_label_new(_("days"));
707     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
708
709     space_label = gtk_label_new("   ");
710     gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
711
712     /* sizes */
713     gtk_button_set_label(GTK_BUTTON(mw->StartDate_button)
714             , (const gchar *)start_date);
715     gtk_widget_size_request(mw->StartDate_button, &mw->StartDate_button_req);
716     mw->StartDate_button_req.width += mw->StartDate_button_req.width/10;
717     label = gtk_label_new("00");
718     gtk_widget_size_request(label, &mw->hour_req);
719 }
720
721 static void build_month_view_colours(month_win *mw)
722 {
723     GtkStyle *def_style;
724     GdkColormap *pic1_cmap;
725     GtkWidget *ctree = NULL;
726     def_style = gtk_widget_get_default_style();
727     pic1_cmap = gdk_colormap_get_system();
728     
729     if (mainwindow_get_mainwindow()) {
730         ctree = mainwindow_get_mainwindow()->summaryview->ctree;
731     }
732     if (ctree) {
733         mw->bg1 = ctree->style->bg[GTK_STATE_NORMAL];
734         mw->bg2 = ctree->style->bg[GTK_STATE_NORMAL];
735     } else {
736         mw->bg1 = def_style->bg[GTK_STATE_NORMAL];
737         mw->bg2 = def_style->bg[GTK_STATE_NORMAL];
738     }
739
740     mw->bg1.red +=  (mw->bg1.red < 63000 ? 2000 : -2000);
741     mw->bg1.green += (mw->bg1.green < 63000 ? 2000 : -2000);
742     mw->bg1.blue += (mw->bg1.blue < 63000 ? 2000 : -2000);
743     gdk_colormap_alloc_color(pic1_cmap, &mw->bg1, FALSE, TRUE);
744
745     mw->bg2.red +=  (mw->bg2.red > 1000 ? -1000 : 1000);
746     mw->bg2.green += (mw->bg2.green > 1000 ? -1000 : 1000);
747     mw->bg2.blue += (mw->bg2.blue > 1000 ? -1000 : 1000);
748     gdk_colormap_alloc_color(pic1_cmap, &mw->bg2, FALSE, TRUE);
749
750     if (!gdk_color_parse("white", &mw->line_color)) {
751         mw->line_color.red =  239 * (65535/255);
752         mw->line_color.green = 235 * (65535/255);
753         mw->line_color.blue = 230 * (65535/255);
754     }
755
756     if (!gdk_color_parse("blue", &mw->fg_sunday)) {
757         g_warning("color parse failed: red\n");
758         mw->fg_sunday.red = 10 * (65535/255);
759         mw->fg_sunday.green = 10 * (65535/255);
760         mw->fg_sunday.blue = 255 * (65535/255);
761     }
762
763     if (!gdk_color_parse("gold", &mw->bg_today)) {
764         g_warning("color parse failed: gold\n");
765         mw->bg_today.red = 255 * (65535/255);
766         mw->bg_today.green = 215 * (65535/255);
767         mw->bg_today.blue = 115 * (65535/255);
768     }
769
770     if (ctree) {
771         mw->fg_sunday.red = (mw->fg_sunday.red + ctree->style->fg[GTK_STATE_SELECTED].red)/2;
772         mw->fg_sunday.green = (mw->fg_sunday.green + ctree->style->fg[GTK_STATE_SELECTED].red)/2;
773         mw->fg_sunday.blue = (3*mw->fg_sunday.blue + ctree->style->fg[GTK_STATE_SELECTED].red)/4;
774         mw->bg_today.red = (3*mw->bg_today.red + ctree->style->bg[GTK_STATE_NORMAL].red)/4;
775         mw->bg_today.green = (3*mw->bg_today.green + ctree->style->bg[GTK_STATE_NORMAL].red)/4;
776         mw->bg_today.blue = (3*mw->bg_today.blue + ctree->style->bg[GTK_STATE_NORMAL].red)/4;
777     }
778     gdk_colormap_alloc_color(pic1_cmap, &mw->line_color, FALSE, TRUE);
779     gdk_colormap_alloc_color(pic1_cmap, &mw->fg_sunday, FALSE, TRUE);
780     gdk_colormap_alloc_color(pic1_cmap, &mw->bg_today, FALSE, TRUE);
781 }
782
783 static void fill_hour(month_win *mw, gint col, gint row, char *text)
784 {
785     GtkWidget *name, *ev;
786
787     ev = gtk_event_box_new();
788     name = gtk_label_new(text);
789     gtk_misc_set_alignment(GTK_MISC(name), 0, 0.5);
790     CLAWS_SET_TIP(ev, _("Week number"));
791     gtk_container_add(GTK_CONTAINER(ev), name);
792     gtk_widget_set_size_request(ev, mw->hour_req.width
793             , mw->StartDate_button_req.height);
794     if (text)
795         gtk_table_attach(GTK_TABLE(mw->dtable), ev, col, col+1, row, row+1
796              , (GTK_FILL), (0), 0, 0);
797     else  /* special, needed for header table full day events */
798         gtk_table_attach(GTK_TABLE(mw->dtable_h), ev, col, col+1, row, row+1
799              , (GTK_FILL), (0), 0, 0);
800 }
801
802 static void build_month_view_table(month_win *mw)
803 {
804     gint days;   /* number of days to show */
805     gint i = 0;
806     GtkWidget *button;
807     struct tm tm_date, tm_today;
808     guint monthdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
809     GtkWidget *vp;
810     time_t t = time(NULL);
811     GtkWidget *arrow;
812     int avail_w = 0, avail_d = 0;
813     int avail_h = 0;
814     int weekoffset = -1;
815     GDate *date;
816     int first_week=0;
817
818     if (mainwindow_get_mainwindow()) {
819         GtkAllocation allocation;
820         SummaryView *summaryview = mainwindow_get_mainwindow()->summaryview;
821         GTK_EVENTS_FLUSH();
822         allocation = summaryview->mainwidget_book->allocation;
823         
824         avail_w = allocation.width - 25 - 2*(mw->hour_req.width);
825         avail_h = allocation.height - 20;
826         if (avail_h < 250)
827                 avail_h = 250;
828         avail_d = avail_w / mw->StartDate_button_req.width;
829     }
830     avail_d = 7;
831     gtk_widget_set_size_request(mw->StartDate_button, avail_w / avail_d, 
832                         (avail_h)/6);
833     gtk_widget_size_request(mw->StartDate_button, &mw->StartDate_button_req);
834    
835     /* initial values */
836     if (avail_d)
837         gtk_spin_button_set_value(GTK_SPIN_BUTTON(mw->day_spin), avail_d);
838
839 #ifdef G_OS_WIN32
840         if (t < 0)
841                 t = 1;
842 #endif
843     localtime_r(&t, &tm_today);
844     days = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mw->day_spin));
845     /****** header of day table = days columns ******/
846     mw->scroll_win_h = gtk_scrolled_window_new(NULL, NULL);
847     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(mw->scroll_win_h)
848             , GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
849     gtk_box_pack_start(GTK_BOX(mw->Vbox), mw->scroll_win_h
850             , TRUE, TRUE, 0);
851     mw->month_view_vbox = gtk_vbox_new(FALSE, 0);
852     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(mw->scroll_win_h)
853             , mw->month_view_vbox);
854     /*
855     gtk_container_add(GTK_CONTAINER(mw->scroll_win_h), mw->month_view_vbox);
856     */
857     /* row 1= day header buttons 
858      * row 2= full day events after the buttons */
859     mw->dtable_h = gtk_table_new(2 , days+2, FALSE);
860     gtk_box_pack_start(GTK_BOX(mw->month_view_vbox), mw->dtable_h
861             , FALSE, FALSE, 0);
862
863     tm_date = mw->startdate;
864
865     if (((tm_date.tm_year%4) == 0) && (((tm_date.tm_year%100) != 0) 
866             || ((tm_date.tm_year%400) == 0)))
867         ++monthdays[1];
868
869
870     i=0;
871     mw->Previous_toolbutton = gtk_event_box_new();
872     gtk_event_box_set_visible_window(GTK_EVENT_BOX(mw->Previous_toolbutton), FALSE);
873     gtk_container_set_border_width(GTK_CONTAINER(mw->Previous_toolbutton), 0);
874     arrow = gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_NONE);
875     gtk_container_add(GTK_CONTAINER(mw->Previous_toolbutton), arrow);
876     gtk_table_attach(GTK_TABLE(mw->dtable_h), mw->Previous_toolbutton, i, i+1, 0, 1
877                 , (GTK_FILL), (0), 0, 0);
878     gtk_widget_show_all(mw->Previous_toolbutton);
879     g_signal_connect((gpointer)mw->Previous_toolbutton, "button_release_event"
880             , G_CALLBACK(on_Previous_clicked), mw);
881     CLAWS_SET_TIP(mw->Previous_toolbutton, _("Previous month"));
882     for (i = 1; i < days+1; i++) {
883         button = gtk_label_new(_(dayname[i-1]));
884
885         if (i == 8) {
886             gtk_widget_modify_fg(button, GTK_STATE_NORMAL, &mw->fg_sunday);
887         }
888         gtk_widget_set_size_request(button, mw->StartDate_button_req.width, -1);
889         g_object_set_data(G_OBJECT(button), "offset", GINT_TO_POINTER(i-1));
890         gtk_table_attach(GTK_TABLE(mw->dtable_h), button, i, i+1, 0, 1
891                 , (GTK_FILL), (0), 0, 0);
892     }
893
894     mw->Next_toolbutton = gtk_event_box_new();
895     gtk_event_box_set_visible_window(GTK_EVENT_BOX(mw->Next_toolbutton), FALSE);
896     gtk_container_set_border_width(GTK_CONTAINER(mw->Next_toolbutton), 0);
897     arrow = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
898     gtk_container_add(GTK_CONTAINER(mw->Next_toolbutton), arrow);
899     gtk_table_attach(GTK_TABLE(mw->dtable_h), mw->Next_toolbutton, i, i+1, 0, 1
900                 , (GTK_FILL), (0), 0, 0);
901     gtk_widget_show_all(mw->Next_toolbutton);
902     g_signal_connect((gpointer)mw->Next_toolbutton, "button_release_event"
903             , G_CALLBACK(on_Next_clicked), mw);
904     CLAWS_SET_TIP(mw->Next_toolbutton, _("Next month"));
905
906     /****** body of day table ******/
907     mw->scroll_win = gtk_scrolled_window_new(NULL, NULL);
908     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(mw->scroll_win)
909             , GTK_SHADOW_NONE);
910     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(mw->scroll_win)
911             , GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
912     gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(mw->scroll_win)
913             , GTK_CORNER_TOP_LEFT);
914     gtk_box_pack_start(GTK_BOX(mw->month_view_vbox), mw->scroll_win
915             , TRUE, TRUE, 0);
916     vp = gtk_viewport_new(NULL, NULL);
917     gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_IN);
918     gtk_container_add(GTK_CONTAINER(mw->scroll_win), vp);
919     mw->dtable = gtk_table_new(6, days+2, FALSE);
920     gtk_container_add(GTK_CONTAINER(vp), mw->dtable);
921
922     gtk_widget_show_all(mw->dtable_h);
923
924     date = g_date_new_dmy(1, 1, tm_date.tm_year+1900);
925     first_week = g_date_get_monday_week_of_year(date);
926
927     if (first_week == 0)
928         first_week = 1;
929     else
930         first_week = 0;
931     g_date_free(date);
932
933     /* hours column = hour rows */
934     for (i = 0; i <= 6; i++) {
935         int day;
936         for (day = 1; day <= monthdays[tm_date.tm_mon]; day++) {
937             date = g_date_new_dmy(day, tm_date.tm_mon+1, tm_date.tm_year+1900);
938             int row = (int)g_date_get_monday_week_of_year(date);
939             if (weekoffset == -1) {
940                 weekoffset = row;
941             }
942             if (row - weekoffset == i) {
943                 gchar *wn = g_strdup_printf("%d", row+first_week > 53?1:row+first_week);
944                 fill_hour(mw, 0, i, wn);
945                 fill_hour(mw, days+1, i, "");
946                 g_free(wn);
947                 g_date_free(date);
948                 break;
949             }
950             g_date_free(date);
951         }
952     }
953     fill_days(mw, days, mw->item);
954 }
955
956 void refresh_month_win(month_win *mw)
957 {
958     gtk_widget_destroy(mw->scroll_win_h);
959     build_month_view_table(mw);
960     gtk_widget_show_all(mw->scroll_win_h);
961 }
962
963 month_win *create_month_win(FolderItem *item, struct tm tmdate)
964 {
965     month_win *mw;
966     char *start_date = get_locale_date(&tmdate);
967
968     /* initialisation + main window + base vbox */
969     mw = g_new0(month_win, 1);
970     mw->scroll_pos = -1; /* not set */
971
972     mw->accel_group = gtk_accel_group_new();
973
974     while (tmdate.tm_mday != 1)
975         orage_move_day(&tmdate, -1);
976
977     mw->startdate = tmdate;
978
979     mw->Vbox = gtk_vbox_new(FALSE, 0);
980
981     mw->item = item;
982     build_month_view_colours(mw);
983     build_month_view_header(mw, start_date);
984     build_month_view_table(mw);
985     gtk_widget_show_all(mw->Vbox);
986     mw->selsig = vcal_view_set_calendar_page(mw->Vbox, 
987                     G_CALLBACK(mw_summary_selected), mw);
988
989     vcal_view_create_popup_menus(mw->Vbox, &mw->view_menu,
990                                  &mw->event_menu, &mw->event_group,
991                                  &mw->ui_manager);
992     return(mw);
993 }