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.
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
*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
];
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 static gchar
*get_locale_date(struct tm
*tmdate
)
109 gchar
*d
= g_malloc(100);
110 strftime(d
, 99, "%x", tmdate
);
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
);
151 static char *orage_tm_date_to_i18_date(struct tm
*tm_date
)
153 static char i18_date
[32];
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;
164 if (strftime(i18_date
, 32, "%x", &t
) == 0)
165 g_error("Orage: orage_tm_date_to_i18_date too long string in strftime");
169 static void changeSelectedDate(day_win
*dw
, gint day
)
173 orage_move_day(&(dw
->startdate
), 1);
177 orage_move_day(&(dw
->startdate
), -1);
182 static gint
on_Previous_clicked(GtkWidget
*button
, GdkEventButton
*event
,
185 int days
= gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dw
->day_spin
));
186 changeSelectedDate(dw
, -days
);
191 static gint
on_Next_clicked(GtkWidget
*button
, GdkEventButton
*event
,
194 int days
= gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dw
->day_spin
));
195 changeSelectedDate(dw
, +days
);
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
);
209 time_t t_first
= mktime(&dw
->startdate
);
210 time_t t_start
= icaltime_as_timet(icaltime_from_string(event
->dtstart
));
212 gboolean changed
= FALSE
;
213 GtkAdjustment
*v_adj
;
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
);
228 while (t_start
> t_first
+ (days
-1)*24*60*60) {
229 changeSelectedDate(dw
, +days
);
230 t_first
= mktime(&dw
->startdate
);
234 t_start
= icaltime_as_timet(icaltime_from_string(event
->dtstart
));
239 localtime_r(&t_start
, &tm_start
);
241 debug_print("changed from %s\n", event
->summary
);
242 v_adj
= gtk_scrolled_window_get_vadjustment(
243 GTK_SCROLLED_WINDOW(dw
->scroll_win
));
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))));
253 gtk_adjustment_set_value(v_adj
, 0);
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)))
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
);
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
;
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) {
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",
338 g_object_set_data(G_OBJECT(dw
->Vbox
), "menu_data_i",
340 g_object_set_data(G_OBJECT(dw
->Vbox
), "menu_data_s",
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",
351 gtk_menu_popup_at_pointer(GTK_MENU(dw
->event_menu
), NULL
);
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
));
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;
391 start_col
= orage_days_between(&tm_first
, &tm_start
)+1;
392 end_col
= orage_days_between(&tm_first
, &tm_end
)+1;
396 if (start_col
> days
)
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
);
414 gtk_widget_modify_bg(ev
, GTK_STATE_NORMAL
, &dw
->bg1
);
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
;
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
);
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"
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
);
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
);
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 */
460 height
= dw
->StartDate_button_req
.height
;
462 * same_date = !strncmp(start_ical_time, end_ical_time, 8);
467 first_col
= start_col
;
472 for (col
= first_col
; col
<= last_col
; col
++) {
473 if (col
== start_col
)
474 start_row
= tm_start
.tm_hour
;
478 end_row
= tm_end
.tm_hour
;
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;
486 if (row
== tm_end
.tm_hour
&& col
== end_col
)
487 end_height
= tm_end
.tm_min
*height
/60;
490 dw
->line
[row
][col
] = build_line(1, start_height
491 , 2, end_height
-start_height
, dw
->line
[row
][col
]
498 static void app_rows(day_win
*dw
, FolderItem
*item
)
500 GSList
*events
= vcal_get_events_list(item
);
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
)
517 static void fill_days(day_win
*dw
, gint days
, FolderItem
*item
, gint first_col_day
)
519 gint row
, col
, height
, width
;
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
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);
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);
554 for (row
= 0; row
< 24; row
++) {
555 hb
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 2);
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
]
563 gtk_box_pack_start(GTK_BOX(hb
), dw
->element
[row
][col
]
565 gtk_widget_set_size_request(hb
, width
, -1);
566 gtk_widget_set_hexpand(hb
, TRUE
);
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);
578 gtk_widget_modify_bg(ev
, GTK_STATE_NORMAL
, &dw
->bg1
);
580 gtk_widget_modify_bg(ev
, GTK_STATE_NORMAL
, &dw
->bg2
);
581 gtk_box_pack_start(GTK_BOX(hb
), dw
->line
[row
][col
]
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);
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);
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
,
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
;
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
);
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
;
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
];
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);
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
);
720 gtk_widget_modify_bg(ev
, GTK_STATE_NORMAL
, &dw
->bg1
);
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
);
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 */
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};
740 time_t t
= time(NULL
);
743 gint first_col_day
= -1;
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
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
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)))
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
);
786 for (i
= 1; i
< days
+1; i
++) {
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
);
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) {
819 ++tm_date
.tm_wday
; tm_date
.tm_wday
%=7;
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
);
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
)
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
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
)
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");
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
,
907 g_timeout_add(100, (GSourceFunc
)scroll_position_timer
, (gpointer
)dw
);