Improve some sieve-related translations
[claws.git] / src / plugins / vcalendar / month-view.c
blob0529559f860b0d62d407da364c2294166d0f0d03
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-2021 the Claws Mail team and Colin Leroy
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 _month_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 *month_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[6][MAX_DAYS];
91 GtkWidget *line[6][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 gchar *dayname[7] = {
108 N_("Monday"),
109 N_("Tuesday"),
110 N_("Wednesday"),
111 N_("Thursday"),
112 N_("Friday"),
113 N_("Saturday"),
114 N_("Sunday")
116 gchar *monthname[12] = {
117 N_("January"),
118 N_("February"),
119 N_("March"),
120 N_("April"),
121 N_("May"),
122 N_("June"),
123 N_("July"),
124 N_("August"),
125 N_("September"),
126 N_("October"),
127 N_("November"),
128 N_("December")
131 static gchar *get_locale_date(struct tm *tmdate)
133 gchar *d = g_malloc(100);
134 strftime(d, 99, "%x", tmdate);
135 return d;
138 void mw_close_window(month_win *mw)
140 vcal_view_set_summary_page(mw->Vbox, mw->selsig);
143 g_free(mw);
144 mw = NULL;
147 static char *orage_tm_date_to_i18_date(struct tm *tm_date)
149 static char i18_date[32];
150 struct tm t;
151 t.tm_mday = tm_date->tm_mday;
152 t.tm_mon = tm_date->tm_mon - 1;
153 t.tm_year = tm_date->tm_year - 1900;
154 t.tm_sec = 0;
155 t.tm_min = 0;
156 t.tm_hour = 0;
157 t.tm_wday = 0;
158 t.tm_yday = 0;
159 if (strftime(i18_date, 32, "%x", &t) == 0)
160 g_error("Orage: orage_tm_date_to_i18_date too long string in strftime");
161 return(i18_date);
164 static void changeSelectedDate(month_win *mw, gint month)
166 gint curmon = mw->startdate.tm_mon;
167 if (month > 0) {
168 do { /* go to first of next month */
169 orage_move_day(&(mw->startdate), 1);
170 } while (curmon == mw->startdate.tm_mon);
171 } else {
172 do { /* go to last day of last month */
173 orage_move_day(&(mw->startdate), -1);
174 } while (curmon == mw->startdate.tm_mon);
175 do { /* go to first of last month */
176 orage_move_day(&(mw->startdate), -1);
177 } while (mw->startdate.tm_mday > 1);
181 static gint on_Previous_clicked(GtkWidget *button, GdkEventButton *event,
182 month_win *mw)
184 changeSelectedDate(mw, -1);
185 refresh_month_win(mw);
186 return TRUE;
189 static gint on_Next_clicked(GtkWidget *button, GdkEventButton *event,
190 month_win *mw)
192 changeSelectedDate(mw, +1);
193 refresh_month_win(mw);
194 return TRUE;
197 static void mw_summary_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
198 gint column, month_win *mw)
200 MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, row);
202 if (msginfo && msginfo->msgid) {
203 VCalEvent *event = vcal_manager_load_event(msginfo->msgid);
204 if (event) {
205 struct tm tm_start;
206 time_t t_start = icaltime_as_timet(icaltime_from_string(event->dtstart));
207 gboolean changed = FALSE;
209 #ifdef G_OS_WIN32
210 if (t_start < 0)
211 t_start = 1;
212 #endif
213 localtime_r(&t_start, &tm_start);
214 while (tm_start.tm_year < mw->startdate.tm_year) {
215 changeSelectedDate(mw, -1);
216 changed = TRUE;
218 while (tm_start.tm_year > mw->startdate.tm_year) {
219 changeSelectedDate(mw, +1);
220 changed = TRUE;
222 while (tm_start.tm_mon < mw->startdate.tm_mon) {
223 changeSelectedDate(mw, -1);
224 changed = TRUE;
226 while (tm_start.tm_mon > mw->startdate.tm_mon) {
227 changeSelectedDate(mw, +1);
228 changed = TRUE;
230 if (changed)
231 refresh_month_win(mw);
233 vcal_manager_free_event(event);
237 static void month_view_new_meeting_cb(month_win *mw, gpointer data_i, gpointer data_s)
239 int offset = GPOINTER_TO_INT(data_i);
240 struct tm tm_date = mw->startdate;
242 while (offset > tm_date.tm_mday) {
243 orage_move_day(&tm_date, 1);
245 while (offset < tm_date.tm_mday) {
246 orage_move_day(&tm_date, -1);
248 tm_date.tm_hour = 0;
249 vcal_meeting_create_with_start(NULL, &tm_date);
252 static void month_view_edit_meeting_cb(month_win *mw, gpointer data_i, gpointer data_s)
254 const gchar *uid = (gchar *)data_s;
255 vcal_view_select_event (uid, mw->item, TRUE,
256 G_CALLBACK(mw_summary_selected), mw);
259 static void month_view_cancel_meeting_cb(month_win *mw, gpointer data_i, gpointer data_s)
261 const gchar *uid = (gchar *)data_s;
262 vcalendar_cancel_meeting(mw->item, uid);
265 static void month_view_today_cb(month_win *mw, gpointer data_i, gpointer data_s)
267 time_t now = time(NULL);
268 struct tm tm_today;
269 localtime_r(&now, &tm_today);
271 while (tm_today.tm_mday != 1)
272 orage_move_day(&tm_today, -1);
274 mw->startdate = tm_today;
275 refresh_month_win(mw);
278 static void header_button_clicked_cb(GtkWidget *button
279 , GdkEventButton *event, gpointer *user_data)
281 month_win *mw = (month_win *)user_data;
282 int offset = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "day"));
284 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
285 month_view_new_meeting_cb(mw, GINT_TO_POINTER(offset), NULL);
287 if (event->button == 3) {
288 g_object_set_data(G_OBJECT(mw->Vbox), "menu_win",
289 mw);
290 g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_i",
291 GINT_TO_POINTER(offset));
292 g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_s",
293 NULL);
294 g_object_set_data(G_OBJECT(mw->Vbox), "new_meeting_cb",
295 month_view_new_meeting_cb);
296 g_object_set_data(G_OBJECT(mw->Vbox), "go_today_cb",
297 month_view_today_cb);
298 gtk_menu_popup_at_pointer(GTK_MENU(mw->view_menu), NULL);
302 static void on_button_press_event_cb(GtkWidget *widget
303 , GdkEventButton *event, gpointer *user_data)
305 month_win *mw = (month_win *)user_data;
306 gchar *uid = g_object_get_data(G_OBJECT(widget), "UID");
307 gpointer offset = g_object_get_data(G_OBJECT(widget), "offset");
309 if (event->button == 1) {
310 if (uid)
311 vcal_view_select_event (uid, mw->item, (event->type==GDK_2BUTTON_PRESS),
312 G_CALLBACK(mw_summary_selected), mw);
313 else if (event->type == GDK_2BUTTON_PRESS) {
314 month_view_new_meeting_cb(mw, GINT_TO_POINTER(offset), NULL);
317 if (event->button == 3) {
318 g_object_set_data(G_OBJECT(mw->Vbox), "menu_win",
319 mw);
320 g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_i",
321 offset);
322 g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_s",
323 uid);
324 g_object_set_data(G_OBJECT(mw->Vbox), "new_meeting_cb",
325 month_view_new_meeting_cb);
326 g_object_set_data(G_OBJECT(mw->Vbox), "edit_meeting_cb",
327 month_view_edit_meeting_cb);
328 g_object_set_data(G_OBJECT(mw->Vbox), "cancel_meeting_cb",
329 month_view_cancel_meeting_cb);
330 g_object_set_data(G_OBJECT(mw->Vbox), "go_today_cb",
331 month_view_today_cb);
332 gtk_menu_popup_at_pointer(GTK_MENU(mw->event_menu), NULL);
336 static void add_row(month_win *mw, VCalEvent *event, gint days)
338 gint row, start_row, end_row, first_row, last_row;
339 gint col, start_col, end_col, first_col, last_col;
340 gint width, start_width, end_width;
341 gchar *text, *tip, *start_date, *end_date;
342 GtkWidget *ev = NULL, *lab = NULL, *hb;
343 time_t t_start, t_end;
344 struct tm tm_first, tm_start, tm_end;
345 guint monthdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
346 int weekoffset = -1;
347 gboolean pack = TRUE, update_tip = FALSE;
348 time_t now = time(NULL);
349 struct tm tm_today;
350 gboolean start_prev_mon = FALSE;
352 localtime_r(&now, &tm_today);
354 tm_today.tm_year += 1900;
355 tm_today.tm_mon++;
357 /* First clarify timings */
358 t_start = icaltime_as_timet(icaltime_from_string(event->dtstart));
359 if (event->dtend && *event->dtend)
360 t_end = icaltime_as_timet(icaltime_from_string(event->dtend));
361 else
362 t_end = t_start;
364 #ifdef G_OS_WIN32
365 if (t_start < 0)
366 t_start = 1;
367 if (t_end < 0)
368 t_end = 1;
369 #endif
370 localtime_r(&t_start, &tm_start);
371 localtime_r(&t_end, &tm_end);
372 tm_first = mw->startdate;
374 tm_first.tm_year += 1900;
376 if (((tm_first.tm_year%4) == 0)
377 && (((tm_first.tm_year%100) != 0) || ((tm_first.tm_year%400) == 0)))
378 ++monthdays[1]; /* leap year, february has 29 days */
381 tm_first.tm_mon += 1;
382 tm_start.tm_year += 1900;
383 tm_start.tm_mon += 1;
384 tm_end.tm_year += 1900;
385 tm_end.tm_mon += 1;
387 start_col = orage_days_between(&tm_first, &tm_start)+1;
388 end_col = orage_days_between(&tm_first, &tm_end)+1;
390 if (start_col < 0)
391 start_prev_mon = TRUE;
393 if (end_col < 1) {
394 return;
396 if (start_col > 0 && start_col > monthdays[tm_first.tm_mon-1]) {
397 return;
399 else {
400 GDate *edate = g_date_new_dmy(tm_end.tm_mday, tm_end.tm_mon, tm_end.tm_year);
401 GDate *fdate = g_date_new_dmy(1, tm_first.tm_mon, tm_first.tm_year);
402 GDate *sdate;
403 if (start_col >= 1) {
404 sdate = g_date_new_dmy(tm_start.tm_mday, tm_start.tm_mon, tm_start.tm_year);
405 } else {
406 sdate = g_date_new_dmy(1, tm_first.tm_mon, tm_first.tm_year);
407 } /* endif */
409 col = start_col = (int)g_date_get_weekday(sdate);
410 end_col = (int)g_date_get_weekday(edate);
411 weekoffset = (int)g_date_get_monday_week_of_year(fdate);
412 row = start_row = (int)g_date_get_monday_week_of_year(sdate) - weekoffset;
413 end_row = (int)g_date_get_monday_week_of_year(edate) - weekoffset;
414 g_date_free(fdate);
415 g_date_free(sdate);
416 g_date_free(edate);
419 if (end_col > 7)
420 end_col = 7;
422 /* then add the appointment */
423 text = g_strdup(event->summary?event->summary : _("Unknown"));
425 if (mw->element[row][col] == NULL) {
426 hb = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
427 mw->element[row][col] = hb;
429 else {
430 GList *children = NULL;
431 hb = mw->element[row][col];
432 /* FIXME: set some real bar here to make it visible that we
433 * have more than 1 appointment here
435 children = gtk_container_get_children(GTK_CONTAINER(hb));
436 if (g_list_length(children) > 2) {
437 pack = FALSE;
438 update_tip = TRUE;
439 } else if (g_list_length(children) > 1) {
440 pack = FALSE;
441 update_tip = FALSE;
443 g_list_free(children);
445 if (pack || !update_tip) {
446 ev = gtk_event_box_new();
447 lab = gtk_label_new(text);
448 gtk_label_set_xalign(GTK_LABEL(lab), 0.0);
449 gtk_label_set_yalign(GTK_LABEL(lab), 0.0);
450 gtk_label_set_ellipsize(GTK_LABEL(lab), PANGO_ELLIPSIZE_END);
451 if ((row % 2) == 1)
452 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg1);
453 else
454 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg2);
455 } else if (!pack && update_tip) {
456 GList *children = gtk_container_get_children(GTK_CONTAINER(hb));
457 ev = GTK_WIDGET(g_list_last(children)->data);
458 g_list_free(children);
461 if (ev && tm_start.tm_mday == tm_today.tm_mday && tm_start.tm_mon == tm_today.tm_mon
462 && tm_start.tm_year == tm_today.tm_year)
463 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg_today);
465 if (orage_days_between(&tm_start, &tm_end) == 0)
466 tip = g_strdup_printf("%s\n%02d:%02d-%02d:%02d\n%s"
467 , text, tm_start.tm_hour, tm_start.tm_min
468 , tm_end.tm_hour, tm_end.tm_min, event->description);
469 else {
470 /* we took the date in unnormalized format, so we need to do that now */
471 start_date = g_strdup(orage_tm_date_to_i18_date(&tm_start));
472 end_date = g_strdup(orage_tm_date_to_i18_date(&tm_end));
473 tip = g_strdup_printf("%s\n%s %02d:%02d - %s %02d:%02d\n%s"
474 , text
475 , start_date, tm_start.tm_hour, tm_start.tm_min
476 , end_date, tm_end.tm_hour, tm_end.tm_min
477 , event->description);
478 g_free(start_date);
479 g_free(end_date);
481 if (pack) {
482 gtk_container_add(GTK_CONTAINER(ev), lab);
483 CLAWS_SET_TIP(ev, tip);
484 gtk_box_pack_start(GTK_BOX(hb), ev, TRUE, TRUE, 0);
485 } else if (!update_tip) {
486 gtk_label_set_label(GTK_LABEL(lab), "...");
487 gtk_container_add(GTK_CONTAINER(ev), lab);
488 CLAWS_SET_TIP(ev, tip);
489 gtk_box_pack_start(GTK_BOX(hb), ev, TRUE, TRUE, 0);
490 } else {
491 gchar *old = gtk_widget_get_tooltip_text(ev);
492 gchar *new = g_strdup_printf("%s\n\n%s", old?old:"", tip);
493 g_free(old);
495 CLAWS_SET_TIP(ev, new);
496 g_free(new);
498 g_object_set_data_full(G_OBJECT(ev), "UID", g_strdup(event->uid), g_free);
499 g_object_set_data(G_OBJECT(ev), "offset", GINT_TO_POINTER(tm_start.tm_mday));
500 g_signal_connect((gpointer)ev, "button-press-event"
501 , G_CALLBACK(on_button_press_event_cb), mw);
502 g_free(tip);
503 g_free(text);
505 /* and finally draw the line to show how long the appointment is,
506 * but only if it is Busy type event (=availability != 0)
507 * and it is not whole day event */
508 if (TRUE) {
509 width = mw->StartDate_button_req.width;
511 * same_date = !strncmp(start_ical_time, end_ical_time, 8);
512 * */
513 if (start_row < 0)
514 first_row = 0;
515 else
516 first_row = start_row;
517 if (end_row > 5)
518 last_row = 5;
519 else
520 last_row = end_row;
521 for (row = first_row; row <= last_row; row++) {
522 if (row == start_row)
523 first_col = start_col;
524 else
525 first_col = 0;
526 if (row == end_row)
527 last_col = end_col;
528 else
529 last_col = 7;
530 for (col = first_col; col <= last_col; col++) {
531 if (row == start_row && col == start_col && !start_prev_mon)
532 start_width = ((tm_start.tm_hour*60)+(tm_start.tm_min))*width/(24*60);
533 else
534 start_width = 0;
535 if (row == last_row && col == last_col)
536 end_width = ((tm_end.tm_hour*60)+(tm_end.tm_min))*width/(24*60);
537 else
538 end_width = width;
540 mw->line[row][col] = build_line(start_width, 0
541 , end_width-start_width, 2, mw->line[row][col]
542 , &mw->line_color);
548 static void app_rows(month_win *mw, FolderItem *item)
550 GSList *events = vcal_get_events_list(item);
551 GSList *cur = NULL;
552 int days = 7;
553 for (cur = events; cur ; cur = cur->next) {
554 VCalEvent *event = (VCalEvent *) (cur->data);
555 add_row(mw, event, days);
556 vcal_manager_free_event(event);
558 g_slist_free(events);
561 static void app_data(month_win *mw, FolderItem *item)
563 app_rows(mw, item);
567 static void fill_days(month_win *mw, gint days, FolderItem *item)
569 gint day, col, height, width;
570 GtkWidget *ev, *vb, *hb;
571 guint monthdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
572 struct tm t = mw->startdate;
573 int weekoffset = -1;
574 time_t now = time(NULL);
575 struct tm tm_today;
577 localtime_r(&now, &tm_today);
579 t.tm_year += 1900;
580 t.tm_mon++;
581 tm_today.tm_year += 1900;
582 tm_today.tm_mon++;
584 if (((t.tm_year%4) == 0)
585 && (((t.tm_year%100) != 0) || ((t.tm_year%400) == 0)))
586 ++monthdays[1]; /* leap year, february has 29 days */
588 height = mw->StartDate_button_req.height;
589 width = mw->StartDate_button_req.width;
591 /* first clear the structure */
592 for (col = 1; col < days+1; col++) {
593 mw->header[col] = NULL;
595 for (day = 0; day < MAX_DAYS; day++)
596 for (col = 0; col < 6; col++)
597 mw->line[col][day] = NULL;
598 for (day = 1; day <= monthdays[t.tm_mon-1]; day++) {
599 GDate *date = g_date_new_dmy(day, t.tm_mon, t.tm_year);
600 int dcol = (int)g_date_get_weekday(date);
601 int row = (int)g_date_get_monday_week_of_year(date);
602 if (weekoffset == -1) {
603 weekoffset = row;
604 row = 0;
605 } else {
606 row = row - weekoffset;
608 mw->element[row][dcol] = NULL;
609 mw->line[row][dcol] = build_line(0, 0, width, 3, NULL
610 , &mw->line_color);
611 g_date_free(date);
614 app_data(mw, item);
616 /* check rows */
617 for (day = 1; day <= monthdays[t.tm_mon-1]; day++) {
618 GDate *date = g_date_new_dmy(day, t.tm_mon, t.tm_year);
619 int col = (int)g_date_get_weekday(date);
620 int row = (int)g_date_get_monday_week_of_year(date);
621 gchar *label;
622 gchar *tmp;
623 GtkWidget *name;
625 if (weekoffset == -1) {
626 weekoffset = row;
627 row = 0;
628 } else {
629 row = row - weekoffset;
631 vb = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
632 gtk_widget_set_size_request(vb, width, height);
633 gtk_widget_set_hexpand(vb, TRUE);
634 gtk_widget_set_vexpand(vb, TRUE);
635 if (g_date_get_day(date) == 1)
636 label = g_strdup_printf("%d %s", g_date_get_day(date),
637 _(monthname[g_date_get_month(date)-1]));
638 else
639 label = g_strdup_printf("%d", g_date_get_day(date));
640 tmp = g_strdup_printf("%s %d %s %d",
641 _(dayname[col-1]),
642 g_date_get_day(date),
643 _(monthname[g_date_get_month(date)-1]),
644 g_date_get_year(date));
646 ev = gtk_event_box_new();
647 g_object_set_data(G_OBJECT(ev), "day", GINT_TO_POINTER((int)g_date_get_day(date)));
648 g_signal_connect((gpointer)ev, "button-press-event"
649 , G_CALLBACK(header_button_clicked_cb), mw);
650 name = gtk_label_new(label);
651 gtk_label_set_xalign(GTK_LABEL(name), 0.0);
652 gtk_label_set_yalign(GTK_LABEL(name), 0.0);
654 CLAWS_SET_TIP(ev, tmp);
655 gtk_container_add(GTK_CONTAINER(ev), name);
656 g_free(tmp);
657 g_free(label);
659 if ((row % 2) == 1)
660 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg1);
661 else
662 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg2);
663 if (col == 7)
664 gtk_widget_modify_fg(name, GTK_STATE_NORMAL, &mw->fg_sunday);
665 if (day == tm_today.tm_mday && t.tm_mon == tm_today.tm_mon && t.tm_year == tm_today.tm_year)
666 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg_today);
668 hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
669 gtk_box_pack_start(GTK_BOX(hb), ev, TRUE, TRUE, 1);
670 gtk_box_pack_start(GTK_BOX(vb), hb, TRUE, TRUE, 0);
671 if (mw->element[row][col]) {
672 gtk_box_pack_start(GTK_BOX(vb), mw->element[row][col], TRUE, TRUE, 0);
674 gtk_box_pack_start(GTK_BOX(vb), mw->line[row][col]
675 , FALSE, FALSE, 0);
677 gtk_grid_attach(GTK_GRID(mw->dtable), vb, col, row, 1, 1);
678 g_date_free(date);
682 static void build_month_view_header(month_win *mw, char *start_date)
684 GtkWidget *hbox, *label, *space_label;
686 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
688 label = gtk_label_new(_("Start"));
689 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
691 /* start date button */
692 mw->StartDate_button = gtk_button_new();
693 gtk_box_pack_start(GTK_BOX(hbox), mw->StartDate_button, FALSE, FALSE, 0);
695 space_label = gtk_label_new(" ");
696 gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
698 space_label = gtk_label_new(" ");
699 gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
701 label = gtk_label_new(_("Show"));
702 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
704 /* show days spin = how many days to show */
705 mw->day_spin = gtk_spin_button_new_with_range(1, MAX_DAYS, 1);
706 gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(mw->day_spin), TRUE);
707 gtk_widget_set_size_request(mw->day_spin, 40, -1);
708 gtk_box_pack_start(GTK_BOX(hbox), mw->day_spin, FALSE, FALSE, 0);
709 label = gtk_label_new(_("days"));
710 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
712 space_label = gtk_label_new(" ");
713 gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
715 /* sizes */
716 gtk_button_set_label(GTK_BUTTON(mw->StartDate_button)
717 , (const gchar *)start_date);
718 gtk_widget_get_preferred_size(mw->StartDate_button, NULL, &mw->StartDate_button_req);
719 mw->StartDate_button_req.width += mw->StartDate_button_req.width/10;
720 label = gtk_label_new("00");
721 gtk_widget_get_preferred_size(label, NULL, &mw->hour_req);
724 static void build_month_view_colours(month_win *mw)
726 GtkStyle *def_style, *cur_style;
727 GtkWidget *ctree = NULL;
728 def_style = gtk_widget_get_default_style();
730 if (mainwindow_get_mainwindow()) {
731 ctree = mainwindow_get_mainwindow()->summaryview->ctree;
733 if (ctree) {
734 cur_style = gtk_widget_get_style(ctree);
735 mw->bg1 = cur_style->bg[GTK_STATE_NORMAL];
736 mw->bg2 = cur_style->bg[GTK_STATE_NORMAL];
737 } else {
738 mw->bg1 = def_style->bg[GTK_STATE_NORMAL];
739 mw->bg2 = def_style->bg[GTK_STATE_NORMAL];
742 mw->bg1.red += (mw->bg1.red < 63000 ? 2000 : -2000);
743 mw->bg1.green += (mw->bg1.green < 63000 ? 2000 : -2000);
744 mw->bg1.blue += (mw->bg1.blue < 63000 ? 2000 : -2000);
746 mw->bg2.red += (mw->bg2.red > 1000 ? -1000 : 1000);
747 mw->bg2.green += (mw->bg2.green > 1000 ? -1000 : 1000);
748 mw->bg2.blue += (mw->bg2.blue > 1000 ? -1000 : 1000);
750 if (!gdk_color_parse("white", &mw->line_color)) {
751 g_warning("color parse failed: white");
752 mw->line_color.red = 239 * (65535/255);
753 mw->line_color.green = 235 * (65535/255);
754 mw->line_color.blue = 230 * (65535/255);
757 if (!gdk_color_parse("blue", &mw->fg_sunday)) {
758 g_warning("color parse failed: blue");
759 mw->fg_sunday.red = 10 * (65535/255);
760 mw->fg_sunday.green = 10 * (65535/255);
761 mw->fg_sunday.blue = 255 * (65535/255);
764 if (!gdk_color_parse("gold", &mw->bg_today)) {
765 g_warning("color parse failed: gold");
766 mw->bg_today.red = 255 * (65535/255);
767 mw->bg_today.green = 215 * (65535/255);
768 mw->bg_today.blue = 115 * (65535/255);
771 if (ctree) {
772 cur_style = gtk_widget_get_style(ctree);
773 mw->fg_sunday.red = (mw->fg_sunday.red + cur_style->fg[GTK_STATE_SELECTED].red)/2;
774 mw->fg_sunday.green = (mw->fg_sunday.green + cur_style->fg[GTK_STATE_SELECTED].red)/2;
775 mw->fg_sunday.blue = (3*mw->fg_sunday.blue + cur_style->fg[GTK_STATE_SELECTED].red)/4;
776 mw->bg_today.red = (3*mw->bg_today.red + cur_style->bg[GTK_STATE_NORMAL].red)/4;
777 mw->bg_today.green = (3*mw->bg_today.green + cur_style->bg[GTK_STATE_NORMAL].red)/4;
778 mw->bg_today.blue = (3*mw->bg_today.blue + cur_style->bg[GTK_STATE_NORMAL].red)/4;
782 static void fill_hour(month_win *mw, gint col, gint row, char *text)
784 GtkWidget *name, *ev;
786 ev = gtk_event_box_new();
787 name = gtk_label_new(text);
788 gtk_label_set_xalign(GTK_LABEL(name), 0.0);
789 CLAWS_SET_TIP(ev, _("Week number"));
790 gtk_container_add(GTK_CONTAINER(ev), name);
791 gtk_widget_set_size_request(ev, mw->hour_req.width
792 , mw->StartDate_button_req.height);
793 if (text)
794 gtk_grid_attach(GTK_GRID(mw->dtable), ev, col, row, 1, 1);
795 else /* special, needed for header table full day events */
796 gtk_grid_attach(GTK_GRID(mw->dtable_h), ev, col, row, 1, 1);
799 static void build_month_view_table(month_win *mw)
801 gint days; /* number of days to show */
802 gint i = 0;
803 GtkWidget *button;
804 struct tm tm_date, tm_today;
805 guint monthdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
806 GtkWidget *vp;
807 time_t t = time(NULL);
808 GtkWidget *arrow;
809 int avail_w = 0, avail_d = 7;
810 int avail_h = 0;
811 int weekoffset = -1;
812 GDate *date;
813 int first_week = 0;
815 if (mainwindow_get_mainwindow()) {
816 GtkAllocation allocation;
817 SummaryView *summaryview = mainwindow_get_mainwindow()->summaryview;
818 GTK_EVENTS_FLUSH();
819 gtk_widget_get_allocation(summaryview->mainwidget_book,
820 &allocation);
822 avail_w = allocation.width - 25 - 2*(mw->hour_req.width);
823 avail_h = allocation.height - 20;
824 if (avail_h < 250)
825 avail_h = 250;
826 /* avail_d = avail_w / mw->StartDate_button_req.width; */
829 gtk_widget_set_size_request(mw->StartDate_button, avail_w / avail_d,
830 (avail_h)/6);
831 gtk_widget_get_preferred_size(mw->StartDate_button, &mw->StartDate_button_req,
832 NULL);
834 /* initial values */
835 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mw->day_spin), avail_d);
837 #ifdef G_OS_WIN32
838 if (t < 0)
839 t = 1;
840 #endif
841 localtime_r(&t, &tm_today);
842 days = 7;
843 /****** header of day table = days columns ******/
844 mw->scroll_win_h = gtk_scrolled_window_new(NULL, NULL);
845 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(mw->scroll_win_h)
846 , GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
847 gtk_box_pack_start(GTK_BOX(mw->Vbox), mw->scroll_win_h
848 , TRUE, TRUE, 0);
849 mw->month_view_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
850 gtk_container_add(GTK_CONTAINER(mw->scroll_win_h), mw->month_view_vbox);
851 /* row 1= day header buttons
852 * row 2= full day events after the buttons */
853 mw->dtable_h = gtk_grid_new();
854 gtk_box_pack_start(GTK_BOX(mw->month_view_vbox), mw->dtable_h
855 , FALSE, FALSE, 0);
857 tm_date = mw->startdate;
859 if (((tm_date.tm_year%4) == 0) && (((tm_date.tm_year%100) != 0)
860 || ((tm_date.tm_year%400) == 0)))
861 ++monthdays[1];
864 i=0;
865 mw->Previous_toolbutton = gtk_event_box_new();
866 gtk_event_box_set_visible_window(GTK_EVENT_BOX(mw->Previous_toolbutton), FALSE);
867 gtk_container_set_border_width(GTK_CONTAINER(mw->Previous_toolbutton), 0);
868 arrow = gtk_image_new_from_icon_name("pan-start-symbolic", GTK_ICON_SIZE_MENU);
869 gtk_container_add(GTK_CONTAINER(mw->Previous_toolbutton), arrow);
870 gtk_grid_attach(GTK_GRID(mw->dtable_h), mw->Previous_toolbutton, i, 0, 1, 1);
871 gtk_widget_show_all(mw->Previous_toolbutton);
872 g_signal_connect((gpointer)mw->Previous_toolbutton, "button_release_event"
873 , G_CALLBACK(on_Previous_clicked), mw);
874 CLAWS_SET_TIP(mw->Previous_toolbutton, _("Previous month"));
875 for (i = 1; i < days+1; i++) {
876 button = gtk_label_new(_(dayname[i-1]));
878 gtk_widget_set_size_request(button, mw->StartDate_button_req.width, -1);
879 g_object_set_data(G_OBJECT(button), "offset", GINT_TO_POINTER(i-1));
880 gtk_widget_set_hexpand(button, TRUE);
881 gtk_grid_attach(GTK_GRID(mw->dtable_h), button, i, 0, 1, 1);
884 mw->Next_toolbutton = gtk_event_box_new();
885 gtk_event_box_set_visible_window(GTK_EVENT_BOX(mw->Next_toolbutton), FALSE);
886 gtk_container_set_border_width(GTK_CONTAINER(mw->Next_toolbutton), 0);
887 arrow = gtk_image_new_from_icon_name("pan-end-symbolic", GTK_ICON_SIZE_MENU);
888 gtk_container_add(GTK_CONTAINER(mw->Next_toolbutton), arrow);
889 gtk_grid_attach(GTK_GRID(mw->dtable_h), mw->Next_toolbutton, i, 0, 1, 1);
890 gtk_widget_show_all(mw->Next_toolbutton);
891 g_signal_connect((gpointer)mw->Next_toolbutton, "button_release_event"
892 , G_CALLBACK(on_Next_clicked), mw);
893 CLAWS_SET_TIP(mw->Next_toolbutton, _("Next month"));
895 /****** body of day table ******/
896 mw->scroll_win = gtk_scrolled_window_new(NULL, NULL);
897 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(mw->scroll_win)
898 , GTK_SHADOW_NONE);
899 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(mw->scroll_win)
900 , GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
901 gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(mw->scroll_win)
902 , GTK_CORNER_TOP_LEFT);
903 gtk_box_pack_start(GTK_BOX(mw->month_view_vbox), mw->scroll_win
904 , TRUE, TRUE, 0);
905 vp = gtk_viewport_new(NULL, NULL);
906 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_IN);
907 gtk_container_add(GTK_CONTAINER(mw->scroll_win), vp);
908 mw->dtable = gtk_grid_new();
909 gtk_container_add(GTK_CONTAINER(vp), mw->dtable);
911 gtk_widget_show_all(mw->dtable_h);
913 date = g_date_new_dmy(1, 1, tm_date.tm_year+1900);
914 first_week = g_date_get_monday_week_of_year(date);
916 if (first_week == 0)
917 first_week = 1;
918 else
919 first_week = 0;
920 g_date_free(date);
922 /* hours column = hour rows */
923 for (i = 0; i <= 6; i++) {
924 int day;
925 for (day = 1; day <= monthdays[tm_date.tm_mon]; day++) {
926 date = g_date_new_dmy(day, tm_date.tm_mon+1, tm_date.tm_year+1900);
927 int row = (int)g_date_get_monday_week_of_year(date);
928 if (weekoffset == -1) {
929 weekoffset = row;
931 if (row - weekoffset == i) {
932 gchar *wn = g_strdup_printf("%d", row+first_week > 53?1:row+first_week);
933 fill_hour(mw, 0, i, wn);
934 fill_hour(mw, days+1, i, "");
935 g_free(wn);
936 g_date_free(date);
937 break;
939 g_date_free(date);
942 fill_days(mw, days, mw->item);
945 void refresh_month_win(month_win *mw)
947 gtk_widget_destroy(mw->scroll_win_h);
948 build_month_view_table(mw);
949 gtk_widget_show_all(mw->scroll_win_h);
952 month_win *create_month_win(FolderItem *item, struct tm tmdate)
954 month_win *mw;
955 char *start_date = get_locale_date(&tmdate);
957 /* initialisation + main window + base vbox */
958 mw = g_new0(month_win, 1);
959 mw->scroll_pos = -1; /* not set */
961 mw->accel_group = gtk_accel_group_new();
963 while (tmdate.tm_mday != 1)
964 orage_move_day(&tmdate, -1);
966 mw->startdate = tmdate;
968 mw->Vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
969 gtk_widget_set_name(GTK_WIDGET(mw->Vbox), "vcal_month_win");
971 mw->item = item;
972 build_month_view_colours(mw);
973 build_month_view_header(mw, start_date);
974 build_month_view_table(mw);
975 gtk_widget_show_all(mw->Vbox);
976 mw->selsig = vcal_view_set_calendar_page(mw->Vbox,
977 G_CALLBACK(mw_summary_selected), mw);
979 vcal_view_create_popup_menus(mw->Vbox, &mw->view_menu,
980 &mw->event_menu, &mw->event_group,
981 &mw->ui_manager);
982 return(mw);