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.
24 #include "claws-features.h"
29 #include <glib/gi18n.h>
38 #include <glib/gprintf.h>
39 #include <gdk/gdkkeysyms.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"
54 GtkAccelGroup
*accel_group
;
61 GtkWidget
*File_menu_new
;
62 GtkWidget
*File_menu_close
;
64 GtkWidget
*View_menu_refresh
;
66 GtkWidget
*Go_menu_today
;
67 GtkWidget
*Go_menu_prev
;
68 GtkWidget
*Go_menu_next
;
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
;
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
];
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 */
101 GtkWidget
*view_menu
;
102 GtkWidget
*event_menu
;
103 GtkActionGroup
*event_group
;
104 GtkUIManager
*ui_manager
;
107 gchar
*dayname
[7] = {
116 gchar
*monthname
[12] = {
131 static gchar
*get_locale_date(struct tm
*tmdate
)
133 gchar
*d
= g_malloc(100);
134 strftime(d
, 99, "%x", tmdate
);
138 void mw_close_window(month_win
*mw
)
140 vcal_view_set_summary_page(mw
->Vbox
, mw
->selsig
);
147 static char *orage_tm_date_to_i18_date(struct tm
*tm_date
)
149 static char i18_date
[32];
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;
159 if (strftime(i18_date
, 32, "%x", &t
) == 0)
160 g_error("Orage: orage_tm_date_to_i18_date too long string in strftime");
164 static void changeSelectedDate(month_win
*mw
, gint month
)
166 gint curmon
= mw
->startdate
.tm_mon
;
168 do { /* go to first of next month */
169 orage_move_day(&(mw
->startdate
), 1);
170 } while (curmon
== mw
->startdate
.tm_mon
);
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
,
184 changeSelectedDate(mw
, -1);
185 refresh_month_win(mw
);
189 static gint
on_Next_clicked(GtkWidget
*button
, GdkEventButton
*event
,
192 changeSelectedDate(mw
, +1);
193 refresh_month_win(mw
);
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
);
206 time_t t_start
= icaltime_as_timet(icaltime_from_string(event
->dtstart
));
207 gboolean changed
= FALSE
;
213 localtime_r(&t_start
, &tm_start
);
214 while (tm_start
.tm_year
< mw
->startdate
.tm_year
) {
215 changeSelectedDate(mw
, -1);
218 while (tm_start
.tm_year
> mw
->startdate
.tm_year
) {
219 changeSelectedDate(mw
, +1);
222 while (tm_start
.tm_mon
< mw
->startdate
.tm_mon
) {
223 changeSelectedDate(mw
, -1);
226 while (tm_start
.tm_mon
> mw
->startdate
.tm_mon
) {
227 changeSelectedDate(mw
, +1);
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);
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
);
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",
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",
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) {
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",
320 g_object_set_data(G_OBJECT(mw
->Vbox
), "menu_data_i",
322 g_object_set_data(G_OBJECT(mw
->Vbox
), "menu_data_s",
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};
347 gboolean pack
= TRUE
, update_tip
= FALSE
;
348 time_t now
= time(NULL
);
350 gboolean start_prev_mon
= FALSE
;
352 localtime_r(&now
, &tm_today
);
354 tm_today
.tm_year
+= 1900;
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
));
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;
387 start_col
= orage_days_between(&tm_first
, &tm_start
)+1;
388 end_col
= orage_days_between(&tm_first
, &tm_end
)+1;
391 start_prev_mon
= TRUE
;
396 if (start_col
> 0 && start_col
> monthdays
[tm_first
.tm_mon
-1]) {
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
);
403 if (start_col
>= 1) {
404 sdate
= g_date_new_dmy(tm_start
.tm_mday
, tm_start
.tm_mon
, tm_start
.tm_year
);
406 sdate
= g_date_new_dmy(1, tm_first
.tm_mon
, tm_first
.tm_year
);
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
;
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
;
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) {
439 } else if (g_list_length(children
) > 1) {
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
);
452 gtk_widget_modify_bg(ev
, GTK_STATE_NORMAL
, &mw
->bg1
);
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
);
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"
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
);
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);
491 gchar
*old
= gtk_widget_get_tooltip_text(ev
);
492 gchar
*new = g_strdup_printf("%s\n\n%s", old
?old
:"", tip
);
495 CLAWS_SET_TIP(ev
, 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
);
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 */
509 width
= mw
->StartDate_button_req
.width
;
511 * same_date = !strncmp(start_ical_time, end_ical_time, 8);
516 first_row
= start_row
;
521 for (row
= first_row
; row
<= last_row
; row
++) {
522 if (row
== start_row
)
523 first_col
= start_col
;
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);
535 if (row
== last_row
&& col
== last_col
)
536 end_width
= ((tm_end
.tm_hour
*60)+(tm_end
.tm_min
))*width
/(24*60);
540 mw
->line
[row
][col
] = build_line(start_width
, 0
541 , end_width
-start_width
, 2, mw
->line
[row
][col
]
548 static void app_rows(month_win
*mw
, FolderItem
*item
)
550 GSList
*events
= vcal_get_events_list(item
);
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
)
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
;
574 time_t now
= time(NULL
);
577 localtime_r(&now
, &tm_today
);
581 tm_today
.tm_year
+= 1900;
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) {
606 row
= row
- weekoffset
;
608 mw
->element
[row
][dcol
] = NULL
;
609 mw
->line
[row
][dcol
] = build_line(0, 0, width
, 3, NULL
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
);
625 if (weekoffset
== -1) {
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]));
639 label
= g_strdup_printf("%d", g_date_get_day(date
));
640 tmp
= g_strdup_printf("%s %d %s %d",
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
);
660 gtk_widget_modify_bg(ev
, GTK_STATE_NORMAL
, &mw
->bg1
);
662 gtk_widget_modify_bg(ev
, GTK_STATE_NORMAL
, &mw
->bg2
);
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
]
677 gtk_grid_attach(GTK_GRID(mw
->dtable
), vb
, col
, row
, 1, 1);
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);
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
;
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
];
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);
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
);
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 */
804 struct tm tm_date
, tm_today
;
805 guint monthdays
[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
807 time_t t
= time(NULL
);
809 int avail_w
= 0, avail_d
= 7;
815 if (mainwindow_get_mainwindow()) {
816 GtkAllocation allocation
;
817 SummaryView
*summaryview
= mainwindow_get_mainwindow()->summaryview
;
819 gtk_widget_get_allocation(summaryview
->mainwidget_book
,
822 avail_w
= allocation
.width
- 25 - 2*(mw
->hour_req
.width
);
823 avail_h
= allocation
.height
- 20;
826 /* avail_d = avail_w / mw->StartDate_button_req.width; */
829 gtk_widget_set_size_request(mw
->StartDate_button
, avail_w
/ avail_d
,
831 gtk_widget_get_preferred_size(mw
->StartDate_button
, &mw
->StartDate_button_req
,
835 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mw
->day_spin
), avail_d
);
841 localtime_r(&t
, &tm_today
);
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
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
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)))
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
)
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
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
);
922 /* hours column = hour rows */
923 for (i
= 0; i
<= 6; i
++) {
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) {
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
, "");
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
)
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");
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
,