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 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.
24 #include "claws-features.h"
29 #include <glib/gi18n.h>
39 #include <glib/gprintf.h>
40 #include <gdk/gdkkeysyms.h>
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"
55 GtkAccelGroup *accel_group;
62 GtkWidget *File_menu_new;
63 GtkWidget *File_menu_close;
65 GtkWidget *View_menu_refresh;
67 GtkWidget *Go_menu_today;
68 GtkWidget *Go_menu_prev;
69 GtkWidget *Go_menu_next;
72 GtkWidget *Create_toolbutton;
73 GtkWidget *Previous_toolbutton;
74 GtkWidget *Today_toolbutton;
75 GtkWidget *Next_toolbutton;
76 GtkWidget *Refresh_toolbutton;
77 GtkWidget *Close_toolbutton;
79 GtkWidget *StartDate_button;
80 GtkRequisition StartDate_button_req;
83 GtkWidget *month_view_vbox;
84 GtkWidget *scroll_win_h;
85 GtkWidget *dtable_h; /* header of day table */
86 GtkWidget *scroll_win;
87 GtkWidget *dtable; /* day table */
88 GtkRequisition hour_req;
90 GtkWidget *header[MAX_DAYS];
91 GtkWidget *element[6][MAX_DAYS];
92 GtkWidget *line[6][MAX_DAYS];
95 gdouble scroll_pos; /* remember the scroll position */
97 GdkColor bg1, bg2, line_color, bg_today, fg_sunday;
98 GList *apptw_list; /* keep track of appointments being updated */
102 GtkWidget *view_menu;
103 GtkWidget *event_menu;
104 GtkActionGroup *event_group;
105 GtkUIManager *ui_manager;
108 gchar *dayname[7] = {
117 gchar *monthname[12] = {
132 static gchar *get_locale_date(struct tm *tmdate)
134 gchar *d = g_malloc(100);
135 strftime(d, 99, "%x", tmdate);
139 void mw_close_window(month_win *mw)
141 vcal_view_set_summary_page(mw->Vbox, mw->selsig);
148 static char *orage_tm_date_to_i18_date(struct tm *tm_date)
150 static char i18_date[32];
152 t.tm_mday = tm_date->tm_mday;
153 t.tm_mon = tm_date->tm_mon - 1;
154 t.tm_year = tm_date->tm_year - 1900;
160 if (strftime(i18_date, 32, "%x", &t) == 0)
161 g_error("Orage: orage_tm_date_to_i18_date too long string in strftime");
165 static void changeSelectedDate(month_win *mw, gint month)
167 gint curmon = mw->startdate.tm_mon;
169 do { /* go to first of next month */
170 orage_move_day(&(mw->startdate), 1);
171 } while (curmon == mw->startdate.tm_mon);
173 do { /* go to last day of last month */
174 orage_move_day(&(mw->startdate), -1);
175 } while (curmon == mw->startdate.tm_mon);
176 do { /* go to first of last month */
177 orage_move_day(&(mw->startdate), -1);
178 } while (mw->startdate.tm_mday > 1);
182 static gint on_Previous_clicked(GtkWidget *button, GdkEventButton *event,
185 changeSelectedDate(mw, -1);
186 refresh_month_win(mw);
190 static gint on_Next_clicked(GtkWidget *button, GdkEventButton *event,
193 changeSelectedDate(mw, +1);
194 refresh_month_win(mw);
198 static void mw_summary_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
199 gint column, month_win *mw)
201 MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, row);
203 if (msginfo && msginfo->msgid) {
204 VCalEvent *event = vcal_manager_load_event(msginfo->msgid);
207 time_t t_start = icaltime_as_timet(icaltime_from_string(event->dtstart));
208 gboolean changed = FALSE;
214 localtime_r(&t_start, &tm_start);
215 while (tm_start.tm_year < mw->startdate.tm_year) {
216 changeSelectedDate(mw, -1);
219 while (tm_start.tm_year > mw->startdate.tm_year) {
220 changeSelectedDate(mw, +1);
223 while (tm_start.tm_mon < mw->startdate.tm_mon) {
224 changeSelectedDate(mw, -1);
227 while (tm_start.tm_mon > mw->startdate.tm_mon) {
228 changeSelectedDate(mw, +1);
232 refresh_month_win(mw);
234 vcal_manager_free_event(event);
238 static void month_view_new_meeting_cb(month_win *mw, gpointer data_i, gpointer data_s)
240 int offset = GPOINTER_TO_INT(data_i);
241 struct tm tm_date = mw->startdate;
243 while (offset > tm_date.tm_mday) {
244 orage_move_day(&tm_date, 1);
246 while (offset < tm_date.tm_mday) {
247 orage_move_day(&tm_date, -1);
250 vcal_meeting_create_with_start(NULL, &tm_date);
253 static void month_view_edit_meeting_cb(month_win *mw, gpointer data_i, gpointer data_s)
255 const gchar *uid = (gchar *)data_s;
256 vcal_view_select_event (uid, mw->item, TRUE,
257 G_CALLBACK(mw_summary_selected), mw);
260 static void month_view_cancel_meeting_cb(month_win *mw, gpointer data_i, gpointer data_s)
262 const gchar *uid = (gchar *)data_s;
263 vcalendar_cancel_meeting(mw->item, uid);
266 static void month_view_today_cb(month_win *mw, gpointer data_i, gpointer data_s)
268 time_t now = time(NULL);
270 localtime_r(&now, &tm_today);
272 while (tm_today.tm_mday != 1)
273 orage_move_day(&tm_today, -1);
275 mw->startdate = tm_today;
276 refresh_month_win(mw);
279 static void header_button_clicked_cb(GtkWidget *button
280 , GdkEventButton *event, gpointer *user_data)
282 month_win *mw = (month_win *)user_data;
283 int offset = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "day"));
285 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
286 month_view_new_meeting_cb(mw, GINT_TO_POINTER(offset), NULL);
288 if (event->button == 3) {
289 g_object_set_data(G_OBJECT(mw->Vbox), "menu_win",
291 g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_i",
292 GINT_TO_POINTER(offset));
293 g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_s",
295 g_object_set_data(G_OBJECT(mw->Vbox), "new_meeting_cb",
296 month_view_new_meeting_cb);
297 g_object_set_data(G_OBJECT(mw->Vbox), "go_today_cb",
298 month_view_today_cb);
299 gtk_menu_popup(GTK_MENU(mw->view_menu),
300 NULL, NULL, NULL, NULL,
301 event->button, event->time);
305 static void on_button_press_event_cb(GtkWidget *widget
306 , GdkEventButton *event, gpointer *user_data)
308 month_win *mw = (month_win *)user_data;
309 gchar *uid = g_object_get_data(G_OBJECT(widget), "UID");;
310 gpointer offset = g_object_get_data(G_OBJECT(widget), "offset");
312 if (event->button == 1) {
314 vcal_view_select_event (uid, mw->item, (event->type==GDK_2BUTTON_PRESS),
315 G_CALLBACK(mw_summary_selected), mw);
316 else if (event->type == GDK_2BUTTON_PRESS) {
317 month_view_new_meeting_cb(mw, GINT_TO_POINTER(offset), NULL);
320 if (event->button == 3) {
321 g_object_set_data(G_OBJECT(mw->Vbox), "menu_win",
323 g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_i",
325 g_object_set_data(G_OBJECT(mw->Vbox), "menu_data_s",
327 g_object_set_data(G_OBJECT(mw->Vbox), "new_meeting_cb",
328 month_view_new_meeting_cb);
329 g_object_set_data(G_OBJECT(mw->Vbox), "edit_meeting_cb",
330 month_view_edit_meeting_cb);
331 g_object_set_data(G_OBJECT(mw->Vbox), "cancel_meeting_cb",
332 month_view_cancel_meeting_cb);
333 g_object_set_data(G_OBJECT(mw->Vbox), "go_today_cb",
334 month_view_today_cb);
335 gtk_menu_popup(GTK_MENU(mw->event_menu),
336 NULL, NULL, NULL, NULL,
337 event->button, event->time);
341 static void add_row(month_win *mw, VCalEvent *event, gint days)
343 gint row, start_row, end_row, first_row, last_row;
344 gint col, start_col, end_col, first_col, last_col;
345 gint width, start_width, end_width;
346 gchar *text, *tip, *start_date, *end_date;
347 GtkWidget *ev = NULL, *lab = NULL, *hb;
348 time_t t_start, t_end;
349 struct tm tm_first, tm_start, tm_end;
350 guint monthdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
352 gboolean pack = TRUE, update_tip = FALSE;
353 time_t now = time(NULL);
355 gboolean start_prev_mon = FALSE;
357 localtime_r(&now, &tm_today);
359 tm_today.tm_year += 1900;
362 /* First clarify timings */
363 t_start = icaltime_as_timet(icaltime_from_string(event->dtstart));
364 if (event->dtend && *event->dtend)
365 t_end = icaltime_as_timet(icaltime_from_string(event->dtend));
375 localtime_r(&t_start, &tm_start);
376 localtime_r(&t_end, &tm_end);
377 tm_first = mw->startdate;
379 tm_first.tm_year += 1900;
381 if (((tm_first.tm_year%4) == 0)
382 && (((tm_first.tm_year%100) != 0) || ((tm_first.tm_year%400) == 0)))
383 ++monthdays[1]; /* leap year, february has 29 days */
386 tm_first.tm_mon += 1;
387 tm_start.tm_year += 1900;
388 tm_start.tm_mon += 1;
389 tm_end.tm_year += 1900;
392 start_col = orage_days_between(&tm_first, &tm_start)+1;
393 end_col = orage_days_between(&tm_first, &tm_end)+1;
396 start_prev_mon = TRUE;
401 if (start_col > 0 && start_col > monthdays[tm_first.tm_mon-1]) {
405 GDate *edate = g_date_new_dmy(tm_end.tm_mday, tm_end.tm_mon, tm_end.tm_year);
406 GDate *fdate = g_date_new_dmy(1, tm_first.tm_mon, tm_first.tm_year);
408 if (start_col >= 1) {
409 sdate = g_date_new_dmy(tm_start.tm_mday, tm_start.tm_mon, tm_start.tm_year);
411 sdate = g_date_new_dmy(1, tm_first.tm_mon, tm_first.tm_year);
414 col = start_col = (int)g_date_get_weekday(sdate);
415 end_col = (int)g_date_get_weekday(edate);
416 weekoffset = (int)g_date_get_monday_week_of_year(fdate);
417 row = start_row = (int)g_date_get_monday_week_of_year(sdate) - weekoffset;
418 end_row = (int)g_date_get_monday_week_of_year(edate) - weekoffset;
427 /* then add the appointment */
428 text = g_strdup(event->summary?event->summary : _("Unknown"));
430 if (mw->element[row][col] == NULL) {
431 hb = gtk_vbox_new(TRUE, 1);
432 mw->element[row][col] = hb;
435 GList *children = NULL;
436 hb = mw->element[row][col];
437 /* FIXME: set some real bar here to make it visible that we
438 * have more than 1 appointment here
440 children = gtk_container_get_children(GTK_CONTAINER(hb));
441 if (g_list_length(children) > 2) {
444 } else if (g_list_length(children) > 1) {
448 g_list_free(children);
450 if (pack || !update_tip) {
451 ev = gtk_event_box_new();
452 lab = gtk_label_new(text);
453 gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
454 gtk_label_set_ellipsize(GTK_LABEL(lab), PANGO_ELLIPSIZE_END);
456 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg1);
458 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg2);
459 } else if (!pack && update_tip) {
460 GList *children = gtk_container_get_children(GTK_CONTAINER(hb));
461 ev = GTK_WIDGET(g_list_last(children)->data);
462 g_list_free(children);
465 if (ev && tm_start.tm_mday == tm_today.tm_mday && tm_start.tm_mon == tm_today.tm_mon
466 && tm_start.tm_year == tm_today.tm_year)
467 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg_today);
469 if (orage_days_between(&tm_start, &tm_end) == 0)
470 tip = g_strdup_printf("%s\n%02d:%02d-%02d:%02d\n%s"
471 , text, tm_start.tm_hour, tm_start.tm_min
472 , tm_end.tm_hour, tm_end.tm_min, event->description);
474 /* we took the date in unnormalized format, so we need to do that now */
475 start_date = g_strdup(orage_tm_date_to_i18_date(&tm_start));
476 end_date = g_strdup(orage_tm_date_to_i18_date(&tm_end));
477 tip = g_strdup_printf("%s\n%s %02d:%02d - %s %02d:%02d\n%s"
479 , start_date, tm_start.tm_hour, tm_start.tm_min
480 , end_date, tm_end.tm_hour, tm_end.tm_min
481 , event->description);
486 gtk_container_add(GTK_CONTAINER(ev), lab);
487 CLAWS_SET_TIP(ev, tip);
488 gtk_box_pack_start(GTK_BOX(hb), ev, TRUE, TRUE, 0);
489 } else if (!update_tip) {
490 gtk_label_set_label(GTK_LABEL(lab), "...");
491 gtk_container_add(GTK_CONTAINER(ev), lab);
492 CLAWS_SET_TIP(ev, tip);
493 gtk_box_pack_start(GTK_BOX(hb), ev, TRUE, TRUE, 0);
495 gchar *old = gtk_widget_get_tooltip_text(ev);
496 gchar *new = g_strdup_printf("%s\n\n%s", old?old:"", tip);
499 CLAWS_SET_TIP(ev, new);
502 g_object_set_data_full(G_OBJECT(ev), "UID", g_strdup(event->uid), g_free);
503 g_object_set_data(G_OBJECT(ev), "offset", GINT_TO_POINTER(tm_start.tm_mday));
504 g_signal_connect((gpointer)ev, "button-press-event"
505 , G_CALLBACK(on_button_press_event_cb), mw);
509 /* and finally draw the line to show how long the appointment is,
510 * but only if it is Busy type event (=availability != 0)
511 * and it is not whole day event */
513 width = mw->StartDate_button_req.width;
515 * same_date = !strncmp(start_ical_time, end_ical_time, 8);
520 first_row = start_row;
525 for (row = first_row; row <= last_row; row++) {
526 if (row == start_row)
527 first_col = start_col;
534 for (col = first_col; col <= last_col; col++) {
535 if (row == start_row && col == start_col && !start_prev_mon)
536 start_width = ((tm_start.tm_hour*60)+(tm_start.tm_min))*width/(24*60);
539 if (row == last_row && col == last_col)
540 end_width = ((tm_end.tm_hour*60)+(tm_end.tm_min))*width/(24*60);
544 mw->line[row][col] = build_line(start_width, 0
545 , end_width-start_width, 2, mw->line[row][col]
552 static void app_rows(month_win *mw, FolderItem *item)
554 GSList *events = vcal_get_events_list(item);
557 for (cur = events; cur ; cur = cur->next) {
558 VCalEvent *event = (VCalEvent *) (cur->data);
559 add_row(mw, event, days);
560 vcal_manager_free_event(event);
562 g_slist_free(events);
565 static void app_data(month_win *mw, FolderItem *item)
571 static void fill_days(month_win *mw, gint days, FolderItem *item)
573 gint day, col, height, width;
574 GtkWidget *ev, *vb, *hb;
575 guint monthdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
576 struct tm t = mw->startdate;
578 time_t now = time(NULL);
581 localtime_r(&now, &tm_today);
585 tm_today.tm_year += 1900;
588 if (((t.tm_year%4) == 0)
589 && (((t.tm_year%100) != 0) || ((t.tm_year%400) == 0)))
590 ++monthdays[1]; /* leap year, february has 29 days */
592 height = mw->StartDate_button_req.height;
593 width = mw->StartDate_button_req.width;
595 /* first clear the structure */
596 for (col = 1; col < days+1; col++) {
597 mw->header[col] = NULL;
599 for (day = 0; day < MAX_DAYS; day++)
600 for (col = 0; col < 6; col++)
601 mw->line[col][day] = NULL;
602 for (day = 1; day <= monthdays[t.tm_mon-1]; day++) {
603 GDate *date = g_date_new_dmy(day, t.tm_mon, t.tm_year);
604 int dcol = (int)g_date_get_weekday(date);
605 int row = (int)g_date_get_monday_week_of_year(date);
606 if (weekoffset == -1) {
610 row = row - weekoffset;
612 mw->element[row][dcol] = NULL;
613 mw->line[row][dcol] = build_line(0, 0, width, 3, NULL
621 for (day = 1; day <= monthdays[t.tm_mon-1]; day++) {
622 GDate *date = g_date_new_dmy(day, t.tm_mon, t.tm_year);
623 int col = (int)g_date_get_weekday(date);
624 int row = (int)g_date_get_monday_week_of_year(date);
629 if (weekoffset == -1) {
633 row = row - weekoffset;
635 vb = gtk_vbox_new(FALSE, 0);
636 gtk_widget_set_size_request(vb, width, height);
637 if (g_date_get_day(date) == 1)
638 label = g_strdup_printf("%d %s", g_date_get_day(date),
639 _(monthname[g_date_get_month(date)-1]));
641 label = g_strdup_printf("%d", g_date_get_day(date));
642 tmp = g_strdup_printf("%s %d %s %d",
644 g_date_get_day(date),
645 _(monthname[g_date_get_month(date)-1]),
646 g_date_get_year(date));
648 ev = gtk_event_box_new();
649 g_object_set_data(G_OBJECT(ev), "day", GINT_TO_POINTER((int)g_date_get_day(date)));
650 g_signal_connect((gpointer)ev, "button-press-event"
651 , G_CALLBACK(header_button_clicked_cb), mw);
652 name = gtk_label_new(label);
653 gtk_misc_set_alignment(GTK_MISC(name), 0.0, 0.0);
655 CLAWS_SET_TIP(ev, tmp);
656 gtk_container_add(GTK_CONTAINER(ev), name);
661 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg1);
663 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg2);
665 gtk_widget_modify_fg(name, GTK_STATE_NORMAL, &mw->fg_sunday);
666 if (day == tm_today.tm_mday && t.tm_mon == tm_today.tm_mon && t.tm_year == tm_today.tm_year)
667 gtk_widget_modify_bg(ev, GTK_STATE_NORMAL, &mw->bg_today);
669 hb = gtk_hbox_new(FALSE, 0);
670 gtk_box_pack_start(GTK_BOX(hb), ev, TRUE, TRUE, 1);
671 gtk_box_pack_start(GTK_BOX(vb), hb, TRUE, TRUE, 0);
672 if (mw->element[row][col]) {
673 gtk_box_pack_start(GTK_BOX(vb), mw->element[row][col], TRUE, TRUE, 0);
675 gtk_box_pack_start(GTK_BOX(vb), mw->line[row][col]
678 gtk_table_attach(GTK_TABLE(mw->dtable), vb, col, col+1, row, row+1
679 , (GTK_FILL), (0), 0, 0);
684 static void build_month_view_header(month_win *mw, char *start_date)
686 GtkWidget *hbox, *label, *space_label;
688 hbox = gtk_hbox_new(FALSE, 0);
690 label = gtk_label_new(_("Start"));
691 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
693 /* start date button */
694 mw->StartDate_button = gtk_button_new();
695 gtk_box_pack_start(GTK_BOX(hbox), mw->StartDate_button, FALSE, FALSE, 0);
697 space_label = gtk_label_new(" ");
698 gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
700 space_label = gtk_label_new(" ");
701 gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
703 label = gtk_label_new(_("Show"));
704 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
706 /* show days spin = how many days to show */
707 mw->day_spin = gtk_spin_button_new_with_range(1, MAX_DAYS, 1);
708 gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(mw->day_spin), TRUE);
709 gtk_widget_set_size_request(mw->day_spin, 40, -1);
710 gtk_box_pack_start(GTK_BOX(hbox), mw->day_spin, FALSE, FALSE, 0);
711 label = gtk_label_new(_("days"));
712 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
714 space_label = gtk_label_new(" ");
715 gtk_box_pack_start(GTK_BOX(hbox), space_label, FALSE, FALSE, 0);
718 gtk_button_set_label(GTK_BUTTON(mw->StartDate_button)
719 , (const gchar *)start_date);
720 gtk_widget_size_request(mw->StartDate_button, &mw->StartDate_button_req);
721 mw->StartDate_button_req.width += mw->StartDate_button_req.width/10;
722 label = gtk_label_new("00");
723 gtk_widget_size_request(label, &mw->hour_req);
726 static void build_month_view_colours(month_win *mw)
728 GtkStyle *def_style, *cur_style;
729 GdkColormap *pic1_cmap;
730 GtkWidget *ctree = NULL;
731 def_style = gtk_widget_get_default_style();
732 pic1_cmap = gdk_colormap_get_system();
734 if (mainwindow_get_mainwindow()) {
735 ctree = mainwindow_get_mainwindow()->summaryview->ctree;
738 cur_style = gtk_widget_get_style(ctree);
739 mw->bg1 = cur_style->bg[GTK_STATE_NORMAL];
740 mw->bg2 = cur_style->bg[GTK_STATE_NORMAL];
742 mw->bg1 = def_style->bg[GTK_STATE_NORMAL];
743 mw->bg2 = def_style->bg[GTK_STATE_NORMAL];
746 mw->bg1.red += (mw->bg1.red < 63000 ? 2000 : -2000);
747 mw->bg1.green += (mw->bg1.green < 63000 ? 2000 : -2000);
748 mw->bg1.blue += (mw->bg1.blue < 63000 ? 2000 : -2000);
749 gdk_colormap_alloc_color(pic1_cmap, &mw->bg1, FALSE, TRUE);
751 mw->bg2.red += (mw->bg2.red > 1000 ? -1000 : 1000);
752 mw->bg2.green += (mw->bg2.green > 1000 ? -1000 : 1000);
753 mw->bg2.blue += (mw->bg2.blue > 1000 ? -1000 : 1000);
754 gdk_colormap_alloc_color(pic1_cmap, &mw->bg2, FALSE, TRUE);
756 if (!gdk_color_parse("white", &mw->line_color)) {
757 g_warning("color parse failed: white");
758 mw->line_color.red = 239 * (65535/255);
759 mw->line_color.green = 235 * (65535/255);
760 mw->line_color.blue = 230 * (65535/255);
763 if (!gdk_color_parse("blue", &mw->fg_sunday)) {
764 g_warning("color parse failed: blue");
765 mw->fg_sunday.red = 10 * (65535/255);
766 mw->fg_sunday.green = 10 * (65535/255);
767 mw->fg_sunday.blue = 255 * (65535/255);
770 if (!gdk_color_parse("gold", &mw->bg_today)) {
771 g_warning("color parse failed: gold");
772 mw->bg_today.red = 255 * (65535/255);
773 mw->bg_today.green = 215 * (65535/255);
774 mw->bg_today.blue = 115 * (65535/255);
778 cur_style = gtk_widget_get_style(ctree);
779 mw->fg_sunday.red = (mw->fg_sunday.red + cur_style->fg[GTK_STATE_SELECTED].red)/2;
780 mw->fg_sunday.green = (mw->fg_sunday.green + cur_style->fg[GTK_STATE_SELECTED].red)/2;
781 mw->fg_sunday.blue = (3*mw->fg_sunday.blue + cur_style->fg[GTK_STATE_SELECTED].red)/4;
782 mw->bg_today.red = (3*mw->bg_today.red + cur_style->bg[GTK_STATE_NORMAL].red)/4;
783 mw->bg_today.green = (3*mw->bg_today.green + cur_style->bg[GTK_STATE_NORMAL].red)/4;
784 mw->bg_today.blue = (3*mw->bg_today.blue + cur_style->bg[GTK_STATE_NORMAL].red)/4;
786 gdk_colormap_alloc_color(pic1_cmap, &mw->line_color, FALSE, TRUE);
787 gdk_colormap_alloc_color(pic1_cmap, &mw->fg_sunday, FALSE, TRUE);
788 gdk_colormap_alloc_color(pic1_cmap, &mw->bg_today, FALSE, TRUE);
791 static void fill_hour(month_win *mw, gint col, gint row, char *text)
793 GtkWidget *name, *ev;
795 ev = gtk_event_box_new();
796 name = gtk_label_new(text);
797 gtk_misc_set_alignment(GTK_MISC(name), 0, 0.5);
798 CLAWS_SET_TIP(ev, _("Week number"));
799 gtk_container_add(GTK_CONTAINER(ev), name);
800 gtk_widget_set_size_request(ev, mw->hour_req.width
801 , mw->StartDate_button_req.height);
803 gtk_table_attach(GTK_TABLE(mw->dtable), ev, col, col+1, row, row+1
804 , (GTK_FILL), (0), 0, 0);
805 else /* special, needed for header table full day events */
806 gtk_table_attach(GTK_TABLE(mw->dtable_h), ev, col, col+1, row, row+1
807 , (GTK_FILL), (0), 0, 0);
810 static void build_month_view_table(month_win *mw)
812 gint days; /* number of days to show */
815 struct tm tm_date, tm_today;
816 guint monthdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
818 time_t t = time(NULL);
820 int avail_w = 0, avail_d = 7;
826 if (mainwindow_get_mainwindow()) {
827 GtkAllocation allocation;
828 SummaryView *summaryview = mainwindow_get_mainwindow()->summaryview;
830 gtk_widget_get_allocation(summaryview->mainwidget_book,
833 avail_w = allocation.width - 25 - 2*(mw->hour_req.width);
834 avail_h = allocation.height - 20;
837 /* avail_d = avail_w / mw->StartDate_button_req.width; */
840 gtk_widget_set_size_request(mw->StartDate_button, avail_w / avail_d,
842 gtk_widget_size_request(mw->StartDate_button, &mw->StartDate_button_req);
845 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mw->day_spin), avail_d);
851 localtime_r(&t, &tm_today);
853 /****** header of day table = days columns ******/
854 mw->scroll_win_h = gtk_scrolled_window_new(NULL, NULL);
855 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(mw->scroll_win_h)
856 , GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
857 gtk_box_pack_start(GTK_BOX(mw->Vbox), mw->scroll_win_h
859 mw->month_view_vbox = gtk_vbox_new(FALSE, 0);
860 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(mw->scroll_win_h)
861 , mw->month_view_vbox);
863 gtk_container_add(GTK_CONTAINER(mw->scroll_win_h), mw->month_view_vbox);
865 /* row 1= day header buttons
866 * row 2= full day events after the buttons */
867 mw->dtable_h = gtk_table_new(2 , days+2, FALSE);
868 gtk_box_pack_start(GTK_BOX(mw->month_view_vbox), mw->dtable_h
871 tm_date = mw->startdate;
873 if (((tm_date.tm_year%4) == 0) && (((tm_date.tm_year%100) != 0)
874 || ((tm_date.tm_year%400) == 0)))
879 mw->Previous_toolbutton = gtk_event_box_new();
880 gtk_event_box_set_visible_window(GTK_EVENT_BOX(mw->Previous_toolbutton), FALSE);
881 gtk_container_set_border_width(GTK_CONTAINER(mw->Previous_toolbutton), 0);
882 arrow = gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_NONE);
883 gtk_container_add(GTK_CONTAINER(mw->Previous_toolbutton), arrow);
884 gtk_table_attach(GTK_TABLE(mw->dtable_h), mw->Previous_toolbutton, i, i+1, 0, 1
885 , (GTK_FILL), (0), 0, 0);
886 gtk_widget_show_all(mw->Previous_toolbutton);
887 g_signal_connect((gpointer)mw->Previous_toolbutton, "button_release_event"
888 , G_CALLBACK(on_Previous_clicked), mw);
889 CLAWS_SET_TIP(mw->Previous_toolbutton, _("Previous month"));
890 for (i = 1; i < days+1; i++) {
891 button = gtk_label_new(_(dayname[i-1]));
893 gtk_widget_set_size_request(button, mw->StartDate_button_req.width, -1);
894 g_object_set_data(G_OBJECT(button), "offset", GINT_TO_POINTER(i-1));
895 gtk_table_attach(GTK_TABLE(mw->dtable_h), button, i, i+1, 0, 1
896 , (GTK_FILL), (0), 0, 0);
899 mw->Next_toolbutton = gtk_event_box_new();
900 gtk_event_box_set_visible_window(GTK_EVENT_BOX(mw->Next_toolbutton), FALSE);
901 gtk_container_set_border_width(GTK_CONTAINER(mw->Next_toolbutton), 0);
902 arrow = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
903 gtk_container_add(GTK_CONTAINER(mw->Next_toolbutton), arrow);
904 gtk_table_attach(GTK_TABLE(mw->dtable_h), mw->Next_toolbutton, i, i+1, 0, 1
905 , (GTK_FILL), (0), 0, 0);
906 gtk_widget_show_all(mw->Next_toolbutton);
907 g_signal_connect((gpointer)mw->Next_toolbutton, "button_release_event"
908 , G_CALLBACK(on_Next_clicked), mw);
909 CLAWS_SET_TIP(mw->Next_toolbutton, _("Next month"));
911 /****** body of day table ******/
912 mw->scroll_win = gtk_scrolled_window_new(NULL, NULL);
913 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(mw->scroll_win)
915 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(mw->scroll_win)
916 , GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
917 gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(mw->scroll_win)
918 , GTK_CORNER_TOP_LEFT);
919 gtk_box_pack_start(GTK_BOX(mw->month_view_vbox), mw->scroll_win
921 vp = gtk_viewport_new(NULL, NULL);
922 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_IN);
923 gtk_container_add(GTK_CONTAINER(mw->scroll_win), vp);
924 mw->dtable = gtk_table_new(6, days+2, FALSE);
925 gtk_container_add(GTK_CONTAINER(vp), mw->dtable);
927 gtk_widget_show_all(mw->dtable_h);
929 date = g_date_new_dmy(1, 1, tm_date.tm_year+1900);
930 first_week = g_date_get_monday_week_of_year(date);
938 /* hours column = hour rows */
939 for (i = 0; i <= 6; i++) {
941 for (day = 1; day <= monthdays[tm_date.tm_mon]; day++) {
942 date = g_date_new_dmy(day, tm_date.tm_mon+1, tm_date.tm_year+1900);
943 int row = (int)g_date_get_monday_week_of_year(date);
944 if (weekoffset == -1) {
947 if (row - weekoffset == i) {
948 gchar *wn = g_strdup_printf("%d", row+first_week > 53?1:row+first_week);
949 fill_hour(mw, 0, i, wn);
950 fill_hour(mw, days+1, i, "");
958 fill_days(mw, days, mw->item);
961 void refresh_month_win(month_win *mw)
963 gtk_widget_destroy(mw->scroll_win_h);
964 build_month_view_table(mw);
965 gtk_widget_show_all(mw->scroll_win_h);
968 month_win *create_month_win(FolderItem *item, struct tm tmdate)
971 char *start_date = get_locale_date(&tmdate);
973 /* initialisation + main window + base vbox */
974 mw = g_new0(month_win, 1);
975 mw->scroll_pos = -1; /* not set */
977 mw->accel_group = gtk_accel_group_new();
979 while (tmdate.tm_mday != 1)
980 orage_move_day(&tmdate, -1);
982 mw->startdate = tmdate;
984 mw->Vbox = gtk_vbox_new(FALSE, 0);
987 build_month_view_colours(mw);
988 build_month_view_header(mw, start_date);
989 build_month_view_table(mw);
990 gtk_widget_show_all(mw->Vbox);
991 mw->selsig = vcal_view_set_calendar_page(mw->Vbox,
992 G_CALLBACK(mw_summary_selected), mw);
994 vcal_view_create_popup_menus(mw->Vbox, &mw->view_menu,
995 &mw->event_menu, &mw->event_group,