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 
get_locale_date(struct tm * tmdate)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 
mw_close_window(month_win * mw)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 
orage_tm_date_to_i18_date(struct tm * tm_date)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     t.tm_sec = 0;
156     t.tm_min = 0;
157     t.tm_hour = 0;
158     t.tm_wday = 0;
159     t.tm_yday = 0;
160     if (strftime(i18_date, 32, "%x", &t) == 0)
161         g_error("Orage: orage_tm_date_to_i18_date too long string in strftime");
162     return(i18_date);
163 }
164 
changeSelectedDate(month_win * mw,gint month)165 static void changeSelectedDate(month_win *mw, gint month)
166 {
167     gint curmon = mw->startdate.tm_mon;
168     if (month > 0) {
169      do { /* go to first of next month */
170       orage_move_day(&(mw->startdate), 1);
171      } while (curmon == mw->startdate.tm_mon);
172     } else {
173      do { /* go to last day of last month */
174       orage_move_day(&(mw->startdate), -1);
175      } while (curmon == mw->startdate.tm_mon);
176      do { /* go to first of last month */
177       orage_move_day(&(mw->startdate), -1);
178      } while (mw->startdate.tm_mday > 1);
179     }
180 }
181 
on_Previous_clicked(GtkWidget * button,GdkEventButton * event,month_win * mw)182 static gint on_Previous_clicked(GtkWidget *button, GdkEventButton *event,
183 				    month_win *mw)
184 {
185     changeSelectedDate(mw, -1);
186     refresh_month_win(mw);
187     return TRUE;
188 }
189 
on_Next_clicked(GtkWidget * button,GdkEventButton * event,month_win * mw)190 static gint on_Next_clicked(GtkWidget *button, GdkEventButton *event,
191 				    month_win *mw)
192 {
193     changeSelectedDate(mw, +1);
194     refresh_month_win(mw);
195     return TRUE;
196 }
197 
mw_summary_selected(GtkCMCTree * ctree,GtkCMCTreeNode * row,gint column,month_win * mw)198 static void mw_summary_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
199 			     gint column, month_win *mw)
200 {
201 	MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, row);
202 
203 	if (msginfo && msginfo->msgid) {
204 		VCalEvent *event = vcal_manager_load_event(msginfo->msgid);
205 		if (event) {
206 			struct tm tm_start;
207 			time_t t_start = icaltime_as_timet(icaltime_from_string(event->dtstart));
208 			gboolean changed = FALSE;
209 
210 #ifdef G_OS_WIN32
211 			if (t_start < 0)
212 				t_start = 1;
213 #endif
214 			localtime_r(&t_start, &tm_start);
215 			while (tm_start.tm_year < mw->startdate.tm_year) {
216 				changeSelectedDate(mw, -1);
217 				changed = TRUE;
218 			}
219 			while (tm_start.tm_year > mw->startdate.tm_year) {
220 				changeSelectedDate(mw, +1);
221 				changed = TRUE;
222 			}
223 			while (tm_start.tm_mon < mw->startdate.tm_mon) {
224 				changeSelectedDate(mw, -1);
225 				changed = TRUE;
226 			}
227 			while (tm_start.tm_mon > mw->startdate.tm_mon) {
228 				changeSelectedDate(mw, +1);
229 				changed = TRUE;
230 			}
231 			if (changed)
232 				refresh_month_win(mw);
233 		}
234 		vcal_manager_free_event(event);
235 	}
236 }
237 
month_view_new_meeting_cb(month_win * mw,gpointer data_i,gpointer data_s)238 static void month_view_new_meeting_cb(month_win *mw, gpointer data_i, gpointer data_s)
239 {
240     int offset = GPOINTER_TO_INT(data_i);
241     struct tm tm_date = mw->startdate;
242 
243     while (offset > tm_date.tm_mday) {
244 	    orage_move_day(&tm_date, 1);
245     }
246     while (offset < tm_date.tm_mday) {
247 	    orage_move_day(&tm_date, -1);
248     }
249     tm_date.tm_hour = 0;
250     vcal_meeting_create_with_start(NULL, &tm_date);
251 }
252 
month_view_edit_meeting_cb(month_win * mw,gpointer data_i,gpointer data_s)253 static void month_view_edit_meeting_cb(month_win *mw, gpointer data_i, gpointer data_s)
254 {
255 	const gchar *uid = (gchar *)data_s;
256         vcal_view_select_event (uid, mw->item, TRUE,
257 		    	    G_CALLBACK(mw_summary_selected), mw);
258 }
259 
month_view_cancel_meeting_cb(month_win * mw,gpointer data_i,gpointer data_s)260 static void month_view_cancel_meeting_cb(month_win *mw, gpointer data_i, gpointer data_s)
261 {
262 	const gchar *uid = (gchar *)data_s;
263 	vcalendar_cancel_meeting(mw->item, uid);
264 }
265 
month_view_today_cb(month_win * mw,gpointer data_i,gpointer data_s)266 static void month_view_today_cb(month_win *mw, gpointer data_i, gpointer data_s)
267 {
268     time_t now = time(NULL);
269     struct tm tm_today;
270     localtime_r(&now, &tm_today);
271 
272     while (tm_today.tm_mday != 1)
273     	orage_move_day(&tm_today, -1);
274 
275     mw->startdate = tm_today;
276     refresh_month_win(mw);
277 }
278 
header_button_clicked_cb(GtkWidget * button,GdkEventButton * event,gpointer * user_data)279 static void header_button_clicked_cb(GtkWidget *button
280         , GdkEventButton *event, gpointer *user_data)
281 {
282     month_win *mw = (month_win *)user_data;
283     int offset = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "day"));
284 
285     if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
286 	    month_view_new_meeting_cb(mw, GINT_TO_POINTER(offset), NULL);
287     }
288     if (event->button == 3) {
289 		g_object_set_data(G_OBJECT(mw->Vbox), "menu_win",
290 			  mw);
291 		g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_i",
292 			  GINT_TO_POINTER(offset));
293 		g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_s",
294 			  NULL);
295 		g_object_set_data(G_OBJECT(mw->Vbox), "new_meeting_cb",
296 			  month_view_new_meeting_cb);
297 		g_object_set_data(G_OBJECT(mw->Vbox), "go_today_cb",
298 			  month_view_today_cb);
299 		gtk_menu_popup(GTK_MENU(mw->view_menu),
300 			       NULL, NULL, NULL, NULL,
301 			       event->button, event->time);
302     }
303 }
304 
on_button_press_event_cb(GtkWidget * widget,GdkEventButton * event,gpointer * user_data)305 static void on_button_press_event_cb(GtkWidget *widget
306         , GdkEventButton *event, gpointer *user_data)
307 {
308     month_win *mw = (month_win *)user_data;
309     gchar *uid = g_object_get_data(G_OBJECT(widget), "UID");;
310     gpointer offset = g_object_get_data(G_OBJECT(widget), "offset");
311 
312     if (event->button == 1) {
313 	if (uid)
314             vcal_view_select_event (uid, mw->item, (event->type==GDK_2BUTTON_PRESS),
315 		    	    G_CALLBACK(mw_summary_selected), mw);
316 	else if (event->type == GDK_2BUTTON_PRESS) {
317 	    month_view_new_meeting_cb(mw, GINT_TO_POINTER(offset), NULL);
318 	}
319     }
320     if (event->button == 3) {
321 	    g_object_set_data(G_OBJECT(mw->Vbox), "menu_win",
322 		      mw);
323 	    g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_i",
324 		      offset);
325 	    g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_s",
326 		      uid);
327 	    g_object_set_data(G_OBJECT(mw->Vbox), "new_meeting_cb",
328 		      month_view_new_meeting_cb);
329 	    g_object_set_data(G_OBJECT(mw->Vbox), "edit_meeting_cb",
330 		      month_view_edit_meeting_cb);
331 	    g_object_set_data(G_OBJECT(mw->Vbox), "cancel_meeting_cb",
332 		      month_view_cancel_meeting_cb);
333 	    g_object_set_data(G_OBJECT(mw->Vbox), "go_today_cb",
334 		      month_view_today_cb);
335 	    gtk_menu_popup(GTK_MENU(mw->event_menu),
336 			   NULL, NULL, NULL, NULL,
337 			   event->button, event->time);
338     }
339 }
340 
add_row(month_win * mw,VCalEvent * event,gint days)341 static void add_row(month_win *mw, VCalEvent *event, gint days)
342 {
343     gint row, start_row, end_row, first_row, last_row;
344     gint col, start_col, end_col, first_col, last_col;
345     gint width, start_width, end_width;
346     gchar *text, *tip, *start_date, *end_date;
347     GtkWidget *ev = NULL, *lab = NULL, *hb;
348     time_t t_start, t_end;
349     struct tm tm_first, tm_start, tm_end;
350     guint monthdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
351     int weekoffset = -1;
352     gboolean pack = TRUE, update_tip = FALSE;
353     time_t now = time(NULL);
354     struct tm tm_today;
355     gboolean start_prev_mon = FALSE;
356 
357     localtime_r(&now, &tm_today);
358 
359     tm_today.tm_year += 1900;
360     tm_today.tm_mon++;
361 
362     /* First clarify timings */
363     t_start = icaltime_as_timet(icaltime_from_string(event->dtstart));
364     if (event->dtend && *event->dtend)
365         t_end = icaltime_as_timet(icaltime_from_string(event->dtend));
366     else
367 	t_end = t_start;
368 
369 #ifdef G_OS_WIN32
370 	if (t_start < 0)
371 		t_start = 1;
372 	if (t_end < 0)
373 		t_end = 1;
374 #endif
375     localtime_r(&t_start, &tm_start);
376     localtime_r(&t_end, &tm_end);
377     tm_first = mw->startdate;
378 
379     tm_first.tm_year += 1900;
380 
381     if (((tm_first.tm_year%4) == 0)
382     && (((tm_first.tm_year%100) != 0) || ((tm_first.tm_year%400) == 0)))
383         ++monthdays[1]; /* leap year, february has 29 days */
384 
385 
386     tm_first.tm_mon += 1;
387     tm_start.tm_year += 1900;
388     tm_start.tm_mon += 1;
389     tm_end.tm_year += 1900;
390     tm_end.tm_mon += 1;
391 
392     start_col = orage_days_between(&tm_first, &tm_start)+1;
393     end_col   = orage_days_between(&tm_first, &tm_end)+1;
394 
395     if (start_col < 0)
396         start_prev_mon = TRUE;
397 
398     if (end_col < 1) {
399     	return;
400     }
401     if (start_col > 0 && start_col > monthdays[tm_first.tm_mon-1]) {
402         return;
403     }
404     else {
405         GDate *edate = g_date_new_dmy(tm_end.tm_mday, tm_end.tm_mon, tm_end.tm_year);
406         GDate *fdate = g_date_new_dmy(1, tm_first.tm_mon, tm_first.tm_year);
407         GDate *sdate;
408         if (start_col >= 1) {
409           sdate = g_date_new_dmy(tm_start.tm_mday, tm_start.tm_mon, tm_start.tm_year);
410         } else {
411           sdate = g_date_new_dmy(1, tm_first.tm_mon, tm_first.tm_year);
412         } /* endif */
413 
414 	col = start_col = (int)g_date_get_weekday(sdate);
415 	end_col = (int)g_date_get_weekday(edate);
416 	weekoffset = (int)g_date_get_monday_week_of_year(fdate);
417 	row = start_row = (int)g_date_get_monday_week_of_year(sdate) - weekoffset;
418 	end_row = (int)g_date_get_monday_week_of_year(edate) - weekoffset;
419 	g_date_free(fdate);
420 	g_date_free(sdate);
421 	g_date_free(edate);
422     }
423 
424     if (end_col > 7)
425        end_col = 7;
426 
427     /* then add the appointment */
428     text = g_strdup(event->summary?event->summary : _("Unknown"));
429 
430     if (mw->element[row][col] == NULL) {
431         hb = gtk_vbox_new(TRUE, 1);
432         mw->element[row][col] = hb;
433     }
434     else {
435         GList *children = NULL;
436         hb = mw->element[row][col];
437         /* FIXME: set some real bar here to make it visible that we
438          * have more than 1 appointment here
439          */
440 	children = gtk_container_get_children(GTK_CONTAINER(hb));
441 	if (g_list_length(children) > 2) {
442 		pack = FALSE;
443 		update_tip = TRUE;
444 	} else if (g_list_length(children) > 1) {
445 		pack = FALSE;
446 		update_tip = FALSE;
447 	}
448 	g_list_free(children);
449     }
450     if (pack || !update_tip) {
451        ev = gtk_event_box_new();
452        lab = gtk_label_new(text);
453        gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
454        gtk_label_set_ellipsize(GTK_LABEL(lab), PANGO_ELLIPSIZE_END);
455        if ((row % 2) == 1)
456            gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg1);
457        else
458            gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg2);
459     } else if (!pack && update_tip) {
460        GList *children = gtk_container_get_children(GTK_CONTAINER(hb));
461        ev = GTK_WIDGET(g_list_last(children)->data);
462        g_list_free(children);
463     }
464 
465     if (ev && tm_start.tm_mday == tm_today.tm_mday && tm_start.tm_mon == tm_today.tm_mon
466         && tm_start.tm_year == tm_today.tm_year)
467 	gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg_today);
468 
469     if (orage_days_between(&tm_start, &tm_end) == 0)
470         tip = g_strdup_printf("%s\n%02d:%02d-%02d:%02d\n%s"
471                 , text, tm_start.tm_hour, tm_start.tm_min
472                 , tm_end.tm_hour, tm_end.tm_min, event->description);
473     else {
474 /* we took the date in unnormalized format, so we need to do that now */
475         start_date = g_strdup(orage_tm_date_to_i18_date(&tm_start));
476         end_date = g_strdup(orage_tm_date_to_i18_date(&tm_end));
477         tip = g_strdup_printf("%s\n%s %02d:%02d - %s %02d:%02d\n%s"
478                 , text
479                 , start_date, tm_start.tm_hour, tm_start.tm_min
480                 , end_date, tm_end.tm_hour, tm_end.tm_min
481                 , event->description);
482         g_free(start_date);
483         g_free(end_date);
484     }
485     if (pack) {
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 if (!update_tip) {
490         gtk_label_set_label(GTK_LABEL(lab), "...");
491         gtk_container_add(GTK_CONTAINER(ev), lab);
492 	CLAWS_SET_TIP(ev, tip);
493         gtk_box_pack_start(GTK_BOX(hb), ev, TRUE, TRUE, 0);
494     } else {
495 	gchar *old = gtk_widget_get_tooltip_text(ev);
496 	gchar *new = g_strdup_printf("%s\n\n%s", old?old:"", tip);
497 	g_free(old);
498 
499 	CLAWS_SET_TIP(ev, new);
500 	g_free(new);
501     }
502     g_object_set_data_full(G_OBJECT(ev), "UID", g_strdup(event->uid), g_free);
503     g_object_set_data(G_OBJECT(ev), "offset", GINT_TO_POINTER(tm_start.tm_mday));
504     g_signal_connect((gpointer)ev, "button-press-event"
505             , G_CALLBACK(on_button_press_event_cb), mw);
506     g_free(tip);
507     g_free(text);
508 
509     /* and finally draw the line to show how long the appointment is,
510      * but only if it is Busy type event (=availability != 0)
511      * and it is not whole day event */
512     if (TRUE) {
513         width = mw->StartDate_button_req.width;
514         /*
515          * same_date = !strncmp(start_ical_time, end_ical_time, 8);
516          * */
517         if (start_row < 0)
518             first_row = 0;
519         else
520             first_row = start_row;
521         if (end_row > 5)
522             last_row = 5;
523         else
524             last_row = end_row;
525         for (row = first_row; row <= last_row; row++) {
526             if (row == start_row)
527                 first_col = start_col;
528             else
529                 first_col = 0;
530             if (row == end_row)
531                 last_col   = end_col;
532             else
533                 last_col   = 7;
534             for (col = first_col; col <= last_col; col++) {
535                 if (row == start_row && col == start_col && !start_prev_mon)
536                     start_width = ((tm_start.tm_hour*60)+(tm_start.tm_min))*width/(24*60);
537                 else
538                     start_width = 0;
539                 if (row == last_row && col == last_col)
540                     end_width = ((tm_end.tm_hour*60)+(tm_end.tm_min))*width/(24*60);
541                 else
542                     end_width = width;
543 
544                 mw->line[row][col] = build_line(start_width, 0
545                         , end_width-start_width, 2, mw->line[row][col]
546 			, &mw->line_color);
547             }
548         }
549     }
550 }
551 
app_rows(month_win * mw,FolderItem * item)552 static void app_rows(month_win *mw, FolderItem *item)
553 {
554    GSList *events = vcal_get_events_list(item);
555    GSList *cur = NULL;
556    int days = 7;
557    for (cur = events; cur ; cur = cur->next) {
558    	VCalEvent *event = (VCalEvent *) (cur->data);
559 	add_row(mw, event, days);
560 	vcal_manager_free_event(event);
561    }
562    g_slist_free(events);
563 }
564 
app_data(month_win * mw,FolderItem * item)565 static void app_data(month_win *mw, FolderItem *item)
566 {
567     app_rows(mw, item);
568 }
569 
570 
fill_days(month_win * mw,gint days,FolderItem * item)571 static void fill_days(month_win *mw, gint days, FolderItem *item)
572 {
573     gint day, col, height, width;
574     GtkWidget *ev, *vb, *hb;
575     guint monthdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
576     struct tm t = mw->startdate;
577     int weekoffset = -1;
578     time_t now = time(NULL);
579     struct tm tm_today;
580 
581     localtime_r(&now, &tm_today);
582 
583     t.tm_year += 1900;
584     t.tm_mon++;
585     tm_today.tm_year += 1900;
586     tm_today.tm_mon++;
587 
588     if (((t.tm_year%4) == 0)
589     && (((t.tm_year%100) != 0) || ((t.tm_year%400) == 0)))
590         ++monthdays[1]; /* leap year, february has 29 days */
591 
592     height = mw->StartDate_button_req.height;
593     width = mw->StartDate_button_req.width;
594 
595     /* first clear the structure */
596     for (col = 1; col <  days+1; col++) {
597         mw->header[col] = NULL;
598     }
599     for (day = 0; day < MAX_DAYS; day++)
600     	for (col = 0; col < 6; col++)
601 		mw->line[col][day] = NULL;
602     for (day = 1; day <= monthdays[t.tm_mon-1]; day++) {
603 	GDate *date = g_date_new_dmy(day, t.tm_mon, t.tm_year);
604 	int dcol = (int)g_date_get_weekday(date);
605 	int row = (int)g_date_get_monday_week_of_year(date);
606 	if (weekoffset == -1) {
607 		weekoffset = row;
608 		row = 0;
609 	} else {
610 		row = row - weekoffset;
611 	}
612         mw->element[row][dcol] = NULL;
613         mw->line[row][dcol] = build_line(0, 0, width, 3, NULL
614 			, &mw->line_color);
615 	g_date_free(date);
616     }
617 
618     app_data(mw, item);
619 
620     /* check rows */
621     for (day = 1; day <= monthdays[t.tm_mon-1]; day++) {
622 	GDate *date = g_date_new_dmy(day, t.tm_mon, t.tm_year);
623 	int col = (int)g_date_get_weekday(date);
624 	int row = (int)g_date_get_monday_week_of_year(date);
625 	gchar *label;
626 	gchar *tmp;
627 	GtkWidget *name;
628 
629 	if (weekoffset == -1) {
630 		weekoffset = row;
631 		row = 0;
632 	} else {
633 		row = row - weekoffset;
634 	}
635         vb = gtk_vbox_new(FALSE, 0);
636         gtk_widget_set_size_request(vb, width, height);
637 	    if (g_date_get_day(date) == 1)
638     	        label = g_strdup_printf("%d %s", g_date_get_day(date),
639 				_(monthname[g_date_get_month(date)-1]));
640 	    else
641 	        label = g_strdup_printf("%d", g_date_get_day(date));
642 	    tmp = g_strdup_printf("%s %d %s %d",
643 	    			_(dayname[col-1]),
644 				g_date_get_day(date),
645 				_(monthname[g_date_get_month(date)-1]),
646 				g_date_get_year(date));
647 
648             ev = gtk_event_box_new();
649 	    g_object_set_data(G_OBJECT(ev), "day", GINT_TO_POINTER((int)g_date_get_day(date)));
650 	    g_signal_connect((gpointer)ev, "button-press-event"
651         	    , G_CALLBACK(header_button_clicked_cb), mw);
652             name = gtk_label_new(label);
653 	    gtk_misc_set_alignment(GTK_MISC(name), 0.0, 0.0);
654 
655 	    CLAWS_SET_TIP(ev, tmp);
656             gtk_container_add(GTK_CONTAINER(ev), name);
657 	    g_free(tmp);
658             g_free(label);
659 
660             if ((row % 2) == 1)
661                 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg1);
662             else
663                 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg2);
664 	    if (col == 7)
665 	    	gtk_widget_modify_fg(name, GTK_STATE_NORMAL, &mw->fg_sunday);
666 	    if (day == tm_today.tm_mday && t.tm_mon == tm_today.tm_mon && t.tm_year == tm_today.tm_year)
667 	    	gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg_today);
668 
669             hb = gtk_hbox_new(FALSE, 0);
670             gtk_box_pack_start(GTK_BOX(hb), ev, TRUE, TRUE, 1);
671             gtk_box_pack_start(GTK_BOX(vb), hb, TRUE, TRUE, 0);
672             if (mw->element[row][col]) {
673 		gtk_box_pack_start(GTK_BOX(vb), mw->element[row][col], TRUE, TRUE, 0);
674             }
675             gtk_box_pack_start(GTK_BOX(vb), mw->line[row][col]
676                     , FALSE, FALSE, 0);
677 
678         gtk_table_attach(GTK_TABLE(mw->dtable), vb, col, col+1, row, row+1
679                  , (GTK_FILL), (0), 0, 0);
680 	g_date_free(date);
681     }
682 }
683 
build_month_view_header(month_win * mw,char * start_date)684 static void build_month_view_header(month_win *mw, char *start_date)
685 {
686     GtkWidget *hbox, *label, *space_label;
687 
688     hbox = gtk_hbox_new(FALSE, 0);
689 
690     label = gtk_label_new(_("Start"));
691     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
692 
693     /* start date button */
694     mw->StartDate_button = gtk_button_new();
695     gtk_box_pack_start(GTK_BOX(hbox), mw->StartDate_button, FALSE, FALSE, 0);
696 
697     space_label = gtk_label_new("  ");
698     gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
699 
700     space_label = gtk_label_new("     ");
701     gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
702 
703     label = gtk_label_new(_("Show"));
704     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
705 
706     /* show days spin = how many days to show */
707     mw->day_spin = gtk_spin_button_new_with_range(1, MAX_DAYS, 1);
708     gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(mw->day_spin), TRUE);
709     gtk_widget_set_size_request(mw->day_spin, 40, -1);
710     gtk_box_pack_start(GTK_BOX(hbox), mw->day_spin, FALSE, FALSE, 0);
711     label = gtk_label_new(_("days"));
712     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
713 
714     space_label = gtk_label_new("   ");
715     gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
716 
717     /* sizes */
718     gtk_button_set_label(GTK_BUTTON(mw->StartDate_button)
719             , (const gchar *)start_date);
720     gtk_widget_size_request(mw->StartDate_button, &mw->StartDate_button_req);
721     mw->StartDate_button_req.width += mw->StartDate_button_req.width/10;
722     label = gtk_label_new("00");
723     gtk_widget_size_request(label, &mw->hour_req);
724 }
725 
build_month_view_colours(month_win * mw)726 static void build_month_view_colours(month_win *mw)
727 {
728     GtkStyle *def_style, *cur_style;
729     GdkColormap *pic1_cmap;
730     GtkWidget *ctree = NULL;
731     def_style = gtk_widget_get_default_style();
732     pic1_cmap = gdk_colormap_get_system();
733 
734     if (mainwindow_get_mainwindow()) {
735         ctree = mainwindow_get_mainwindow()->summaryview->ctree;
736     }
737     if (ctree) {
738         cur_style = gtk_widget_get_style(ctree);
739         mw->bg1 = cur_style->bg[GTK_STATE_NORMAL];
740         mw->bg2 = cur_style->bg[GTK_STATE_NORMAL];
741     } else {
742         mw->bg1 = def_style->bg[GTK_STATE_NORMAL];
743         mw->bg2 = def_style->bg[GTK_STATE_NORMAL];
744     }
745 
746     mw->bg1.red +=  (mw->bg1.red < 63000 ? 2000 : -2000);
747     mw->bg1.green += (mw->bg1.green < 63000 ? 2000 : -2000);
748     mw->bg1.blue += (mw->bg1.blue < 63000 ? 2000 : -2000);
749     gdk_colormap_alloc_color(pic1_cmap, &mw->bg1, FALSE, TRUE);
750 
751     mw->bg2.red +=  (mw->bg2.red > 1000 ? -1000 : 1000);
752     mw->bg2.green += (mw->bg2.green > 1000 ? -1000 : 1000);
753     mw->bg2.blue += (mw->bg2.blue > 1000 ? -1000 : 1000);
754     gdk_colormap_alloc_color(pic1_cmap, &mw->bg2, FALSE, TRUE);
755 
756     if (!gdk_color_parse("white", &mw->line_color)) {
757         g_warning("color parse failed: white");
758         mw->line_color.red =  239 * (65535/255);
759         mw->line_color.green = 235 * (65535/255);
760         mw->line_color.blue = 230 * (65535/255);
761     }
762 
763     if (!gdk_color_parse("blue", &mw->fg_sunday)) {
764         g_warning("color parse failed: blue");
765         mw->fg_sunday.red = 10 * (65535/255);
766         mw->fg_sunday.green = 10 * (65535/255);
767         mw->fg_sunday.blue = 255 * (65535/255);
768     }
769 
770     if (!gdk_color_parse("gold", &mw->bg_today)) {
771         g_warning("color parse failed: gold");
772         mw->bg_today.red = 255 * (65535/255);
773         mw->bg_today.green = 215 * (65535/255);
774         mw->bg_today.blue = 115 * (65535/255);
775     }
776 
777     if (ctree) {
778         cur_style = gtk_widget_get_style(ctree);
779         mw->fg_sunday.red = (mw->fg_sunday.red + cur_style->fg[GTK_STATE_SELECTED].red)/2;
780         mw->fg_sunday.green = (mw->fg_sunday.green + cur_style->fg[GTK_STATE_SELECTED].red)/2;
781         mw->fg_sunday.blue = (3*mw->fg_sunday.blue + cur_style->fg[GTK_STATE_SELECTED].red)/4;
782         mw->bg_today.red = (3*mw->bg_today.red + cur_style->bg[GTK_STATE_NORMAL].red)/4;
783         mw->bg_today.green = (3*mw->bg_today.green + cur_style->bg[GTK_STATE_NORMAL].red)/4;
784         mw->bg_today.blue = (3*mw->bg_today.blue + cur_style->bg[GTK_STATE_NORMAL].red)/4;
785     }
786     gdk_colormap_alloc_color(pic1_cmap, &mw->line_color, FALSE, TRUE);
787     gdk_colormap_alloc_color(pic1_cmap, &mw->fg_sunday, FALSE, TRUE);
788     gdk_colormap_alloc_color(pic1_cmap, &mw->bg_today, FALSE, TRUE);
789 }
790 
fill_hour(month_win * mw,gint col,gint row,char * text)791 static void fill_hour(month_win *mw, gint col, gint row, char *text)
792 {
793     GtkWidget *name, *ev;
794 
795     ev = gtk_event_box_new();
796     name = gtk_label_new(text);
797     gtk_misc_set_alignment(GTK_MISC(name), 0, 0.5);
798     CLAWS_SET_TIP(ev, _("Week number"));
799     gtk_container_add(GTK_CONTAINER(ev), name);
800     gtk_widget_set_size_request(ev, mw->hour_req.width
801             , mw->StartDate_button_req.height);
802     if (text)
803         gtk_table_attach(GTK_TABLE(mw->dtable), ev, col, col+1, row, row+1
804              , (GTK_FILL), (0), 0, 0);
805     else  /* special, needed for header table full day events */
806         gtk_table_attach(GTK_TABLE(mw->dtable_h), ev, col, col+1, row, row+1
807              , (GTK_FILL), (0), 0, 0);
808 }
809 
build_month_view_table(month_win * mw)810 static void build_month_view_table(month_win *mw)
811 {
812     gint days;   /* number of days to show */
813     gint i = 0;
814     GtkWidget *button;
815     struct tm tm_date, tm_today;
816     guint monthdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
817     GtkWidget *vp;
818     time_t t = time(NULL);
819     GtkWidget *arrow;
820     int avail_w = 0, avail_d = 7;
821     int avail_h = 0;
822     int weekoffset = -1;
823     GDate *date;
824     int first_week = 0;
825 
826     if (mainwindow_get_mainwindow()) {
827 		GtkAllocation allocation;
828 		SummaryView *summaryview = mainwindow_get_mainwindow()->summaryview;
829 		GTK_EVENTS_FLUSH();
830 		gtk_widget_get_allocation(summaryview->mainwidget_book,
831 				&allocation);
832 
833 		avail_w = allocation.width - 25 - 2*(mw->hour_req.width);
834 		avail_h = allocation.height - 20;
835 		if (avail_h < 250)
836 			avail_h = 250;
837 		/* avail_d = avail_w / mw->StartDate_button_req.width; */
838 	}
839 
840     gtk_widget_set_size_request(mw->StartDate_button, avail_w / avail_d,
841     			(avail_h)/6);
842     gtk_widget_size_request(mw->StartDate_button, &mw->StartDate_button_req);
843 
844     /* initial values */
845     gtk_spin_button_set_value(GTK_SPIN_BUTTON(mw->day_spin), avail_d);
846 
847 #ifdef G_OS_WIN32
848 	if (t < 0)
849 		t = 1;
850 #endif
851     localtime_r(&t, &tm_today);
852     days = 7;
853     /****** header of day table = days columns ******/
854     mw->scroll_win_h = gtk_scrolled_window_new(NULL, NULL);
855     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(mw->scroll_win_h)
856             , GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
857     gtk_box_pack_start(GTK_BOX(mw->Vbox), mw->scroll_win_h
858             , TRUE, TRUE, 0);
859     mw->month_view_vbox = gtk_vbox_new(FALSE, 0);
860     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(mw->scroll_win_h)
861             , mw->month_view_vbox);
862     /*
863     gtk_container_add(GTK_CONTAINER(mw->scroll_win_h), mw->month_view_vbox);
864     */
865     /* row 1= day header buttons
866      * row 2= full day events after the buttons */
867     mw->dtable_h = gtk_table_new(2 , days+2, FALSE);
868     gtk_box_pack_start(GTK_BOX(mw->month_view_vbox), mw->dtable_h
869             , FALSE, FALSE, 0);
870 
871     tm_date = mw->startdate;
872 
873     if (((tm_date.tm_year%4) == 0) && (((tm_date.tm_year%100) != 0)
874             || ((tm_date.tm_year%400) == 0)))
875         ++monthdays[1];
876 
877 
878     i=0;
879     mw->Previous_toolbutton = gtk_event_box_new();
880     gtk_event_box_set_visible_window(GTK_EVENT_BOX(mw->Previous_toolbutton), FALSE);
881     gtk_container_set_border_width(GTK_CONTAINER(mw->Previous_toolbutton), 0);
882     arrow = gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_NONE);
883     gtk_container_add(GTK_CONTAINER(mw->Previous_toolbutton), arrow);
884     gtk_table_attach(GTK_TABLE(mw->dtable_h), mw->Previous_toolbutton, i, i+1, 0, 1
885                 , (GTK_FILL), (0), 0, 0);
886     gtk_widget_show_all(mw->Previous_toolbutton);
887     g_signal_connect((gpointer)mw->Previous_toolbutton, "button_release_event"
888             , G_CALLBACK(on_Previous_clicked), mw);
889     CLAWS_SET_TIP(mw->Previous_toolbutton, _("Previous month"));
890     for (i = 1; i < days+1; i++) {
891         button = gtk_label_new(_(dayname[i-1]));
892 
893         gtk_widget_set_size_request(button, mw->StartDate_button_req.width, -1);
894         g_object_set_data(G_OBJECT(button), "offset", GINT_TO_POINTER(i-1));
895         gtk_table_attach(GTK_TABLE(mw->dtable_h), button, i, i+1, 0, 1
896                 , (GTK_FILL), (0), 0, 0);
897     }
898 
899     mw->Next_toolbutton = gtk_event_box_new();
900     gtk_event_box_set_visible_window(GTK_EVENT_BOX(mw->Next_toolbutton), FALSE);
901     gtk_container_set_border_width(GTK_CONTAINER(mw->Next_toolbutton), 0);
902     arrow = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
903     gtk_container_add(GTK_CONTAINER(mw->Next_toolbutton), arrow);
904     gtk_table_attach(GTK_TABLE(mw->dtable_h), mw->Next_toolbutton, i, i+1, 0, 1
905                 , (GTK_FILL), (0), 0, 0);
906     gtk_widget_show_all(mw->Next_toolbutton);
907     g_signal_connect((gpointer)mw->Next_toolbutton, "button_release_event"
908             , G_CALLBACK(on_Next_clicked), mw);
909     CLAWS_SET_TIP(mw->Next_toolbutton, _("Next month"));
910 
911     /****** body of day table ******/
912     mw->scroll_win = gtk_scrolled_window_new(NULL, NULL);
913     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(mw->scroll_win)
914             , GTK_SHADOW_NONE);
915     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(mw->scroll_win)
916             , GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
917     gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(mw->scroll_win)
918             , GTK_CORNER_TOP_LEFT);
919     gtk_box_pack_start(GTK_BOX(mw->month_view_vbox), mw->scroll_win
920             , TRUE, TRUE, 0);
921     vp = gtk_viewport_new(NULL, NULL);
922     gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_IN);
923     gtk_container_add(GTK_CONTAINER(mw->scroll_win), vp);
924     mw->dtable = gtk_table_new(6, days+2, FALSE);
925     gtk_container_add(GTK_CONTAINER(vp), mw->dtable);
926 
927     gtk_widget_show_all(mw->dtable_h);
928 
929     date = g_date_new_dmy(1, 1, tm_date.tm_year+1900);
930     first_week = g_date_get_monday_week_of_year(date);
931 
932     if (first_week == 0)
933     	first_week = 1;
934     else
935         first_week = 0;
936     g_date_free(date);
937 
938     /* hours column = hour rows */
939     for (i = 0; i <= 6; i++) {
940 	int day;
941 	for (day = 1; day <= monthdays[tm_date.tm_mon]; day++) {
942 	    date = g_date_new_dmy(day, tm_date.tm_mon+1, tm_date.tm_year+1900);
943 	    int row = (int)g_date_get_monday_week_of_year(date);
944 	    if (weekoffset == -1) {
945 		weekoffset = row;
946 	    }
947 	    if (row - weekoffset == i) {
948 	        gchar *wn = g_strdup_printf("%d", row+first_week > 53?1:row+first_week);
949                 fill_hour(mw, 0, i, wn);
950                 fill_hour(mw, days+1, i, "");
951 		g_free(wn);
952 		g_date_free(date);
953 		break;
954 	    }
955 	    g_date_free(date);
956 	}
957     }
958     fill_days(mw, days, mw->item);
959 }
960 
refresh_month_win(month_win * mw)961 void refresh_month_win(month_win *mw)
962 {
963     gtk_widget_destroy(mw->scroll_win_h);
964     build_month_view_table(mw);
965     gtk_widget_show_all(mw->scroll_win_h);
966 }
967 
create_month_win(FolderItem * item,struct tm tmdate)968 month_win *create_month_win(FolderItem *item, struct tm tmdate)
969 {
970     month_win *mw;
971     char *start_date = get_locale_date(&tmdate);
972 
973     /* initialisation + main window + base vbox */
974     mw = g_new0(month_win, 1);
975     mw->scroll_pos = -1; /* not set */
976 
977     mw->accel_group = gtk_accel_group_new();
978 
979     while (tmdate.tm_mday != 1)
980     	orage_move_day(&tmdate, -1);
981 
982     mw->startdate = tmdate;
983 
984     mw->Vbox = gtk_vbox_new(FALSE, 0);
985 
986     mw->item = item;
987     build_month_view_colours(mw);
988     build_month_view_header(mw, start_date);
989     build_month_view_table(mw);
990     gtk_widget_show_all(mw->Vbox);
991     mw->selsig = vcal_view_set_calendar_page(mw->Vbox,
992 		    G_CALLBACK(mw_summary_selected), mw);
993 
994     vcal_view_create_popup_menus(mw->Vbox, &mw->view_menu,
995 		    		 &mw->event_menu, &mw->event_group,
996 		    		 &mw->ui_manager);
997     return(mw);
998 }
999