Improve some sieve-related translations
[claws.git] / src / plugins / vcalendar / day-view.c
blob156d813b048a6d53bf4acc74f46eebd1ec100e04
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
4 * Copyright (c) 2007-2008 Juha Kautto (juha at xfce.org)
5 * Copyright (c) 2008-2022 the Claws Mail Team and Colin Leroy (colin@colino.net)
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.
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.
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.
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #include "claws-features.h"
25 #endif
27 #include <stddef.h>
28 #include <glib.h>
29 #include <glib/gi18n.h>
30 #include "defs.h"
32 #ifdef USE_PTHREAD
33 #include <pthread.h>
34 #endif
35 #include <string.h>
36 #include <time.h>
38 #include <glib/gprintf.h>
39 #include <gdk/gdkkeysyms.h>
40 #include <gdk/gdk.h>
41 #include <gtk/gtk.h>
43 #include "summaryview.h"
44 #include "vcalendar.h"
45 #include "vcal_folder.h"
46 #include "vcal_prefs.h"
47 #include "vcal_manager.h"
48 #include "common-views.h"
49 #include "vcal_meeting_gtk.h"
51 #define MAX_DAYS 40
52 struct _day_win
54 GtkAccelGroup *accel_group;
56 GtkWidget *Window;
57 GtkWidget *Vbox;
59 GtkWidget *Menubar;
60 GtkWidget *File_menu;
61 GtkWidget *File_menu_new;
62 GtkWidget *File_menu_close;
63 GtkWidget *View_menu;
64 GtkWidget *View_menu_refresh;
65 GtkWidget *Go_menu;
66 GtkWidget *Go_menu_today;
67 GtkWidget *Go_menu_prev;
68 GtkWidget *Go_menu_next;
70 GtkWidget *Toolbar;
71 GtkWidget *Create_toolbutton;
72 GtkWidget *Previous_toolbutton;
73 GtkWidget *Today_toolbutton;
74 GtkWidget *Next_toolbutton;
75 GtkWidget *Refresh_toolbutton;
76 GtkWidget *Close_toolbutton;
78 GtkWidget *StartDate_button;
79 GtkRequisition StartDate_button_req;
80 GtkWidget *day_spin;
82 GtkWidget *day_view_vbox;
83 GtkWidget *scroll_win_h;
84 GtkWidget *dtable_h; /* header of day table */
85 GtkWidget *scroll_win;
86 GtkWidget *dtable; /* day table */
87 GtkRequisition hour_req;
89 GtkWidget *header[MAX_DAYS];
90 GtkWidget *element[24][MAX_DAYS];
91 GtkWidget *line[24][MAX_DAYS];
93 guint upd_timer;
94 gdouble scroll_pos; /* remember the scroll position */
96 GdkColor bg1, bg2, line_color, bg_today, fg_sunday;
97 GList *apptw_list; /* keep track of appointments being updated */
98 struct tm startdate;
99 FolderItem *item;
100 gulong selsig;
101 GtkWidget *view_menu;
102 GtkWidget *event_menu;
103 GtkActionGroup *event_group;
104 GtkUIManager *ui_manager;
107 static gchar *get_locale_date(struct tm *tmdate)
109 gchar *d = g_malloc(100);
110 strftime(d, 99, "%x", tmdate);
111 return d;
114 static void set_scroll_position(day_win *dw)
116 GtkAdjustment *v_adj;
118 v_adj = gtk_scrolled_window_get_vadjustment(
119 GTK_SCROLLED_WINDOW(dw->scroll_win));
120 if (dw->scroll_pos > 0) /* we have old value */
121 gtk_adjustment_set_value(v_adj, dw->scroll_pos);
122 else if (dw->scroll_pos < 0)
123 /* default: let's try to start roughly from line 8 = 8 o'clock */
124 gtk_adjustment_set_value(v_adj,
125 gtk_adjustment_get_upper(v_adj) / 3);
128 static gboolean scroll_position_timer(gpointer user_data)
130 set_scroll_position((day_win *)user_data);
131 return(FALSE); /* only once */
134 static void get_scroll_position(day_win *dw)
136 GtkAdjustment *v_adj;
138 v_adj = gtk_scrolled_window_get_vadjustment(
139 GTK_SCROLLED_WINDOW(dw->scroll_win));
140 dw->scroll_pos = gtk_adjustment_get_value(v_adj);
143 void dw_close_window(day_win *dw)
145 vcal_view_set_summary_page(dw->Vbox, dw->selsig);
147 g_free(dw);
148 dw = NULL;
151 static char *orage_tm_date_to_i18_date(struct tm *tm_date)
153 static char i18_date[32];
154 struct tm t;
155 t.tm_mday = tm_date->tm_mday;
156 t.tm_mon = tm_date->tm_mon - 1;
157 t.tm_year = tm_date->tm_year - 1900;
158 t.tm_sec = 0;
159 t.tm_min = 0;
160 t.tm_hour = 0;
161 t.tm_wday = 0;
162 t.tm_yday = 0;
164 if (strftime(i18_date, 32, "%x", &t) == 0)
165 g_error("Orage: orage_tm_date_to_i18_date too long string in strftime");
166 return(i18_date);
169 static void changeSelectedDate(day_win *dw, gint day)
171 if (day > 0) {
172 do {
173 orage_move_day(&(dw->startdate), 1);
174 } while (--day > 0);
175 } else {
176 do {
177 orage_move_day(&(dw->startdate), -1);
178 } while (++day < 0);
182 static gint on_Previous_clicked(GtkWidget *button, GdkEventButton *event,
183 day_win *dw)
185 int days = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dw->day_spin));
186 changeSelectedDate(dw, -days);
187 refresh_day_win(dw);
188 return TRUE;
191 static gint on_Next_clicked(GtkWidget *button, GdkEventButton *event,
192 day_win *dw)
194 int days = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dw->day_spin));
195 changeSelectedDate(dw, +days);
196 refresh_day_win(dw);
197 return TRUE;
200 static void dw_summary_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
201 gint column, day_win *dw)
203 MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, row);
204 int days = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dw->day_spin));
206 if (msginfo && msginfo->msgid) {
207 VCalEvent *event = vcal_manager_load_event(msginfo->msgid);
208 if (event) {
209 time_t t_first = mktime(&dw->startdate);
210 time_t t_start = icaltime_as_timet(icaltime_from_string(event->dtstart));
211 struct tm tm_start;
212 gboolean changed = FALSE;
213 GtkAdjustment *v_adj;
215 #ifdef G_OS_WIN32
216 if (t_start < 0)
217 t_start = 1;
218 #endif
219 localtime_r(&t_start, &tm_start);
220 tm_start.tm_hour = tm_start.tm_min = tm_start.tm_sec = 0;
221 t_start = mktime(&tm_start);
223 while (t_start < t_first) {
224 changeSelectedDate(dw, -days);
225 t_first = mktime(&dw->startdate);
226 changed = TRUE;
228 while (t_start > t_first + (days-1)*24*60*60) {
229 changeSelectedDate(dw, +days);
230 t_first = mktime(&dw->startdate);
231 changed = TRUE;
234 t_start = icaltime_as_timet(icaltime_from_string(event->dtstart));
235 #ifdef G_OS_WIN32
236 if (t_start < 0)
237 t_start = 1;
238 #endif
239 localtime_r(&t_start, &tm_start);
240 if (changed) {
241 debug_print("changed from %s\n", event->summary);
242 v_adj = gtk_scrolled_window_get_vadjustment(
243 GTK_SCROLLED_WINDOW(dw->scroll_win));
244 #ifdef G_OS_WIN32
245 if (t_start < 0)
246 t_start = 1;
247 #endif
248 localtime_r(&t_start, &tm_start);
249 if (tm_start.tm_hour > 2)
250 gtk_adjustment_set_value(v_adj,
251 ((gtk_adjustment_get_upper(v_adj) - gtk_adjustment_get_page_size(v_adj))/((gdouble)24/(gdouble)(tm_start.tm_hour-2))));
252 else
253 gtk_adjustment_set_value(v_adj, 0);
254 refresh_day_win(dw);
257 vcal_manager_free_event(event);
261 static void day_view_new_meeting_cb(day_win *dw, gpointer data_i, gpointer data_s)
263 int offset = GPOINTER_TO_INT(data_i);
264 struct tm tm_date = dw->startdate;
265 int offset_h = offset % 1000;
266 int offset_d = (offset-offset_h) / 1000;
267 guint monthdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
268 int mon = tm_date.tm_mon;
270 if (((tm_date.tm_year%4) == 0) && (((tm_date.tm_year%100) != 0)
271 || ((tm_date.tm_year%400) == 0)))
272 monthdays[1] = 29;
274 if (offset_d > (int)monthdays[mon]) {
275 while (tm_date.tm_mday > 1)
276 orage_move_day(&tm_date, 1);
277 offset_d -= monthdays[mon];
280 while (offset_d > tm_date.tm_mday) {
281 orage_move_day(&tm_date, 1);
283 while (offset_d < tm_date.tm_mday) {
284 orage_move_day(&tm_date, -1);
286 tm_date.tm_hour = offset_h;
287 vcal_meeting_create_with_start(NULL, &tm_date);
290 static void day_view_edit_meeting_cb(day_win *dw, gpointer data_i, gpointer data_s)
292 const gchar *uid = (gchar *)data_s;
293 vcal_view_select_event (uid, dw->item, TRUE,
294 G_CALLBACK(dw_summary_selected), dw);
297 static void day_view_cancel_meeting_cb(day_win *dw, gpointer data_i, gpointer data_s)
299 const gchar *uid = (gchar *)data_s;
300 vcalendar_cancel_meeting(dw->item, uid);
303 static void day_view_today_cb(day_win *dw, gpointer data_i, gpointer data_s)
305 time_t now = time(NULL);
306 struct tm tm_today;
307 localtime_r(&now, &tm_today);
309 while (tm_today.tm_wday != 1)
310 orage_move_day(&tm_today, -1);
312 dw->startdate = tm_today;
313 refresh_day_win(dw);
316 static void header_button_clicked_cb(GtkWidget *button, day_win *dw)
318 int offset = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "offset"));
320 day_view_new_meeting_cb(dw, GINT_TO_POINTER(offset), NULL);
323 static void on_button_press_event_cb(GtkWidget *widget
324 , GdkEventButton *event, gpointer *user_data)
326 day_win *dw = (day_win *)user_data;
327 gchar *uid = g_object_get_data(G_OBJECT(widget), "UID");
328 gpointer offset = g_object_get_data(G_OBJECT(widget), "offset");
330 if (event->button == 1) {
331 if (uid)
332 vcal_view_select_event (uid, dw->item, (event->type==GDK_2BUTTON_PRESS),
333 G_CALLBACK(dw_summary_selected), dw);
335 if (event->button == 3) {
336 g_object_set_data(G_OBJECT(dw->Vbox), "menu_win",
337 dw);
338 g_object_set_data(G_OBJECT(dw->Vbox), "menu_data_i",
339 offset);
340 g_object_set_data(G_OBJECT(dw->Vbox), "menu_data_s",
341 uid);
342 g_object_set_data(G_OBJECT(dw->Vbox), "new_meeting_cb",
343 day_view_new_meeting_cb);
344 g_object_set_data(G_OBJECT(dw->Vbox), "edit_meeting_cb",
345 day_view_edit_meeting_cb);
346 g_object_set_data(G_OBJECT(dw->Vbox), "cancel_meeting_cb",
347 day_view_cancel_meeting_cb);
348 g_object_set_data(G_OBJECT(dw->Vbox), "go_today_cb",
349 day_view_today_cb);
350 if (uid)
351 gtk_menu_popup_at_pointer(GTK_MENU(dw->event_menu), NULL);
352 else
353 gtk_menu_popup_at_pointer(GTK_MENU(dw->view_menu), NULL);
357 static void add_row(day_win *dw, VCalEvent *event, gint days)
359 gint row, start_row, end_row;
360 gint col, start_col, end_col, first_col, last_col;
361 gint height, start_height, end_height;
362 gchar *text, *tip, *start_date, *end_date;
363 GtkWidget *ev, *lab, *hb;
364 time_t t_start, t_end;
365 struct tm tm_first, tm_start, tm_end;
367 /* First clarify timings */
368 t_start = icaltime_as_timet(icaltime_from_string(event->dtstart));
369 if (event->dtend && *event->dtend)
370 t_end = icaltime_as_timet(icaltime_from_string(event->dtend));
371 else
372 t_end = t_start;
374 #ifdef G_OS_WIN32
375 if (t_start < 0)
376 t_start = 1;
377 if (t_end < 0)
378 t_end = 1;
379 #endif
380 localtime_r(&t_start, &tm_start);
381 localtime_r(&t_end, &tm_end);
382 tm_first = dw->startdate;
384 tm_first.tm_year += 1900;
385 tm_first.tm_mon += 1;
386 tm_start.tm_year += 1900;
387 tm_start.tm_mon += 1;
388 tm_end.tm_year += 1900;
389 tm_end.tm_mon += 1;
391 start_col = orage_days_between(&tm_first, &tm_start)+1;
392 end_col = orage_days_between(&tm_first, &tm_end)+1;
394 if (end_col < 1)
395 return;
396 if (start_col > days)
397 return;
399 else {
400 col = start_col;
401 row = tm_start.tm_hour;
404 /* then add the appointment */
405 text = g_strdup(event->summary?event->summary : _("Unknown"));
406 ev = gtk_event_box_new();
407 lab = gtk_label_new(text);
408 gtk_label_set_xalign(GTK_LABEL(lab), 0.0);
409 gtk_label_set_yalign(GTK_LABEL(lab), 0.5);
410 gtk_label_set_ellipsize(GTK_LABEL(lab), PANGO_ELLIPSIZE_END);
411 gtk_container_add(GTK_CONTAINER(ev), lab);
413 if ((row % 2) == 1)
414 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &dw->bg1);
415 else
416 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &dw->bg2);
417 if (dw->element[row][col] == NULL) {
418 hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
419 dw->element[row][col] = hb;
421 else {
422 hb = dw->element[row][col];
423 /* FIXME: set some real bar here to make it visible that we
424 * have more than 1 appointment here
427 if (orage_days_between(&tm_start, &tm_end) == 0)
428 tip = g_strdup_printf("%s\n%02d:%02d-%02d:%02d\n%s"
429 , text, tm_start.tm_hour, tm_start.tm_min
430 , tm_end.tm_hour, tm_end.tm_min, event->description);
431 else {
432 /* we took the date in unnormalized format, so we need to do that now */
433 start_date = g_strdup(orage_tm_date_to_i18_date(&tm_start));
434 end_date = g_strdup(orage_tm_date_to_i18_date(&tm_end));
435 tip = g_strdup_printf("%s\n%s %02d:%02d - %s %02d:%02d\n%s"
436 , text
437 , start_date, tm_start.tm_hour, tm_start.tm_min
438 , end_date, tm_end.tm_hour, tm_end.tm_min
439 , event->description);
440 g_free(start_date);
441 g_free(end_date);
443 CLAWS_SET_TIP(ev, tip);
445 gtk_box_pack_start(GTK_BOX(hb2), ev, FALSE, FALSE, 0);
446 gtk_box_pack_start(GTK_BOX(hb), hb2, TRUE, TRUE, 0);
448 gtk_box_pack_start(GTK_BOX(hb), ev, TRUE, TRUE, 0);
449 g_object_set_data_full(G_OBJECT(ev), "UID", g_strdup(event->uid), g_free);
450 g_object_set_data(G_OBJECT(ev), "offset", GINT_TO_POINTER(tm_start.tm_mday*1000 + tm_start.tm_hour));
451 g_signal_connect((gpointer)ev, "button-press-event"
452 , G_CALLBACK(on_button_press_event_cb), dw);
453 g_free(tip);
454 g_free(text);
456 /* and finally draw the line to show how long the appointment is,
457 * but only if it is Busy type event (=availability != 0)
458 * and it is not whole day event */
459 if (TRUE) {
460 height = dw->StartDate_button_req.height;
462 * same_date = !strncmp(start_ical_time, end_ical_time, 8);
463 * */
464 if (start_col < 1)
465 first_col = 1;
466 else
467 first_col = start_col;
468 if (end_col > days)
469 last_col = days;
470 else
471 last_col = end_col;
472 for (col = first_col; col <= last_col; col++) {
473 if (col == start_col)
474 start_row = tm_start.tm_hour;
475 else
476 start_row = 0;
477 if (col == end_col)
478 end_row = tm_end.tm_hour;
479 else
480 end_row = 23;
481 for (row = start_row; row <= end_row; row++) {
482 if (row == tm_start.tm_hour && col == start_col)
483 start_height = tm_start.tm_min*height/60;
484 else
485 start_height = 0;
486 if (row == tm_end.tm_hour && col == end_col)
487 end_height = tm_end.tm_min*height/60;
488 else
489 end_height = height;
490 dw->line[row][col] = build_line(1, start_height
491 , 2, end_height-start_height, dw->line[row][col]
492 , &dw->line_color);
498 static void app_rows(day_win *dw, FolderItem *item)
500 GSList *events = vcal_get_events_list(item);
501 GSList *cur = NULL;
502 int days = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dw->day_spin));
503 for (cur = events; cur ; cur = cur->next) {
504 VCalEvent *event = (VCalEvent *) (cur->data);
505 add_row(dw, event, days);
506 vcal_manager_free_event(event);
508 g_slist_free(events);
511 static void app_data(day_win *dw, FolderItem *item)
513 app_rows(dw, item);
517 static void fill_days(day_win *dw, gint days, FolderItem *item, gint first_col_day)
519 gint row, col, height, width;
520 GtkWidget *ev, *hb;
522 height = dw->StartDate_button_req.height;
523 width = dw->StartDate_button_req.width;
525 /* first clear the structure */
526 for (col = 1; col < days+1; col++) {
527 dw->header[col] = NULL;
528 for (row = 0; row < 24; row++) {
529 dw->element[row][col] = NULL;
530 /* gdk_draw_rectangle(, , , left_x, top_y, width, height); */
531 dw->line[row][col] = build_line(0, 0, 3, height, NULL
532 , &dw->line_color);
536 app_data(dw, item);
538 for (col = 1; col < days+1; col++) {
539 hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
540 /* check if we have full day events and put them to header */
541 if (dw->header[col]) {
542 gtk_box_pack_start(GTK_BOX(hb), dw->header[col], TRUE, TRUE, 0);
543 gtk_widget_set_size_request(hb, width, -1);
545 else {
546 ev = gtk_event_box_new();
547 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &dw->bg2);
548 gtk_box_pack_start(GTK_BOX(hb), ev, TRUE, TRUE, 0);
550 gtk_widget_set_hexpand(hb, TRUE);
551 gtk_grid_attach(GTK_GRID(dw->dtable_h), hb, col, 1, 1, 1);
553 /* check rows */
554 for (row = 0; row < 24; row++) {
555 hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
556 if (row == 0) {
557 gtk_widget_set_size_request(hb, width, -1);
558 gtk_widget_set_hexpand(hb, TRUE);
560 if (dw->element[row][col]) {
561 gtk_box_pack_start(GTK_BOX(hb), dw->line[row][col]
562 , FALSE, FALSE, 0);
563 gtk_box_pack_start(GTK_BOX(hb), dw->element[row][col]
564 , TRUE, TRUE, 0);
565 gtk_widget_set_size_request(hb, width, -1);
566 gtk_widget_set_hexpand(hb, TRUE);
568 else {
569 ev = gtk_event_box_new();
570 g_object_set_data(G_OBJECT(ev), "offset", GINT_TO_POINTER(first_col_day*1000 + row));
571 g_signal_connect((gpointer)ev, "button-press-event"
572 , G_CALLBACK(on_button_press_event_cb), dw);
574 name = gtk_label_new(" ");
575 gtk_container_add(GTK_CONTAINER(ev), name);
577 if ((row % 2) == 1)
578 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &dw->bg1);
579 else
580 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &dw->bg2);
581 gtk_box_pack_start(GTK_BOX(hb), dw->line[row][col]
582 , FALSE, FALSE, 0);
583 gtk_box_pack_start(GTK_BOX(hb), ev, TRUE, TRUE, 0);
585 gtk_grid_attach(GTK_GRID(dw->dtable), hb, col, row, 1, 1);
587 first_col_day++;
591 static void build_day_view_header(day_win *dw, char *start_date)
593 GtkWidget *hbox, *label, *space_label;
594 SummaryView *summaryview = NULL;
595 int avail_w = 0, avail_d = 0;
597 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
599 label = gtk_label_new(_("Start"));
600 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
602 /* start date button */
603 dw->StartDate_button = gtk_button_new();
604 gtk_box_pack_start(GTK_BOX(hbox), dw->StartDate_button, FALSE, FALSE, 0);
606 space_label = gtk_label_new(" ");
607 gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
609 space_label = gtk_label_new(" ");
610 gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
612 label = gtk_label_new(_("Show"));
613 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
615 /* show days spin = how many days to show */
616 dw->day_spin = gtk_spin_button_new_with_range(1, MAX_DAYS, 1);
617 gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(dw->day_spin), TRUE);
618 gtk_widget_set_size_request(dw->day_spin, 40, -1);
619 gtk_box_pack_start(GTK_BOX(hbox), dw->day_spin, FALSE, FALSE, 0);
620 label = gtk_label_new(_("days"));
621 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
623 space_label = gtk_label_new(" ");
624 gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
626 /* sizes */
627 gtk_button_set_label(GTK_BUTTON(dw->StartDate_button)
628 , (const gchar *)start_date);
629 gtk_widget_get_preferred_size(dw->StartDate_button, &dw->StartDate_button_req, &dw->StartDate_button_req);
630 dw->StartDate_button_req.width += dw->StartDate_button_req.width/10;
631 label = gtk_label_new("00");
632 gtk_widget_get_preferred_size(label, &dw->hour_req, &dw->hour_req);
634 if (mainwindow_get_mainwindow()) {
635 GtkAllocation allocation;
636 summaryview = mainwindow_get_mainwindow()->summaryview;
637 gtk_widget_get_allocation(summaryview->mainwidget_book,
638 &allocation);
640 avail_w = allocation.width - 20 - 2*(dw->hour_req.width);
641 avail_d = (dw->StartDate_button_req.width > 0)? avail_w / dw->StartDate_button_req.width : avail_w;
643 if (avail_d >= 7) {
644 avail_d = 7;
645 gtk_widget_set_size_request(dw->StartDate_button, avail_w / avail_d, -1);
646 gtk_widget_get_preferred_size(dw->StartDate_button, &dw->StartDate_button_req, &dw->StartDate_button_req);
649 /* initial values */
650 if (avail_d)
651 gtk_spin_button_set_value(GTK_SPIN_BUTTON(dw->day_spin), avail_d);
655 static void build_day_view_colours(day_win *dw)
657 GtkStyle *def_style, *cur_style;
658 GtkWidget *ctree = NULL;
659 def_style = gtk_widget_get_default_style();
661 if (mainwindow_get_mainwindow()) {
662 ctree = mainwindow_get_mainwindow()->summaryview->ctree;
664 if (ctree) {
665 cur_style = gtk_widget_get_style(ctree);
666 dw->bg1 = cur_style->bg[GTK_STATE_NORMAL];
667 dw->bg2 = cur_style->bg[GTK_STATE_NORMAL];
668 } else {
669 dw->bg1 = def_style->bg[GTK_STATE_NORMAL];
670 dw->bg2 = def_style->bg[GTK_STATE_NORMAL];
672 dw->bg1.red += (dw->bg1.red < 63000 ? 2000 : -2000);
673 dw->bg1.green += (dw->bg1.green < 63000 ? 2000 : -2000);
674 dw->bg1.blue += (dw->bg1.blue < 63000 ? 2000 : -2000);
676 dw->bg2.red += (dw->bg2.red > 1000 ? -1000 : 1000);
677 dw->bg2.green += (dw->bg2.green > 1000 ? -1000 : 1000);
678 dw->bg2.blue += (dw->bg2.blue > 1000 ? -1000 : 1000);
680 if (!gdk_color_parse("white", &dw->line_color)) {
681 g_warning("color parse failed: white");
682 dw->line_color.red = 239 * (65535/255);
683 dw->line_color.green = 235 * (65535/255);
684 dw->line_color.blue = 230 * (65535/255);
687 if (!gdk_color_parse("blue", &dw->fg_sunday)) {
688 g_warning("color parse failed: blue");
689 dw->fg_sunday.red = 10 * (65535/255);
690 dw->fg_sunday.green = 10 * (65535/255);
691 dw->fg_sunday.blue = 255 * (65535/255);
694 if (!gdk_color_parse("gold", &dw->bg_today)) {
695 g_warning("color parse failed: gold");
696 dw->bg_today.red = 255 * (65535/255);
697 dw->bg_today.green = 215 * (65535/255);
698 dw->bg_today.blue = 115 * (65535/255);
701 if (ctree) {
702 cur_style = gtk_widget_get_style(ctree);
703 dw->fg_sunday.red = (dw->fg_sunday.red + cur_style->fg[GTK_STATE_SELECTED].red)/2;
704 dw->fg_sunday.green = (dw->fg_sunday.green + cur_style->fg[GTK_STATE_SELECTED].red)/2;
705 dw->fg_sunday.blue = (3*dw->fg_sunday.blue + cur_style->fg[GTK_STATE_SELECTED].red)/4;
706 dw->bg_today.red = (3*dw->bg_today.red + cur_style->bg[GTK_STATE_NORMAL].red)/4;
707 dw->bg_today.green = (3*dw->bg_today.green + cur_style->bg[GTK_STATE_NORMAL].red)/4;
708 dw->bg_today.blue = (3*dw->bg_today.blue + cur_style->bg[GTK_STATE_NORMAL].red)/4;
712 static void fill_hour(day_win *dw, gint col, gint row, char *text)
714 GtkWidget *name, *ev;
716 ev = gtk_event_box_new();
717 name = gtk_label_new(text);
718 gtk_container_add(GTK_CONTAINER(ev), name);
719 if ((row % 2) == 1)
720 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &dw->bg1);
721 else
722 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &dw->bg2);
723 gtk_widget_set_size_request(ev, dw->hour_req.width
724 , dw->StartDate_button_req.height);
725 if (text)
726 gtk_grid_attach(GTK_GRID(dw->dtable), ev, col, row, 1, 1);
727 else /* special, needed for header table full day events */
728 gtk_grid_attach(GTK_GRID(dw->dtable_h), ev, col, row, 1, 1);
731 static void build_day_view_table(day_win *dw)
733 gint days; /* number of days to show */
734 gint i = 0;
735 GtkWidget *label, *button;
736 char text[5+1], *date, *today;
737 struct tm tm_date, tm_today;
738 guint monthdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
739 GtkWidget *vp;
740 time_t t = time(NULL);
741 GtkWidget *arrow;
742 gchar *tip;
743 gint first_col_day = -1;
745 #ifdef G_OS_WIN32
746 if (t < 0)
747 t = 1;
748 #endif
749 localtime_r(&t, &tm_today);
750 days = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dw->day_spin));
751 today = get_locale_date(&tm_today);
752 /****** header of day table = days columns ******/
753 dw->scroll_win_h = gtk_scrolled_window_new(NULL, NULL);
754 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dw->scroll_win_h)
755 , GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
756 gtk_box_pack_start(GTK_BOX(dw->Vbox), dw->scroll_win_h
757 , TRUE, TRUE, 0);
758 dw->day_view_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
759 gtk_container_add(GTK_CONTAINER(dw->scroll_win_h), dw->day_view_vbox);
760 /* row 1= day header buttons
761 * row 2= full day events after the buttons */
762 dw->dtable_h = gtk_grid_new();
763 gtk_box_pack_start(GTK_BOX(dw->day_view_vbox), dw->dtable_h
764 , FALSE, FALSE, 0);
766 tm_date = dw->startdate;
768 if (((tm_date.tm_year%4) == 0) && (((tm_date.tm_year%100) != 0)
769 || ((tm_date.tm_year%400) == 0)))
770 monthdays[1] = 29;
773 i=0;
774 dw->Previous_toolbutton = gtk_event_box_new();
775 gtk_event_box_set_visible_window(GTK_EVENT_BOX(dw->Previous_toolbutton), FALSE);
776 gtk_container_set_border_width(GTK_CONTAINER(dw->Previous_toolbutton), 0);
777 arrow = gtk_image_new_from_icon_name("pan-start-symbolic", GTK_ICON_SIZE_MENU);
778 gtk_container_add(GTK_CONTAINER(dw->Previous_toolbutton), arrow);
779 gtk_grid_attach(GTK_GRID(dw->dtable_h), dw->Previous_toolbutton, i, 0, 1, 1);
780 gtk_widget_show_all(dw->Previous_toolbutton);
781 g_signal_connect((gpointer)dw->Previous_toolbutton, "button_release_event"
782 , G_CALLBACK(on_Previous_clicked), dw);
783 tip = g_strdup_printf("Back %d days", days);
784 CLAWS_SET_TIP(dw->Previous_toolbutton, tip);
785 g_free(tip);
786 for (i = 1; i < days+1; i++) {
787 tip = g_malloc(100);
788 strftime(tip, 99, "%A %d %B %Y", &tm_date);
789 if (first_col_day == -1)
790 first_col_day = tm_date.tm_mday;
791 date = get_locale_date(&tm_date);
792 button = gtk_button_new();
793 gtk_button_set_label(GTK_BUTTON(button), date);
794 if (strcmp(today, date) == 0) {
795 gtk_widget_modify_bg(button, GTK_STATE_NORMAL, &dw->bg_today);
798 if (tm_date.tm_wday % 7 == 0) {
799 label = gtk_bin_get_child(GTK_BIN(button));
800 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dw->fg_sunday);
802 CLAWS_SET_TIP(button, tip);
803 g_free(tip);
804 gtk_widget_set_size_request(button, dw->StartDate_button_req.width, -1);
805 g_signal_connect((gpointer)button, "clicked"
806 , G_CALLBACK(header_button_clicked_cb), dw);
807 g_object_set_data(G_OBJECT(button), "offset", GINT_TO_POINTER(tm_date.tm_mday*1000));
808 gtk_widget_set_hexpand(button, TRUE);
810 gtk_grid_attach(GTK_GRID(dw->dtable_h), button, i, 0, 1, 1);
812 if (++tm_date.tm_mday == (int)(monthdays[tm_date.tm_mon]+1)) {
813 if (++tm_date.tm_mon == 12) {
814 ++tm_date.tm_year;
815 tm_date.tm_mon = 0;
817 tm_date.tm_mday = 1;
819 ++tm_date.tm_wday; tm_date.tm_wday %=7;
820 g_free(date);
823 dw->Next_toolbutton = gtk_event_box_new();
824 gtk_event_box_set_visible_window(GTK_EVENT_BOX(dw->Next_toolbutton), FALSE);
825 gtk_container_set_border_width(GTK_CONTAINER(dw->Next_toolbutton), 0);
826 arrow = gtk_image_new_from_icon_name("pan-end-symbolic", GTK_ICON_SIZE_MENU);
827 gtk_container_add(GTK_CONTAINER(dw->Next_toolbutton), arrow);
828 gtk_grid_attach(GTK_GRID(dw->dtable_h), dw->Next_toolbutton, i, 0, 1, 1);
829 gtk_widget_show_all(dw->Next_toolbutton);
830 g_signal_connect((gpointer)dw->Next_toolbutton, "button_release_event"
831 , G_CALLBACK(on_Next_clicked), dw);
832 tip = g_strdup_printf("Forward %d days", days);
833 CLAWS_SET_TIP(dw->Next_toolbutton, tip);
834 g_free(tip);
835 g_free(today);
837 /****** body of day table ******/
838 dw->scroll_win = gtk_scrolled_window_new(NULL, NULL);
839 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(dw->scroll_win)
840 , GTK_SHADOW_NONE);
841 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dw->scroll_win)
842 , GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
843 gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(dw->scroll_win)
844 , GTK_CORNER_TOP_LEFT);
845 gtk_box_pack_start(GTK_BOX(dw->day_view_vbox), dw->scroll_win
846 , TRUE, TRUE, 0);
847 vp = gtk_viewport_new(NULL, NULL);
848 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_NONE);
849 gtk_container_add(GTK_CONTAINER(dw->scroll_win), vp);
850 dw->dtable = gtk_grid_new();
851 gtk_container_add(GTK_CONTAINER(vp), dw->dtable);
853 gtk_widget_show_all(dw->dtable_h);
854 /* hours column = hour rows */
855 for (i = 0; i < 24; i++) {
856 g_sprintf(text, "%02d", i);
857 /* ev is needed for background colour */
858 fill_hour(dw, 0, i, text);
859 fill_hour(dw, days+1, i, text);
861 fill_days(dw, days, dw->item, first_col_day);
864 void refresh_day_win(day_win *dw)
866 get_scroll_position(dw);
867 gtk_widget_destroy(dw->scroll_win_h);
868 build_day_view_table(dw);
869 gtk_widget_show_all(dw->scroll_win_h);
870 /* I was not able to get this work without the timer. Ugly yes, but
871 * it works and does not hurt - much */
872 g_timeout_add(100, (GSourceFunc)scroll_position_timer, (gpointer)dw);
875 day_win *create_day_win(FolderItem *item, struct tm tmdate)
877 day_win *dw;
878 char *start_date = get_locale_date(&tmdate);
880 /* initialisation + main window + base vbox */
881 dw = g_new0(day_win, 1);
882 dw->scroll_pos = -1; /* not set */
884 dw->accel_group = gtk_accel_group_new();
886 while (tmdate.tm_wday != 1)
887 orage_move_day(&tmdate, -1);
889 dw->startdate = tmdate;
890 dw->startdate.tm_hour = 0;
891 dw->startdate.tm_min = 0;
892 dw->startdate.tm_sec = 0;
893 dw->Vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
894 gtk_widget_set_name(GTK_WIDGET(dw->Vbox), "vcal_day_win");
896 dw->item = item;
897 build_day_view_colours(dw);
898 build_day_view_header(dw, start_date);
899 build_day_view_table(dw);
900 gtk_widget_show_all(dw->Vbox);
901 dw->selsig = vcal_view_set_calendar_page(dw->Vbox,
902 G_CALLBACK(dw_summary_selected), dw);
903 vcal_view_create_popup_menus(dw->Vbox, &dw->view_menu,
904 &dw->event_menu, &dw->event_group,
905 &dw->ui_manager);
907 g_timeout_add(100, (GSourceFunc)scroll_position_timer, (gpointer)dw);
909 return(dw);