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