1 | /* GTK - The GIMP Toolkit |
2 | * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
3 | * |
4 | * GTK Calendar Widget |
5 | * Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson and Mattias Groenlund |
6 | * |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2 of the License, or (at your option) any later version. |
11 | * |
12 | * This library 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 GNU |
15 | * Lesser General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | /* |
22 | * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS |
23 | * file for a list of people on the GTK+ Team. See the ChangeLog |
24 | * files for a list of changes. These files are distributed with |
25 | * GTK+ at ftp://ftp.gtk.org/pub/gtk/. |
26 | */ |
27 | |
28 | /** |
29 | * GtkCalendar: |
30 | * |
31 | * `GtkCalendar` is a widget that displays a Gregorian calendar, one month |
32 | * at a time. |
33 | * |
34 | * ![An example GtkCalendar](calendar.png) |
35 | * |
36 | * A `GtkCalendar` can be created with [ctor@Gtk.Calendar.new]. |
37 | * |
38 | * The date that is currently displayed can be altered with |
39 | * [method@Gtk.Calendar.select_day]. |
40 | * |
41 | * To place a visual marker on a particular day, use |
42 | * [method@Gtk.Calendar.mark_day] and to remove the marker, |
43 | * [method@Gtk.Calendar.unmark_day]. Alternative, all |
44 | * marks can be cleared with [method@Gtk.Calendar.clear_marks]. |
45 | * |
46 | * The selected date can be retrieved from a `GtkCalendar` using |
47 | * [method@Gtk.Calendar.get_date]. |
48 | * |
49 | * Users should be aware that, although the Gregorian calendar is the |
50 | * legal calendar in most countries, it was adopted progressively |
51 | * between 1582 and 1929. Display before these dates is likely to be |
52 | * historically incorrect. |
53 | * |
54 | * # CSS nodes |
55 | * |
56 | * ``` |
57 | * calendar.view |
58 | * ├── header |
59 | * │ ├── button |
60 | * │ ├── stack.month |
61 | * │ ├── button |
62 | * │ ├── button |
63 | * │ ├── label.year |
64 | * │ ╰── button |
65 | * ╰── grid |
66 | * ╰── label[.day-name][.week-number][.day-number][.other-month][.today] |
67 | * ``` |
68 | * |
69 | * `GtkCalendar` has a main node with name calendar. It contains a subnode |
70 | * called header containing the widgets for switching between years and months. |
71 | * |
72 | * The grid subnode contains all day labels, including week numbers on the left |
73 | * (marked with the .week-number css class) and day names on top (marked with the |
74 | * .day-name css class). |
75 | * |
76 | * Day labels that belong to the previous or next month get the .other-month |
77 | * style class. The label of the current day get the .today style class. |
78 | * |
79 | * Marked day labels get the :selected state assigned. |
80 | */ |
81 | |
82 | #include "config.h" |
83 | |
84 | #ifdef HAVE_SYS_TIME_H |
85 | #include <sys/time.h> |
86 | #endif |
87 | #ifdef HAVE__NL_TIME_FIRST_WEEKDAY |
88 | #include <langinfo.h> |
89 | #endif |
90 | #include <string.h> |
91 | #include <stdlib.h> |
92 | #include <time.h> |
93 | |
94 | #include <glib.h> |
95 | |
96 | #ifdef G_OS_WIN32 |
97 | #include <windows.h> |
98 | #endif |
99 | |
100 | #include "gtkcalendar.h" |
101 | #include "gtkdroptarget.h" |
102 | #include "gtkintl.h" |
103 | #include "gtkmain.h" |
104 | #include "gtkmarshalers.h" |
105 | #include "gtktooltip.h" |
106 | #include "gtkprivate.h" |
107 | #include "gtkrendericonprivate.h" |
108 | #include "gtksnapshot.h" |
109 | #include "gtkwidgetprivate.h" |
110 | #include "gtkgestureclick.h" |
111 | #include "gtkgesturedrag.h" |
112 | #include "gtkeventcontrollerscroll.h" |
113 | #include "gtkeventcontrollerkey.h" |
114 | #include "gtkeventcontrollerfocus.h" |
115 | #include "gtkdragsource.h" |
116 | #include "gtknative.h" |
117 | #include "gtkicontheme.h" |
118 | #include "gtkdragicon.h" |
119 | #include "gtkbutton.h" |
120 | #include "gtkbox.h" |
121 | #include "gtkboxlayout.h" |
122 | #include "gtkorientable.h" |
123 | #include "gtklabel.h" |
124 | #include "gtkstack.h" |
125 | #include "gtkgrid.h" |
126 | |
127 | static const guint month_length[2][13] = |
128 | { |
129 | { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, |
130 | { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } |
131 | }; |
132 | |
133 | static gboolean |
134 | leap (guint year) |
135 | { |
136 | return ((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0)); |
137 | } |
138 | |
139 | static guint |
140 | day_of_week (guint year, guint mm, guint dd) |
141 | { |
142 | GDateTime *dt; |
143 | guint days; |
144 | |
145 | dt = g_date_time_new_local (year, month: mm, day: dd, hour: 1, minute: 1, seconds: 1); |
146 | if (dt == NULL) |
147 | return 0; |
148 | |
149 | days = g_date_time_get_day_of_week (datetime: dt); |
150 | g_date_time_unref (datetime: dt); |
151 | |
152 | return days; |
153 | } |
154 | |
155 | static guint |
156 | week_of_year (guint year, guint mm, guint dd) |
157 | { |
158 | GDateTime *dt; |
159 | guint week; |
160 | |
161 | dt = g_date_time_new_local (year, month: mm, day: dd, hour: 1, minute: 1, seconds: 1); |
162 | if (dt == NULL) |
163 | return 1; |
164 | |
165 | week = g_date_time_get_week_of_year (datetime: dt); |
166 | g_date_time_unref (datetime: dt); |
167 | |
168 | return week; |
169 | } |
170 | |
171 | enum { |
172 | MONTH_PREV, |
173 | MONTH_CURRENT, |
174 | MONTH_NEXT |
175 | }; |
176 | |
177 | enum { |
178 | DAY_SELECTED_SIGNAL, |
179 | PREV_MONTH_SIGNAL, |
180 | NEXT_MONTH_SIGNAL, |
181 | PREV_YEAR_SIGNAL, |
182 | NEXT_YEAR_SIGNAL, |
183 | LAST_SIGNAL |
184 | }; |
185 | |
186 | enum |
187 | { |
188 | PROP_0, |
189 | PROP_YEAR, |
190 | PROP_MONTH, |
191 | PROP_DAY, |
192 | PROP_SHOW_HEADING, |
193 | PROP_SHOW_DAY_NAMES, |
194 | PROP_SHOW_WEEK_NUMBERS, |
195 | }; |
196 | |
197 | static guint gtk_calendar_signals[LAST_SIGNAL] = { 0 }; |
198 | |
199 | typedef struct _GtkCalendarClass GtkCalendarClass; |
200 | typedef struct _GtkCalendarPrivate GtkCalendarPrivate; |
201 | |
202 | struct _GtkCalendar |
203 | { |
204 | GtkWidget widget; |
205 | |
206 | guint show_week_numbers : 1; |
207 | guint show_heading : 1; |
208 | guint show_day_names : 1; |
209 | guint year_before : 1; |
210 | |
211 | GtkWidget *; |
212 | GtkWidget *year_label; |
213 | GtkWidget *month_name_stack; |
214 | GtkWidget *arrow_widgets[4]; |
215 | |
216 | GtkWidget *grid; |
217 | GtkWidget *day_name_labels[7]; |
218 | GtkWidget *week_number_labels[6]; |
219 | GtkWidget *day_number_labels[6][7]; |
220 | |
221 | GDateTime *date; |
222 | |
223 | int day_month[6][7]; |
224 | int day[6][7]; |
225 | |
226 | int num_marked_dates; |
227 | int marked_date[31]; |
228 | |
229 | int focus_row; |
230 | int focus_col; |
231 | |
232 | int week_start; |
233 | }; |
234 | |
235 | struct _GtkCalendarClass |
236 | { |
237 | GtkWidgetClass parent_class; |
238 | |
239 | void (* day_selected) (GtkCalendar *calendar); |
240 | void (* prev_month) (GtkCalendar *calendar); |
241 | void (* next_month) (GtkCalendar *calendar); |
242 | void (* prev_year) (GtkCalendar *calendar); |
243 | void (* next_year) (GtkCalendar *calendar); |
244 | }; |
245 | |
246 | static void gtk_calendar_set_property (GObject *object, |
247 | guint prop_id, |
248 | const GValue *value, |
249 | GParamSpec *pspec); |
250 | static void gtk_calendar_get_property (GObject *object, |
251 | guint prop_id, |
252 | GValue *value, |
253 | GParamSpec *pspec); |
254 | |
255 | static void gtk_calendar_button_press (GtkGestureClick *gesture, |
256 | int n_press, |
257 | double x, |
258 | double y, |
259 | gpointer user_data); |
260 | static gboolean gtk_calendar_key_controller_key_pressed (GtkEventControllerKey *controller, |
261 | guint keyval, |
262 | guint keycode, |
263 | GdkModifierType state, |
264 | GtkWidget *widget); |
265 | static void gtk_calendar_focus_controller_focus (GtkEventController *controller, |
266 | GtkWidget *widget); |
267 | |
268 | static void calendar_invalidate_day (GtkCalendar *widget, |
269 | int row, |
270 | int col); |
271 | static void calendar_invalidate_day_num (GtkCalendar *widget, |
272 | int day); |
273 | |
274 | static gboolean gtk_calendar_scroll_controller_scroll (GtkEventControllerScroll *scroll, |
275 | double dx, |
276 | double dy, |
277 | GtkWidget *widget); |
278 | |
279 | static void calendar_set_month_prev (GtkCalendar *calendar); |
280 | static void calendar_set_month_next (GtkCalendar *calendar); |
281 | static void calendar_set_year_prev (GtkCalendar *calendar); |
282 | static void calendar_set_year_next (GtkCalendar *calendar); |
283 | |
284 | |
285 | static char *default_abbreviated_dayname[7]; |
286 | static char *default_monthname[12]; |
287 | |
288 | G_DEFINE_TYPE (GtkCalendar, gtk_calendar, GTK_TYPE_WIDGET) |
289 | |
290 | static void |
291 | gtk_calendar_drag_notify_value (GtkDropTarget *target, |
292 | GParamSpec **pspec, |
293 | GtkCalendar *calendar) |
294 | { |
295 | GDate *date; |
296 | const GValue *value; |
297 | |
298 | value = gtk_drop_target_get_value (self: target); |
299 | if (value == NULL) |
300 | return; |
301 | |
302 | date = g_date_new (); |
303 | g_date_set_parse (date, str: g_value_get_string (value)); |
304 | if (!g_date_valid (date)) |
305 | gtk_drop_target_reject (self: target); |
306 | g_date_free (date); |
307 | } |
308 | |
309 | static gboolean |
310 | gtk_calendar_drag_drop (GtkDropTarget *dest, |
311 | const GValue *value, |
312 | double x, |
313 | double y, |
314 | GtkCalendar *calendar) |
315 | { |
316 | GDate *date; |
317 | GDateTime *datetime; |
318 | |
319 | date = g_date_new (); |
320 | g_date_set_parse (date, str: g_value_get_string (value)); |
321 | |
322 | if (!g_date_valid (date)) |
323 | { |
324 | g_warning ("Received invalid date data" ); |
325 | g_date_free (date); |
326 | return FALSE; |
327 | } |
328 | |
329 | datetime = g_date_time_new_local (year: g_date_get_year (date), |
330 | month: g_date_get_month (date), |
331 | day: g_date_get_day (date), |
332 | hour: 0, minute: 0, seconds: 0); |
333 | g_date_free (date); |
334 | |
335 | gtk_calendar_select_day (self: calendar, date: datetime); |
336 | g_date_time_unref (datetime); |
337 | |
338 | return TRUE; |
339 | } |
340 | |
341 | static void |
342 | gtk_calendar_dispose (GObject *object) |
343 | { |
344 | GtkCalendar *calendar = GTK_CALENDAR (object); |
345 | |
346 | g_clear_pointer (&calendar->date, g_date_time_unref); |
347 | g_clear_pointer (&calendar->header_box, gtk_widget_unparent); |
348 | g_clear_pointer (&calendar->grid, gtk_widget_unparent); |
349 | |
350 | G_OBJECT_CLASS (gtk_calendar_parent_class)->dispose (object); |
351 | } |
352 | |
353 | static void |
354 | gtk_calendar_class_init (GtkCalendarClass *class) |
355 | { |
356 | GObjectClass *gobject_class; |
357 | GtkWidgetClass *widget_class; |
358 | |
359 | gobject_class = (GObjectClass*) class; |
360 | widget_class = (GtkWidgetClass*) class; |
361 | |
362 | gobject_class->dispose = gtk_calendar_dispose; |
363 | gobject_class->set_property = gtk_calendar_set_property; |
364 | gobject_class->get_property = gtk_calendar_get_property; |
365 | |
366 | /** |
367 | * GtkCalendar:year: |
368 | * |
369 | * The selected year. |
370 | * |
371 | * This property gets initially set to the current year. |
372 | */ |
373 | g_object_class_install_property (oclass: gobject_class, |
374 | property_id: PROP_YEAR, |
375 | pspec: g_param_spec_int (name: "year" , |
376 | P_("Year" ), |
377 | P_("The selected year" ), |
378 | minimum: 1, maximum: 9999, default_value: 1, |
379 | flags: G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); |
380 | |
381 | /** |
382 | * GtkCalendar:month: |
383 | * |
384 | * The selected month (as a number between 0 and 11). |
385 | * |
386 | * This property gets initially set to the current month. |
387 | */ |
388 | g_object_class_install_property (oclass: gobject_class, |
389 | property_id: PROP_MONTH, |
390 | pspec: g_param_spec_int (name: "month" , |
391 | P_("Month" ), |
392 | P_("The selected month (as a number between 0 and 11)" ), |
393 | minimum: 0, maximum: 11, default_value: 0, |
394 | flags: G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); |
395 | |
396 | /** |
397 | * GtkCalendar:day: |
398 | * |
399 | * The selected day (as a number between 1 and 31). |
400 | */ |
401 | g_object_class_install_property (oclass: gobject_class, |
402 | property_id: PROP_DAY, |
403 | pspec: g_param_spec_int (name: "day" , |
404 | P_("Day" ), |
405 | P_("The selected day (as a number between 1 and 31)" ), |
406 | minimum: 1, maximum: 31, default_value: 1, |
407 | flags: G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); |
408 | |
409 | /** |
410 | * GtkCalendar:show-heading: (attributes org.gtk.Property.get=gtk_calendar_get_show_heading org.gtk.Property.set=gtk_calendar_set_show_heading) |
411 | * |
412 | * Determines whether a heading is displayed. |
413 | */ |
414 | g_object_class_install_property (oclass: gobject_class, |
415 | property_id: PROP_SHOW_HEADING, |
416 | pspec: g_param_spec_boolean (name: "show-heading" , |
417 | P_("Show Heading" ), |
418 | P_("If TRUE, a heading is displayed" ), |
419 | TRUE, |
420 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); |
421 | |
422 | /** |
423 | * GtkCalendar:show-day-names: (attributes org.gtk.Property.get=gtk_calendar_get_show_day_names org.gtk.Property.set=gtk_calendar_set_show_day_names) |
424 | * |
425 | * Determines whether day names are displayed. |
426 | */ |
427 | g_object_class_install_property (oclass: gobject_class, |
428 | property_id: PROP_SHOW_DAY_NAMES, |
429 | pspec: g_param_spec_boolean (name: "show-day-names" , |
430 | P_("Show Day Names" ), |
431 | P_("If TRUE, day names are displayed" ), |
432 | TRUE, |
433 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); |
434 | /** |
435 | * GtkCalendar:show-week-numbers: (attributes org.gtk.Property.get=gtk_calendar_get_show_week_numbers org.gtk.Property.set=gtk_calendar_set_show_week_numbers) |
436 | * |
437 | * Determines whether week numbers are displayed. |
438 | */ |
439 | g_object_class_install_property (oclass: gobject_class, |
440 | property_id: PROP_SHOW_WEEK_NUMBERS, |
441 | pspec: g_param_spec_boolean (name: "show-week-numbers" , |
442 | P_("Show Week Numbers" ), |
443 | P_("If TRUE, week numbers are displayed" ), |
444 | FALSE, |
445 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); |
446 | |
447 | /** |
448 | * GtkCalendar::day-selected: |
449 | * @calendar: the object which received the signal. |
450 | * |
451 | * Emitted when the user selects a day. |
452 | */ |
453 | gtk_calendar_signals[DAY_SELECTED_SIGNAL] = |
454 | g_signal_new (I_("day-selected" ), |
455 | G_OBJECT_CLASS_TYPE (gobject_class), |
456 | signal_flags: G_SIGNAL_RUN_FIRST, |
457 | G_STRUCT_OFFSET (GtkCalendarClass, day_selected), |
458 | NULL, NULL, |
459 | NULL, |
460 | G_TYPE_NONE, n_params: 0); |
461 | |
462 | /** |
463 | * GtkCalendar::prev-month: |
464 | * @calendar: the object which received the signal. |
465 | * |
466 | * Emitted when the user switched to the previous month. |
467 | */ |
468 | gtk_calendar_signals[PREV_MONTH_SIGNAL] = |
469 | g_signal_new (I_("prev-month" ), |
470 | G_OBJECT_CLASS_TYPE (gobject_class), |
471 | signal_flags: G_SIGNAL_RUN_FIRST, |
472 | G_STRUCT_OFFSET (GtkCalendarClass, prev_month), |
473 | NULL, NULL, |
474 | NULL, |
475 | G_TYPE_NONE, n_params: 0); |
476 | |
477 | /** |
478 | * GtkCalendar::next-month: |
479 | * @calendar: the object which received the signal. |
480 | * |
481 | * Emitted when the user switched to the next month. |
482 | */ |
483 | gtk_calendar_signals[NEXT_MONTH_SIGNAL] = |
484 | g_signal_new (I_("next-month" ), |
485 | G_OBJECT_CLASS_TYPE (gobject_class), |
486 | signal_flags: G_SIGNAL_RUN_FIRST, |
487 | G_STRUCT_OFFSET (GtkCalendarClass, next_month), |
488 | NULL, NULL, |
489 | NULL, |
490 | G_TYPE_NONE, n_params: 0); |
491 | |
492 | /** |
493 | * GtkCalendar::prev-year: |
494 | * @calendar: the object which received the signal. |
495 | * |
496 | * Emitted when user switched to the previous year. |
497 | */ |
498 | gtk_calendar_signals[PREV_YEAR_SIGNAL] = |
499 | g_signal_new (I_("prev-year" ), |
500 | G_OBJECT_CLASS_TYPE (gobject_class), |
501 | signal_flags: G_SIGNAL_RUN_FIRST, |
502 | G_STRUCT_OFFSET (GtkCalendarClass, prev_year), |
503 | NULL, NULL, |
504 | NULL, |
505 | G_TYPE_NONE, n_params: 0); |
506 | |
507 | /** |
508 | * GtkCalendar::next-year: |
509 | * @calendar: the object which received the signal. |
510 | * |
511 | * Emitted when user switched to the next year. |
512 | */ |
513 | gtk_calendar_signals[NEXT_YEAR_SIGNAL] = |
514 | g_signal_new (I_("next-year" ), |
515 | G_OBJECT_CLASS_TYPE (gobject_class), |
516 | signal_flags: G_SIGNAL_RUN_FIRST, |
517 | G_STRUCT_OFFSET (GtkCalendarClass, next_year), |
518 | NULL, NULL, |
519 | NULL, |
520 | G_TYPE_NONE, n_params: 0); |
521 | |
522 | gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT); |
523 | gtk_widget_class_set_css_name (widget_class, I_("calendar" )); |
524 | } |
525 | |
526 | static GdkContentProvider * |
527 | gtk_calendar_drag_prepare (GtkDragSource *source, |
528 | double x, |
529 | double y, |
530 | GtkCalendar *calendar) |
531 | { |
532 | GDate *date; |
533 | char str[128]; |
534 | |
535 | date = g_date_new_dmy (day: g_date_time_get_day_of_month (datetime: calendar->date), |
536 | month: g_date_time_get_month (datetime: calendar->date), |
537 | year: g_date_time_get_year (datetime: calendar->date)); |
538 | g_date_strftime (s: str, slen: 127, format: "%x" , date); |
539 | g_free (mem: date); |
540 | |
541 | return gdk_content_provider_new_typed (G_TYPE_STRING, str); |
542 | } |
543 | |
544 | #pragma GCC diagnostic push |
545 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" |
546 | |
547 | static void |
548 | gtk_calendar_init (GtkCalendar *calendar) |
549 | { |
550 | GtkWidget *widget = GTK_WIDGET (calendar); |
551 | GtkEventController *controller; |
552 | GtkGesture *gesture; |
553 | GtkDragSource *source; |
554 | GtkDropTarget *target; |
555 | int i; |
556 | #ifdef G_OS_WIN32 |
557 | wchar_t wbuffer[100]; |
558 | #else |
559 | static const char *month_format = NULL; |
560 | char buffer[255]; |
561 | time_t tmp_time; |
562 | #endif |
563 | char *year_before; |
564 | #ifdef HAVE__NL_TIME_FIRST_WEEKDAY |
565 | union { unsigned int word; char *string; } langinfo; |
566 | int week_1stday = 0; |
567 | int first_weekday = 1; |
568 | guint week_origin; |
569 | #else |
570 | char *week_start; |
571 | #endif |
572 | int min_year_width; |
573 | GDateTime *now; |
574 | |
575 | gtk_widget_set_focusable (widget, TRUE); |
576 | |
577 | gtk_widget_add_css_class (GTK_WIDGET (calendar), css_class: "view" ); |
578 | |
579 | calendar->header_box = g_object_new (GTK_TYPE_BOX, |
580 | first_property_name: "css-name" , "header" , |
581 | NULL); |
582 | calendar->year_label = gtk_label_new (str: "" ); |
583 | gtk_widget_add_css_class (widget: calendar->year_label, css_class: "year" ); |
584 | calendar->month_name_stack = gtk_stack_new (); |
585 | gtk_widget_add_css_class (widget: calendar->month_name_stack, css_class: "month" ); |
586 | calendar->arrow_widgets[0] = gtk_button_new_from_icon_name (icon_name: "pan-start-symbolic" ); |
587 | g_signal_connect_swapped (calendar->arrow_widgets[0], "clicked" , G_CALLBACK (calendar_set_month_prev), calendar); |
588 | calendar->arrow_widgets[1] = gtk_button_new_from_icon_name (icon_name: "pan-end-symbolic" ); |
589 | g_signal_connect_swapped (calendar->arrow_widgets[1], "clicked" , G_CALLBACK (calendar_set_month_next), calendar); |
590 | gtk_widget_set_hexpand (widget: calendar->arrow_widgets[1], TRUE); |
591 | gtk_widget_set_halign (widget: calendar->arrow_widgets[1], align: GTK_ALIGN_START); |
592 | calendar->arrow_widgets[2] = gtk_button_new_from_icon_name (icon_name: "pan-start-symbolic" ); |
593 | g_signal_connect_swapped (calendar->arrow_widgets[2], "clicked" , G_CALLBACK (calendar_set_year_prev), calendar); |
594 | calendar->arrow_widgets[3] = gtk_button_new_from_icon_name (icon_name: "pan-end-symbolic" ); |
595 | g_signal_connect_swapped (calendar->arrow_widgets[3], "clicked" , G_CALLBACK (calendar_set_year_next), calendar); |
596 | |
597 | gtk_box_append (GTK_BOX (calendar->header_box), child: calendar->arrow_widgets[0]); |
598 | gtk_box_append (GTK_BOX (calendar->header_box), child: calendar->month_name_stack); |
599 | gtk_box_append (GTK_BOX (calendar->header_box), child: calendar->arrow_widgets[1]); |
600 | gtk_box_append (GTK_BOX (calendar->header_box), child: calendar->arrow_widgets[2]); |
601 | gtk_box_append (GTK_BOX (calendar->header_box), child: calendar->year_label); |
602 | gtk_box_append (GTK_BOX (calendar->header_box), child: calendar->arrow_widgets[3]); |
603 | |
604 | gtk_widget_set_parent (widget: calendar->header_box, GTK_WIDGET (calendar)); |
605 | |
606 | gesture = gtk_gesture_click_new (); |
607 | g_signal_connect (gesture, "pressed" , G_CALLBACK (gtk_calendar_button_press), calendar); |
608 | gtk_widget_add_controller (GTK_WIDGET (calendar), GTK_EVENT_CONTROLLER (gesture)); |
609 | |
610 | source = gtk_drag_source_new (); |
611 | g_signal_connect (source, "prepare" , G_CALLBACK (gtk_calendar_drag_prepare), calendar); |
612 | gtk_widget_add_controller (GTK_WIDGET (calendar), GTK_EVENT_CONTROLLER (source)); |
613 | |
614 | controller = |
615 | gtk_event_controller_scroll_new (flags: GTK_EVENT_CONTROLLER_SCROLL_VERTICAL | |
616 | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE); |
617 | g_signal_connect (controller, "scroll" , |
618 | G_CALLBACK (gtk_calendar_scroll_controller_scroll), |
619 | calendar); |
620 | gtk_widget_add_controller (GTK_WIDGET (calendar), controller); |
621 | |
622 | controller = gtk_event_controller_key_new (); |
623 | g_signal_connect (controller, "key-pressed" , |
624 | G_CALLBACK (gtk_calendar_key_controller_key_pressed), |
625 | calendar); |
626 | gtk_widget_add_controller (GTK_WIDGET (calendar), controller); |
627 | controller = gtk_event_controller_focus_new (); |
628 | g_signal_connect (controller, "enter" , |
629 | G_CALLBACK (gtk_calendar_focus_controller_focus), |
630 | calendar); |
631 | g_signal_connect (controller, "leave" , |
632 | G_CALLBACK (gtk_calendar_focus_controller_focus), |
633 | calendar); |
634 | gtk_widget_add_controller (GTK_WIDGET (calendar), controller); |
635 | |
636 | #ifdef G_OS_WIN32 |
637 | calendar->week_start = 0; |
638 | week_start = NULL; |
639 | |
640 | if (GetLocaleInfoW (GetThreadLocale (), LOCALE_IFIRSTDAYOFWEEK, |
641 | wbuffer, G_N_ELEMENTS (wbuffer))) |
642 | week_start = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL); |
643 | |
644 | if (week_start != NULL) |
645 | { |
646 | calendar->week_start = (week_start[0] - '0' + 1) % 7; |
647 | g_free(week_start); |
648 | } |
649 | #else |
650 | #ifdef HAVE__NL_TIME_FIRST_WEEKDAY |
651 | langinfo.string = nl_langinfo (item: _NL_TIME_FIRST_WEEKDAY); |
652 | first_weekday = langinfo.string[0]; |
653 | langinfo.string = nl_langinfo (item: _NL_TIME_WEEK_1STDAY); |
654 | week_origin = langinfo.word; |
655 | if (week_origin == 19971130) /* Sunday */ |
656 | week_1stday = 0; |
657 | else if (week_origin == 19971201) /* Monday */ |
658 | week_1stday = 1; |
659 | else |
660 | g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY." ); |
661 | |
662 | calendar->week_start = (week_1stday + first_weekday - 1) % 7; |
663 | #else |
664 | /* Translate to calendar:week_start:0 if you want Sunday to be the |
665 | * first day of the week to calendar:week_start:1 if you want Monday |
666 | * to be the first day of the week, and so on. |
667 | */ |
668 | week_start = _("calendar:week_start:0" ); |
669 | |
670 | if (strncmp (week_start, "calendar:week_start:" , 20) == 0) |
671 | calendar->week_start = *(week_start + 20) - '0'; |
672 | else |
673 | calendar->week_start = -1; |
674 | |
675 | if (calendar->week_start < 0 || calendar->week_start > 6) |
676 | { |
677 | g_warning ("Whoever translated calendar:week_start:0 did so wrongly." ); |
678 | calendar->week_start = 0; |
679 | } |
680 | #endif |
681 | #endif |
682 | |
683 | if (!default_abbreviated_dayname[0]) |
684 | for (i=0; i<7; i++) |
685 | { |
686 | #ifndef G_OS_WIN32 |
687 | tmp_time= (i+3)*86400; /* epoch was a Thursday, so add 3 days for Sunday */ |
688 | strftime (s: buffer, maxsize: sizeof (buffer), format: "%a" , tp: gmtime (timer: &tmp_time)); |
689 | default_abbreviated_dayname[i] = g_locale_to_utf8 (opsysstring: buffer, len: -1, NULL, NULL, NULL); |
690 | #else |
691 | if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SABBREVDAYNAME1 + (i+6)%7, |
692 | wbuffer, G_N_ELEMENTS (wbuffer))) |
693 | default_abbreviated_dayname[i] = g_strdup_printf ("(%d)" , i); |
694 | else |
695 | default_abbreviated_dayname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL); |
696 | #endif |
697 | } |
698 | |
699 | if (!default_monthname[0]) |
700 | for (i=0; i<12; i++) |
701 | { |
702 | #ifndef G_OS_WIN32 |
703 | tmp_time=i*2764800; |
704 | if (G_UNLIKELY (month_format == NULL)) |
705 | { |
706 | buffer[0] = '\0'; |
707 | month_format = "%OB" ; |
708 | strftime (s: buffer, maxsize: sizeof (buffer), format: month_format, tp: gmtime (timer: &tmp_time)); |
709 | /* "%OB" is not supported in Linux with glibc < 2.27 */ |
710 | if (!strcmp (s1: buffer, s2: "%OB" ) || !strcmp (s1: buffer, s2: "OB" ) || !strcmp (s1: buffer, s2: "" )) |
711 | { |
712 | month_format = "%B" ; |
713 | strftime (s: buffer, maxsize: sizeof (buffer), format: month_format, tp: gmtime (timer: &tmp_time)); |
714 | } |
715 | } |
716 | else |
717 | strftime (s: buffer, maxsize: sizeof (buffer), format: month_format, tp: gmtime (timer: &tmp_time)); |
718 | |
719 | default_monthname[i] = g_locale_to_utf8 (opsysstring: buffer, len: -1, NULL, NULL, NULL); |
720 | #else |
721 | if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SMONTHNAME1 + i, |
722 | wbuffer, G_N_ELEMENTS (wbuffer))) |
723 | default_monthname[i] = g_strdup_printf ("(%d)" , i); |
724 | else |
725 | default_monthname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL); |
726 | #endif |
727 | } |
728 | |
729 | for (i = 0; i < 12; i ++) |
730 | { |
731 | GtkWidget *month_label = gtk_label_new (str: default_monthname[i]); |
732 | |
733 | gtk_stack_add_named (GTK_STACK (calendar->month_name_stack), child: month_label, name: default_monthname[i]); |
734 | } |
735 | |
736 | calendar->grid = gtk_grid_new (); |
737 | gtk_grid_set_row_homogeneous (GTK_GRID (calendar->grid), TRUE); |
738 | gtk_grid_set_column_homogeneous (GTK_GRID (calendar->grid), TRUE); |
739 | /* Day name labels */ |
740 | for (i = 0; i < 7; i ++) |
741 | { |
742 | int day; |
743 | GtkWidget *label; |
744 | |
745 | day = (i + calendar->week_start) % 7; |
746 | label = gtk_label_new (str: default_abbreviated_dayname[day]); |
747 | |
748 | gtk_widget_set_hexpand (widget: label, TRUE); |
749 | gtk_widget_set_vexpand (widget: label, TRUE); |
750 | gtk_widget_add_css_class (widget: label, css_class: "day-name" ); |
751 | gtk_grid_attach (GTK_GRID (calendar->grid), child: label, column: 1 + i, row: 0, width: 1, height: 1); |
752 | |
753 | calendar->day_name_labels[i] = label; |
754 | } |
755 | |
756 | /* Week number labels */ |
757 | for (i = 0; i < 6; i ++) |
758 | { |
759 | GtkWidget *label = gtk_label_new (str: "" ); |
760 | |
761 | gtk_widget_set_hexpand (widget: label, TRUE); |
762 | gtk_widget_set_vexpand (widget: label, TRUE); |
763 | gtk_widget_add_css_class (widget: label, css_class: "week-number" ); |
764 | gtk_grid_attach (GTK_GRID (calendar->grid), child: label, column: 0, row: 1 + i, width: 1, height: 1); |
765 | |
766 | calendar->week_number_labels[i] = label; |
767 | gtk_widget_hide (widget: label); |
768 | } |
769 | |
770 | { |
771 | int x, y; |
772 | |
773 | for (y = 0; y < 6; y ++) |
774 | for (x = 0; x < 7; x ++) |
775 | { |
776 | GtkWidget *label = gtk_label_new (str: "" ); |
777 | |
778 | gtk_widget_set_hexpand (widget: label, TRUE); |
779 | gtk_widget_set_vexpand (widget: label, TRUE); |
780 | gtk_widget_add_css_class (widget: label, css_class: "day-number" ); |
781 | gtk_grid_attach (GTK_GRID (calendar->grid), child: label, column: 1 + x, row: 1 + y, width: 1, height: 1); |
782 | |
783 | calendar->day_number_labels[y][x] = label; |
784 | } |
785 | } |
786 | |
787 | gtk_widget_set_hexpand (widget: calendar->grid, TRUE); |
788 | gtk_widget_set_vexpand (widget: calendar->grid, TRUE); |
789 | gtk_widget_set_parent (widget: calendar->grid, GTK_WIDGET (calendar)); |
790 | |
791 | for (i=0;i<31;i++) |
792 | calendar->marked_date[i] = FALSE; |
793 | calendar->num_marked_dates = 0; |
794 | |
795 | calendar->show_heading = TRUE; |
796 | calendar->show_day_names = TRUE; |
797 | |
798 | calendar->focus_row = -1; |
799 | calendar->focus_col = -1; |
800 | |
801 | target = gtk_drop_target_new (G_TYPE_STRING, actions: GDK_ACTION_COPY); |
802 | gtk_drop_target_set_preload (self: target, TRUE); |
803 | g_signal_connect (target, "notify::value" , G_CALLBACK (gtk_calendar_drag_notify_value), calendar); |
804 | g_signal_connect (target, "drop" , G_CALLBACK (gtk_calendar_drag_drop), calendar); |
805 | gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (target)); |
806 | |
807 | calendar->year_before = 0; |
808 | |
809 | /* Translate to calendar:YM if you want years to be displayed |
810 | * before months; otherwise translate to calendar:MY. |
811 | * Do *not* translate it to anything else, if it |
812 | * it isn't calendar:YM or calendar:MY it will not work. |
813 | * |
814 | * Note that the ordering described here is logical order, which is |
815 | * further influenced by BIDI ordering. Thus, if you have a default |
816 | * text direction of RTL and specify "calendar:YM", then the year |
817 | * will appear to the right of the month. |
818 | */ |
819 | year_before = _("calendar:MY" ); |
820 | if (strcmp (s1: year_before, s2: "calendar:YM" ) == 0) |
821 | calendar->year_before = 1; |
822 | else if (strcmp (s1: year_before, s2: "calendar:MY" ) != 0) |
823 | g_warning ("Whoever translated calendar:MY did so wrongly." ); |
824 | |
825 | gtk_orientable_set_orientation (GTK_ORIENTABLE (gtk_widget_get_layout_manager (GTK_WIDGET (calendar))), |
826 | orientation: GTK_ORIENTATION_VERTICAL); |
827 | |
828 | /* Select current day */ |
829 | calendar->date = g_date_time_new_from_unix_local (t: 0); |
830 | now = g_date_time_new_now_local (); |
831 | gtk_calendar_select_day (self: calendar, date: now); |
832 | g_date_time_unref (datetime: now); |
833 | |
834 | /* We just initialized the year label, now add some space to it so |
835 | * changing the year does not increase the calendar width */ |
836 | gtk_widget_measure (widget: calendar->year_label, orientation: GTK_ORIENTATION_HORIZONTAL, for_size: -1, |
837 | minimum: &min_year_width, NULL, NULL, NULL); |
838 | gtk_widget_set_size_request (widget: calendar->year_label, width: min_year_width + 10, height: -1); |
839 | } |
840 | |
841 | #pragma GCC diagnostic pop |
842 | |
843 | static void |
844 | calendar_queue_refresh (GtkCalendar *calendar) |
845 | { |
846 | gtk_widget_queue_resize (GTK_WIDGET (calendar)); |
847 | } |
848 | |
849 | static void |
850 | calendar_set_month_prev (GtkCalendar *calendar) |
851 | { |
852 | GDateTime *new_date; |
853 | |
854 | new_date = g_date_time_add_months (datetime: calendar->date, months: -1); |
855 | |
856 | gtk_calendar_select_day (self: calendar, date: new_date); |
857 | g_date_time_unref (datetime: new_date); |
858 | |
859 | g_signal_emit (instance: calendar, signal_id: gtk_calendar_signals[PREV_MONTH_SIGNAL], detail: 0); |
860 | } |
861 | |
862 | static void |
863 | calendar_set_month_next (GtkCalendar *calendar) |
864 | { |
865 | GDateTime *new_date; |
866 | |
867 | new_date = g_date_time_add_months (datetime: calendar->date, months: 1); |
868 | |
869 | gtk_calendar_select_day (self: calendar, date: new_date); |
870 | g_date_time_unref (datetime: new_date); |
871 | |
872 | g_signal_emit (instance: calendar, signal_id: gtk_calendar_signals[NEXT_MONTH_SIGNAL], detail: 0); |
873 | } |
874 | |
875 | static void |
876 | calendar_set_year_prev (GtkCalendar *calendar) |
877 | { |
878 | GDateTime *new_date; |
879 | |
880 | new_date = g_date_time_add_years (datetime: calendar->date, years: -1); |
881 | |
882 | gtk_calendar_select_day (self: calendar, date: new_date); |
883 | g_date_time_unref (datetime: new_date); |
884 | |
885 | g_signal_emit (instance: calendar, signal_id: gtk_calendar_signals[PREV_YEAR_SIGNAL], detail: 0); |
886 | } |
887 | |
888 | static void |
889 | calendar_set_year_next (GtkCalendar *calendar) |
890 | { |
891 | GDateTime *new_date; |
892 | |
893 | new_date = g_date_time_add_years (datetime: calendar->date, years: 1); |
894 | |
895 | gtk_calendar_select_day (self: calendar, date: new_date); |
896 | g_date_time_unref (datetime: new_date); |
897 | |
898 | g_signal_emit (instance: calendar, signal_id: gtk_calendar_signals[NEXT_YEAR_SIGNAL], detail: 0); |
899 | } |
900 | |
901 | static void |
902 | calendar_compute_days (GtkCalendar *calendar) |
903 | { |
904 | const int month = g_date_time_get_month (datetime: calendar->date); |
905 | const int year = g_date_time_get_year (datetime: calendar->date); |
906 | int ndays_in_month; |
907 | int ndays_in_prev_month; |
908 | int first_day; |
909 | int row; |
910 | int col; |
911 | int day; |
912 | |
913 | ndays_in_month = month_length[leap (year)][month]; |
914 | |
915 | first_day = day_of_week (year, mm: month, dd: 1); |
916 | first_day = (first_day + 7 - calendar->week_start) % 7; |
917 | if (first_day == 0) |
918 | first_day = 7; |
919 | |
920 | /* Compute days of previous month */ |
921 | if (month > 1) |
922 | ndays_in_prev_month = month_length[leap (year)][month - 1]; |
923 | else |
924 | ndays_in_prev_month = month_length[leap (year: year - 1)][12]; |
925 | day = ndays_in_prev_month - first_day+ 1; |
926 | |
927 | for (col = 0; col < first_day; col++) |
928 | { |
929 | calendar->day[0][col] = day; |
930 | calendar->day_month[0][col] = MONTH_PREV; |
931 | day++; |
932 | } |
933 | |
934 | /* Compute days of current month */ |
935 | row = first_day / 7; |
936 | col = first_day % 7; |
937 | for (day = 1; day <= ndays_in_month; day++) |
938 | { |
939 | calendar->day[row][col] = day; |
940 | calendar->day_month[row][col] = MONTH_CURRENT; |
941 | |
942 | col++; |
943 | if (col == 7) |
944 | { |
945 | row++; |
946 | col = 0; |
947 | } |
948 | } |
949 | |
950 | /* Compute days of next month */ |
951 | day = 1; |
952 | for (; row <= 5; row++) |
953 | { |
954 | for (; col <= 6; col++) |
955 | { |
956 | calendar->day[row][col] = day; |
957 | calendar->day_month[row][col] = MONTH_NEXT; |
958 | day++; |
959 | } |
960 | col = 0; |
961 | } |
962 | } |
963 | |
964 | static void |
965 | calendar_select_and_focus_day (GtkCalendar *calendar, |
966 | int day) |
967 | { |
968 | GDateTime *new_date; |
969 | int row; |
970 | int col; |
971 | |
972 | for (row = 0; row < 6; row ++) |
973 | for (col = 0; col < 7; col++) |
974 | { |
975 | if (calendar->day_month[row][col] == MONTH_CURRENT && |
976 | calendar->day[row][col] == day) |
977 | { |
978 | calendar->focus_row = row; |
979 | calendar->focus_col = col; |
980 | break; |
981 | } |
982 | } |
983 | |
984 | new_date = g_date_time_new_local (year: g_date_time_get_year (datetime: calendar->date), |
985 | month: g_date_time_get_month (datetime: calendar->date), |
986 | day, |
987 | hour: 0, minute: 0, seconds: 0); |
988 | |
989 | gtk_calendar_select_day (self: calendar, date: new_date); |
990 | g_date_time_unref (datetime: new_date); |
991 | } |
992 | |
993 | static void |
994 | gtk_calendar_set_property (GObject *object, |
995 | guint prop_id, |
996 | const GValue *value, |
997 | GParamSpec *pspec) |
998 | { |
999 | GtkCalendar *calendar = GTK_CALENDAR (object); |
1000 | GDateTime *date; |
1001 | |
1002 | switch (prop_id) |
1003 | { |
1004 | case PROP_YEAR: |
1005 | date = g_date_time_new_local (year: g_value_get_int (value), |
1006 | month: g_date_time_get_month (datetime: calendar->date), |
1007 | day: g_date_time_get_day_of_month (datetime: calendar->date), |
1008 | hour: 0, minute: 0, seconds: 0); |
1009 | if (date) |
1010 | { |
1011 | gtk_calendar_select_day (self: calendar, date); |
1012 | g_date_time_unref (datetime: date); |
1013 | } |
1014 | break; |
1015 | case PROP_MONTH: |
1016 | date = g_date_time_new_local (year: g_date_time_get_year (datetime: calendar->date), |
1017 | month: g_value_get_int (value) + 1, |
1018 | day: g_date_time_get_day_of_month (datetime: calendar->date), |
1019 | hour: 0, minute: 0, seconds: 0); |
1020 | if (date) |
1021 | { |
1022 | gtk_calendar_select_day (self: calendar, date); |
1023 | g_date_time_unref (datetime: date); |
1024 | } |
1025 | break; |
1026 | case PROP_DAY: |
1027 | date = g_date_time_new_local (year: g_date_time_get_year (datetime: calendar->date), |
1028 | month: g_date_time_get_month (datetime: calendar->date), |
1029 | day: g_value_get_int (value), |
1030 | hour: 0, minute: 0, seconds: 0); |
1031 | if (date) |
1032 | { |
1033 | gtk_calendar_select_day (self: calendar, date); |
1034 | g_date_time_unref (datetime: date); |
1035 | } |
1036 | break; |
1037 | case PROP_SHOW_HEADING: |
1038 | gtk_calendar_set_show_heading (self: calendar, value: g_value_get_boolean (value)); |
1039 | break; |
1040 | case PROP_SHOW_DAY_NAMES: |
1041 | gtk_calendar_set_show_day_names (self: calendar, value: g_value_get_boolean (value)); |
1042 | break; |
1043 | case PROP_SHOW_WEEK_NUMBERS: |
1044 | gtk_calendar_set_show_week_numbers (self: calendar, value: g_value_get_boolean (value)); |
1045 | break; |
1046 | default: |
1047 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
1048 | break; |
1049 | } |
1050 | } |
1051 | |
1052 | static void |
1053 | gtk_calendar_get_property (GObject *object, |
1054 | guint prop_id, |
1055 | GValue *value, |
1056 | GParamSpec *pspec) |
1057 | { |
1058 | GtkCalendar *calendar = GTK_CALENDAR (object); |
1059 | |
1060 | switch (prop_id) |
1061 | { |
1062 | case PROP_YEAR: |
1063 | g_value_set_int (value, v_int: g_date_time_get_year (datetime: calendar->date)); |
1064 | break; |
1065 | case PROP_MONTH: |
1066 | g_value_set_int (value, v_int: g_date_time_get_month (datetime: calendar->date) - 1); |
1067 | break; |
1068 | case PROP_DAY: |
1069 | g_value_set_int (value, v_int: g_date_time_get_day_of_month (datetime: calendar->date)); |
1070 | break; |
1071 | case PROP_SHOW_HEADING: |
1072 | g_value_set_boolean (value, v_boolean: gtk_calendar_get_show_heading (self: calendar)); |
1073 | break; |
1074 | case PROP_SHOW_DAY_NAMES: |
1075 | g_value_set_boolean (value, v_boolean: gtk_calendar_get_show_day_names (self: calendar)); |
1076 | break; |
1077 | case PROP_SHOW_WEEK_NUMBERS: |
1078 | g_value_set_boolean (value, v_boolean: gtk_calendar_get_show_week_numbers (self: calendar)); |
1079 | break; |
1080 | default: |
1081 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
1082 | break; |
1083 | } |
1084 | } |
1085 | |
1086 | static void |
1087 | calendar_invalidate_day_num (GtkCalendar *calendar, |
1088 | int day) |
1089 | { |
1090 | gtk_widget_queue_draw (GTK_WIDGET (calendar)); |
1091 | } |
1092 | |
1093 | static void |
1094 | calendar_invalidate_day (GtkCalendar *calendar, |
1095 | int row, |
1096 | int col) |
1097 | { |
1098 | gtk_widget_queue_draw (GTK_WIDGET (calendar)); |
1099 | } |
1100 | |
1101 | static void |
1102 | gtk_calendar_button_press (GtkGestureClick *gesture, |
1103 | int n_press, |
1104 | double x, |
1105 | double y, |
1106 | gpointer user_data) |
1107 | { |
1108 | GtkCalendar *calendar = user_data; |
1109 | GtkWidget *widget = GTK_WIDGET (calendar); |
1110 | GtkWidget *label; |
1111 | int row = -1, col = -1; |
1112 | int ix, iy; |
1113 | int day_month; |
1114 | int day; |
1115 | |
1116 | label = gtk_widget_pick (widget, x, y, flags: GTK_PICK_DEFAULT); |
1117 | for (iy = 0; iy < 6; iy ++) |
1118 | for (ix = 0; ix < 7; ix ++) |
1119 | { |
1120 | if (label == calendar->day_number_labels[iy][ix]) |
1121 | { |
1122 | row = iy; |
1123 | col = ix; |
1124 | } |
1125 | } |
1126 | |
1127 | /* If row or column isn't found, just return. */ |
1128 | if (row == -1 || col == -1) |
1129 | return; |
1130 | |
1131 | day_month = calendar->day_month[row][col]; |
1132 | day = calendar->day[row][col]; |
1133 | |
1134 | if (day_month == MONTH_PREV) |
1135 | calendar_set_month_prev (calendar); |
1136 | else if (day_month == MONTH_NEXT) |
1137 | calendar_set_month_next (calendar); |
1138 | |
1139 | if (!gtk_widget_has_focus (widget)) |
1140 | gtk_widget_grab_focus (widget); |
1141 | |
1142 | calendar_select_and_focus_day (calendar, day); |
1143 | } |
1144 | |
1145 | static gboolean |
1146 | gtk_calendar_scroll_controller_scroll (GtkEventControllerScroll *scroll, |
1147 | double dx, |
1148 | double dy, |
1149 | GtkWidget *widget) |
1150 | { |
1151 | GtkCalendar *calendar = GTK_CALENDAR (widget); |
1152 | |
1153 | if (!gtk_widget_has_focus (widget)) |
1154 | gtk_widget_grab_focus (widget); |
1155 | |
1156 | if (dy < 0) |
1157 | calendar_set_month_prev (calendar); |
1158 | else if (dy > 0) |
1159 | calendar_set_month_next (calendar); |
1160 | |
1161 | return GDK_EVENT_STOP; |
1162 | } |
1163 | |
1164 | |
1165 | /**************************************** |
1166 | * Key handling * |
1167 | ****************************************/ |
1168 | |
1169 | static void |
1170 | move_focus (GtkCalendar *calendar, |
1171 | int direction, |
1172 | int updown) |
1173 | { |
1174 | GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar)); |
1175 | int x, y; |
1176 | |
1177 | if (updown == 1) |
1178 | { |
1179 | if (calendar->focus_row > 0) |
1180 | calendar->focus_row--; |
1181 | if (calendar->focus_row < 0) |
1182 | calendar->focus_row = 5; |
1183 | if (calendar->focus_col < 0) |
1184 | calendar->focus_col = 6; |
1185 | } |
1186 | else if (updown == -1) |
1187 | { |
1188 | if (calendar->focus_row < 5) |
1189 | calendar->focus_row++; |
1190 | if (calendar->focus_col < 0) |
1191 | calendar->focus_col = 0; |
1192 | } |
1193 | else if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) || |
1194 | (text_dir == GTK_TEXT_DIR_RTL && direction == 1)) |
1195 | { |
1196 | if (calendar->focus_col > 0) |
1197 | calendar->focus_col--; |
1198 | else if (calendar->focus_row > 0) |
1199 | { |
1200 | calendar->focus_col = 6; |
1201 | calendar->focus_row--; |
1202 | } |
1203 | |
1204 | if (calendar->focus_col < 0) |
1205 | calendar->focus_col = 6; |
1206 | if (calendar->focus_row < 0) |
1207 | calendar->focus_row = 5; |
1208 | } |
1209 | else |
1210 | { |
1211 | if (calendar->focus_col < 6) |
1212 | calendar->focus_col++; |
1213 | else if (calendar->focus_row < 5) |
1214 | { |
1215 | calendar->focus_col = 0; |
1216 | calendar->focus_row++; |
1217 | } |
1218 | |
1219 | if (calendar->focus_col < 0) |
1220 | calendar->focus_col = 0; |
1221 | if (calendar->focus_row < 0) |
1222 | calendar->focus_row = 0; |
1223 | } |
1224 | |
1225 | for (y = 0; y < 6; y ++) |
1226 | for (x = 0; x < 7; x ++) |
1227 | { |
1228 | GtkWidget *label = calendar->day_number_labels[y][x]; |
1229 | |
1230 | if (calendar->focus_row == y && calendar->focus_col == x) |
1231 | gtk_widget_set_state_flags (widget: label, flags: GTK_STATE_FLAG_FOCUSED, FALSE); |
1232 | else |
1233 | gtk_widget_unset_state_flags (widget: label, flags: GTK_STATE_FLAG_FOCUSED); |
1234 | } |
1235 | } |
1236 | |
1237 | static gboolean |
1238 | gtk_calendar_key_controller_key_pressed (GtkEventControllerKey *controller, |
1239 | guint keyval, |
1240 | guint keycode, |
1241 | GdkModifierType state, |
1242 | GtkWidget *widget) |
1243 | { |
1244 | GtkCalendar *calendar = GTK_CALENDAR (widget); |
1245 | int return_val; |
1246 | int old_focus_row; |
1247 | int old_focus_col; |
1248 | int row, col, day; |
1249 | |
1250 | return_val = FALSE; |
1251 | |
1252 | old_focus_row = calendar->focus_row; |
1253 | old_focus_col = calendar->focus_col; |
1254 | |
1255 | switch (keyval) |
1256 | { |
1257 | case GDK_KEY_KP_Left: |
1258 | case GDK_KEY_Left: |
1259 | return_val = TRUE; |
1260 | if (state & GDK_CONTROL_MASK) |
1261 | calendar_set_month_prev (calendar); |
1262 | else |
1263 | { |
1264 | move_focus (calendar, direction: -1, updown: 0); |
1265 | calendar_invalidate_day (calendar, row: old_focus_row, col: old_focus_col); |
1266 | calendar_invalidate_day (calendar, row: calendar->focus_row, col: calendar->focus_col); |
1267 | } |
1268 | break; |
1269 | case GDK_KEY_KP_Right: |
1270 | case GDK_KEY_Right: |
1271 | return_val = TRUE; |
1272 | if (state & GDK_CONTROL_MASK) |
1273 | calendar_set_month_next (calendar); |
1274 | else |
1275 | { |
1276 | move_focus (calendar, direction: 1, updown: 0); |
1277 | calendar_invalidate_day (calendar, row: old_focus_row, col: old_focus_col); |
1278 | calendar_invalidate_day (calendar, row: calendar->focus_row, col: calendar->focus_col); |
1279 | } |
1280 | break; |
1281 | case GDK_KEY_KP_Up: |
1282 | case GDK_KEY_Up: |
1283 | return_val = TRUE; |
1284 | if (state & GDK_CONTROL_MASK) |
1285 | calendar_set_year_prev (calendar); |
1286 | else |
1287 | { |
1288 | move_focus (calendar, direction: 0, updown: 1); |
1289 | calendar_invalidate_day (calendar, row: old_focus_row, col: old_focus_col); |
1290 | calendar_invalidate_day (calendar, row: calendar->focus_row, col: calendar->focus_col); |
1291 | } |
1292 | break; |
1293 | case GDK_KEY_KP_Down: |
1294 | case GDK_KEY_Down: |
1295 | return_val = TRUE; |
1296 | if (state & GDK_CONTROL_MASK) |
1297 | calendar_set_year_next (calendar); |
1298 | else |
1299 | { |
1300 | move_focus (calendar, direction: 0, updown: -1); |
1301 | calendar_invalidate_day (calendar, row: old_focus_row, col: old_focus_col); |
1302 | calendar_invalidate_day (calendar, row: calendar->focus_row, col: calendar->focus_col); |
1303 | } |
1304 | break; |
1305 | case GDK_KEY_KP_Space: |
1306 | case GDK_KEY_space: |
1307 | row = calendar->focus_row; |
1308 | col = calendar->focus_col; |
1309 | |
1310 | if (row > -1 && col > -1) |
1311 | { |
1312 | return_val = TRUE; |
1313 | |
1314 | day = calendar->day[row][col]; |
1315 | if (calendar->day_month[row][col] == MONTH_PREV) |
1316 | calendar_set_month_prev (calendar); |
1317 | else if (calendar->day_month[row][col] == MONTH_NEXT) |
1318 | calendar_set_month_next (calendar); |
1319 | |
1320 | calendar_select_and_focus_day (calendar, day); |
1321 | } |
1322 | break; |
1323 | default: |
1324 | break; |
1325 | } |
1326 | |
1327 | return return_val; |
1328 | } |
1329 | |
1330 | static void |
1331 | gtk_calendar_focus_controller_focus (GtkEventController *controller, |
1332 | GtkWidget *widget) |
1333 | { |
1334 | GtkCalendar *calendar = GTK_CALENDAR (widget); |
1335 | |
1336 | calendar_queue_refresh (calendar); |
1337 | } |
1338 | |
1339 | |
1340 | /**************************************** |
1341 | * Public API * |
1342 | ****************************************/ |
1343 | |
1344 | /** |
1345 | * gtk_calendar_new: |
1346 | * |
1347 | * Creates a new calendar, with the current date being selected. |
1348 | * |
1349 | * Returns: a newly `GtkCalendar` widget |
1350 | */ |
1351 | GtkWidget* |
1352 | gtk_calendar_new (void) |
1353 | { |
1354 | return g_object_new (GTK_TYPE_CALENDAR, NULL); |
1355 | } |
1356 | |
1357 | /** |
1358 | * gtk_calendar_select_day: |
1359 | * @self: a `GtkCalendar`. |
1360 | * @date: (transfer none): a `GDateTime` representing the day to select |
1361 | * |
1362 | * Switches to @date's year and month and select its day. |
1363 | */ |
1364 | void |
1365 | gtk_calendar_select_day (GtkCalendar *calendar, |
1366 | GDateTime *date) |
1367 | { |
1368 | GDateTime *today; |
1369 | int new_day, new_month, new_year; |
1370 | gboolean day_changed, month_changed, year_changed; |
1371 | char buffer[255]; |
1372 | char *str; |
1373 | time_t tmp_time; |
1374 | struct tm *tm; |
1375 | int i; |
1376 | int x, y; |
1377 | int today_day; |
1378 | |
1379 | g_return_if_fail (GTK_IS_CALENDAR (calendar)); |
1380 | g_return_if_fail (date != NULL); |
1381 | |
1382 | day_changed = g_date_time_get_day_of_month (datetime: calendar->date) != g_date_time_get_day_of_month (datetime: date); |
1383 | month_changed = g_date_time_get_month (datetime: calendar->date) != g_date_time_get_month (datetime: date); |
1384 | year_changed = g_date_time_get_year (datetime: calendar->date) != g_date_time_get_year (datetime: date); |
1385 | |
1386 | if (!day_changed && !month_changed && !year_changed) |
1387 | return; |
1388 | |
1389 | new_year = g_date_time_get_year (datetime: date); |
1390 | new_month = g_date_time_get_month (datetime: date); |
1391 | new_day = g_date_time_get_day_of_month (datetime: date); |
1392 | |
1393 | g_date_time_unref (datetime: calendar->date); |
1394 | calendar->date = g_date_time_ref (datetime: date); |
1395 | |
1396 | tmp_time = 1; /* Jan 1 1970, 00:00:01 UTC */ |
1397 | tm = gmtime (timer: &tmp_time); |
1398 | tm->tm_year = new_year - 1900; |
1399 | |
1400 | /* Translators: This dictates how the year is displayed in |
1401 | * gtkcalendar widget. See strftime() manual for the format. |
1402 | * Use only ASCII in the translation. |
1403 | * |
1404 | * "%Y" is appropriate for most locales. |
1405 | */ |
1406 | strftime (s: buffer, maxsize: sizeof (buffer), C_("calendar year format" , "%Y" ), tp: tm); |
1407 | str = g_locale_to_utf8 (opsysstring: buffer, len: -1, NULL, NULL, NULL); |
1408 | gtk_label_set_label (GTK_LABEL (calendar->year_label), str); |
1409 | g_free (mem: str); |
1410 | |
1411 | /* Update month */ |
1412 | |
1413 | calendar_compute_days (calendar); |
1414 | gtk_stack_set_visible_child_name (GTK_STACK (calendar->month_name_stack), |
1415 | name: default_monthname[new_month - 1]); |
1416 | |
1417 | today = g_date_time_new_now_local (); |
1418 | |
1419 | if (g_date_time_get_year (datetime: calendar->date) == g_date_time_get_year (datetime: today) && |
1420 | g_date_time_get_month (datetime: calendar->date) == g_date_time_get_month (datetime: today)) |
1421 | today_day = g_date_time_get_day_of_month (datetime: today); |
1422 | else |
1423 | today_day = -1; |
1424 | |
1425 | g_date_time_unref (datetime: today); |
1426 | |
1427 | /* Update day labels */ |
1428 | for (y = 0; y < 6; y ++) |
1429 | for (x = 0; x < 7; x ++) |
1430 | { |
1431 | const int day = calendar->day[y][x]; |
1432 | GtkWidget *label = calendar->day_number_labels[y][x]; |
1433 | /* Translators: this defines whether the day numbers should use |
1434 | * localized digits or the ones used in English (0123...). |
1435 | * |
1436 | * Translate to "%Id" if you want to use localized digits, or |
1437 | * translate to "%d" otherwise. |
1438 | * |
1439 | * Note that translating this doesn't guarantee that you get localized |
1440 | * digits. That needs support from your system and locale definition |
1441 | * too. |
1442 | */ |
1443 | g_snprintf (string: buffer, n: sizeof (buffer), C_("calendar:day:digits" , "%d" ), day); |
1444 | |
1445 | gtk_label_set_label (GTK_LABEL (label), str: buffer); |
1446 | |
1447 | if (calendar->day_month[y][x] == MONTH_PREV || |
1448 | calendar->day_month[y][x] == MONTH_NEXT) |
1449 | gtk_widget_add_css_class (widget: label, css_class: "other-month" ); |
1450 | else |
1451 | gtk_widget_remove_css_class (widget: label, css_class: "other-month" ); |
1452 | |
1453 | if (calendar->marked_date[day-1]) |
1454 | gtk_widget_set_state_flags (widget: label, flags: GTK_STATE_FLAG_CHECKED, FALSE); |
1455 | else |
1456 | gtk_widget_unset_state_flags (widget: label, flags: GTK_STATE_FLAG_CHECKED); |
1457 | |
1458 | if (new_day == day && |
1459 | calendar->day_month[y][x] == MONTH_CURRENT) |
1460 | gtk_widget_set_state_flags (widget: label, flags: GTK_STATE_FLAG_SELECTED, FALSE); |
1461 | else |
1462 | gtk_widget_unset_state_flags (widget: label, flags: GTK_STATE_FLAG_SELECTED); |
1463 | |
1464 | if (calendar->focus_row == y && calendar->focus_col == x) |
1465 | gtk_widget_set_state_flags (widget: label, flags: GTK_STATE_FLAG_FOCUSED, FALSE); |
1466 | else |
1467 | gtk_widget_unset_state_flags (widget: label, flags: GTK_STATE_FLAG_FOCUSED); |
1468 | |
1469 | if (day == today_day && |
1470 | calendar->day_month[y][x] == MONTH_CURRENT) |
1471 | gtk_widget_add_css_class (widget: label, css_class: "today" ); |
1472 | else |
1473 | gtk_widget_remove_css_class (widget: label, css_class: "today" ); |
1474 | } |
1475 | |
1476 | /* Update week number labels. |
1477 | * We simply get the week number of calendar->date and add the others. |
1478 | * simple. */ |
1479 | for (i = 0; i < 6; i ++) |
1480 | { |
1481 | int year = new_year; |
1482 | int month, week; |
1483 | |
1484 | month = new_month + calendar->day_month[i][6] - MONTH_CURRENT; |
1485 | |
1486 | if (month < 1) |
1487 | { |
1488 | month += 12; |
1489 | year -= 1; |
1490 | } |
1491 | else if (month > 12) |
1492 | { |
1493 | month -= 12; |
1494 | year += 1; |
1495 | } |
1496 | |
1497 | week = week_of_year (year, mm: month, dd: calendar->day[i][6]); |
1498 | |
1499 | /* Translators: this defines whether the week numbers should use |
1500 | * localized digits or the ones used in English (0123...). |
1501 | * |
1502 | * Translate to "%Id" if you want to use localized digits, or |
1503 | * translate to "%d" otherwise. |
1504 | * Note that translating this doesn't guarantee that you get localized |
1505 | * digits. That needs support from your system and locale definition |
1506 | * too. */ |
1507 | g_snprintf (string: buffer, n: sizeof (buffer), C_("calendar:week:digits" , "%d" ), week); |
1508 | |
1509 | gtk_label_set_label (GTK_LABEL (calendar->week_number_labels[i]), str: buffer); |
1510 | } |
1511 | |
1512 | if (day_changed) |
1513 | { |
1514 | g_object_notify (G_OBJECT (calendar), property_name: "day" ); |
1515 | g_signal_emit (instance: calendar, signal_id: gtk_calendar_signals[DAY_SELECTED_SIGNAL], detail: 0); |
1516 | } |
1517 | |
1518 | if (month_changed) |
1519 | g_object_notify (G_OBJECT (calendar), property_name: "month" ); |
1520 | |
1521 | if (year_changed) |
1522 | g_object_notify (G_OBJECT (calendar), property_name: "year" ); |
1523 | |
1524 | } |
1525 | |
1526 | /** |
1527 | * gtk_calendar_clear_marks: |
1528 | * @calendar: a `GtkCalendar` |
1529 | * |
1530 | * Remove all visual markers. |
1531 | */ |
1532 | void |
1533 | gtk_calendar_clear_marks (GtkCalendar *calendar) |
1534 | { |
1535 | guint day; |
1536 | |
1537 | g_return_if_fail (GTK_IS_CALENDAR (calendar)); |
1538 | |
1539 | for (day = 0; day < 31; day++) |
1540 | { |
1541 | calendar->marked_date[day] = FALSE; |
1542 | } |
1543 | |
1544 | calendar->num_marked_dates = 0; |
1545 | calendar_queue_refresh (calendar); |
1546 | } |
1547 | |
1548 | /** |
1549 | * gtk_calendar_mark_day: |
1550 | * @calendar: a `GtkCalendar` |
1551 | * @day: the day number to mark between 1 and 31. |
1552 | * |
1553 | * Places a visual marker on a particular day. |
1554 | */ |
1555 | void |
1556 | gtk_calendar_mark_day (GtkCalendar *calendar, |
1557 | guint day) |
1558 | { |
1559 | g_return_if_fail (GTK_IS_CALENDAR (calendar)); |
1560 | |
1561 | if (day >= 1 && day <= 31 && !calendar->marked_date[day-1]) |
1562 | { |
1563 | calendar->marked_date[day - 1] = TRUE; |
1564 | calendar->num_marked_dates++; |
1565 | calendar_invalidate_day_num (calendar, day); |
1566 | } |
1567 | } |
1568 | |
1569 | /** |
1570 | * gtk_calendar_get_day_is_marked: |
1571 | * @calendar: a `GtkCalendar` |
1572 | * @day: the day number between 1 and 31. |
1573 | * |
1574 | * Returns if the @day of the @calendar is already marked. |
1575 | * |
1576 | * Returns: whether the day is marked. |
1577 | */ |
1578 | gboolean |
1579 | gtk_calendar_get_day_is_marked (GtkCalendar *calendar, |
1580 | guint day) |
1581 | { |
1582 | g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE); |
1583 | |
1584 | if (day >= 1 && day <= 31) |
1585 | return calendar->marked_date[day - 1]; |
1586 | |
1587 | return FALSE; |
1588 | } |
1589 | |
1590 | /** |
1591 | * gtk_calendar_unmark_day: |
1592 | * @calendar: a `GtkCalendar`. |
1593 | * @day: the day number to unmark between 1 and 31. |
1594 | * |
1595 | * Removes the visual marker from a particular day. |
1596 | */ |
1597 | void |
1598 | gtk_calendar_unmark_day (GtkCalendar *calendar, |
1599 | guint day) |
1600 | { |
1601 | g_return_if_fail (GTK_IS_CALENDAR (calendar)); |
1602 | |
1603 | if (day >= 1 && day <= 31 && calendar->marked_date[day-1]) |
1604 | { |
1605 | calendar->marked_date[day - 1] = FALSE; |
1606 | calendar->num_marked_dates--; |
1607 | calendar_invalidate_day_num (calendar, day); |
1608 | } |
1609 | } |
1610 | |
1611 | /** |
1612 | * gtk_calendar_get_date: |
1613 | * @self: a `GtkCalendar` |
1614 | * |
1615 | * Returns a `GDateTime` representing the shown |
1616 | * year, month and the selected day. |
1617 | * |
1618 | * The returned date is in the local time zone. |
1619 | * |
1620 | * Returns: (transfer full): the `GDate` representing the shown date |
1621 | */ |
1622 | GDateTime * |
1623 | gtk_calendar_get_date (GtkCalendar *self) |
1624 | { |
1625 | g_return_val_if_fail (GTK_IS_CALENDAR (self), NULL); |
1626 | |
1627 | return g_date_time_ref (datetime: self->date); |
1628 | } |
1629 | |
1630 | /** |
1631 | * gtk_calendar_set_show_week_numbers: (attributes org.gtk.Method.set_property=show-week-numbers) |
1632 | * @self: a `GtkCalendar` |
1633 | * @value: whether to show week numbers on the left of the days |
1634 | * |
1635 | * Sets whether week numbers are shown in the calendar. |
1636 | */ |
1637 | void |
1638 | gtk_calendar_set_show_week_numbers (GtkCalendar *self, |
1639 | gboolean value) |
1640 | { |
1641 | int i; |
1642 | |
1643 | g_return_if_fail (GTK_IS_CALENDAR (self)); |
1644 | |
1645 | if (self->show_week_numbers == value) |
1646 | return; |
1647 | |
1648 | self->show_week_numbers = value; |
1649 | |
1650 | for (i = 0; i < 6; i ++) |
1651 | gtk_widget_set_visible (widget: self->week_number_labels[i], visible: value); |
1652 | |
1653 | g_object_notify (G_OBJECT (self), property_name: "show-week-numbers" ); |
1654 | } |
1655 | |
1656 | /** |
1657 | * gtk_calendar_get_show_week_numbers: (attributes org.gtk.Method.get_property=show-week-numbers) |
1658 | * @self: a `GtkCalendar` |
1659 | * |
1660 | * Returns whether @self is showing week numbers right |
1661 | * now. |
1662 | * |
1663 | * This is the value of the [property@Gtk.Calendar:show-week-numbers] |
1664 | * property. |
1665 | * |
1666 | * Return: Whether the calendar is showing week numbers. |
1667 | */ |
1668 | gboolean |
1669 | gtk_calendar_get_show_week_numbers (GtkCalendar *self) |
1670 | { |
1671 | g_return_val_if_fail (GTK_IS_CALENDAR (self), FALSE); |
1672 | |
1673 | return self->show_week_numbers; |
1674 | } |
1675 | |
1676 | /** |
1677 | * gtk_calendar_set_show_heading: (attributes org.gtk.Method.set_property=show-heading) |
1678 | * @self: a `GtkCalendar` |
1679 | * @value: Whether to show the heading in the calendar |
1680 | * |
1681 | * Sets whether the calendar should show a heading. |
1682 | * |
1683 | * The heading contains the current year and month as well as |
1684 | * buttons for changing both. |
1685 | */ |
1686 | void |
1687 | gtk_calendar_set_show_heading (GtkCalendar *self, |
1688 | gboolean value) |
1689 | { |
1690 | g_return_if_fail (GTK_IS_CALENDAR (self)); |
1691 | |
1692 | if (self->show_heading == value) |
1693 | return; |
1694 | |
1695 | self->show_heading = value; |
1696 | |
1697 | gtk_widget_set_visible (widget: self->header_box, visible: value); |
1698 | |
1699 | g_object_notify (G_OBJECT (self), property_name: "show-heading" ); |
1700 | } |
1701 | |
1702 | /** |
1703 | * gtk_calendar_get_show_heading: (attributes org.gtk.Method.get_property=show-heading) |
1704 | * @self: a `GtkCalendar` |
1705 | * |
1706 | * Returns whether @self is currently showing the heading. |
1707 | * |
1708 | * This is the value of the [property@Gtk.Calendar:show-heading] |
1709 | * property. |
1710 | * |
1711 | * Return: Whether the calendar is showing a heading. |
1712 | */ |
1713 | gboolean |
1714 | gtk_calendar_get_show_heading (GtkCalendar *self) |
1715 | { |
1716 | g_return_val_if_fail (GTK_IS_CALENDAR (self), FALSE); |
1717 | |
1718 | return self->show_heading; |
1719 | } |
1720 | |
1721 | /** |
1722 | * gtk_calendar_set_show_day_names: (attributes org.gtk.Method.set_property=show-day-names) |
1723 | * @self: a `GtkCalendar` |
1724 | * @value: Whether to show day names above the day numbers |
1725 | * |
1726 | * Sets whether the calendar shows day names. |
1727 | */ |
1728 | void |
1729 | gtk_calendar_set_show_day_names (GtkCalendar *self, |
1730 | gboolean value) |
1731 | { |
1732 | int i; |
1733 | |
1734 | g_return_if_fail (GTK_IS_CALENDAR (self)); |
1735 | |
1736 | if (self->show_day_names == value) |
1737 | return; |
1738 | |
1739 | self->show_day_names = value; |
1740 | |
1741 | for (i = 0; i < 7; i ++) |
1742 | gtk_widget_set_visible (widget: self->day_name_labels[i], visible: value); |
1743 | |
1744 | g_object_notify (G_OBJECT (self), property_name: "show-day-names" ); |
1745 | } |
1746 | |
1747 | /** |
1748 | * gtk_calendar_get_show_day_names: (attributes org.gtk.Method.get_property=show-day-names) |
1749 | * @self: a `GtkCalendar` |
1750 | * |
1751 | * Returns whether @self is currently showing the names |
1752 | * of the week days. |
1753 | * |
1754 | * This is the value of the [property@Gtk.Calendar:show-day-names] |
1755 | * property. |
1756 | * |
1757 | * Returns: Whether the calendar shows day names. |
1758 | */ |
1759 | gboolean |
1760 | gtk_calendar_get_show_day_names (GtkCalendar *self) |
1761 | { |
1762 | g_return_val_if_fail (GTK_IS_CALENDAR (self), FALSE); |
1763 | |
1764 | return self->show_day_names; |
1765 | } |
1766 | |