1
/* This is copied from GTK+ 2.15/16 so we can use new API without waiting
2
* for stable release of GTK+ 2.16. Life would be easier if GTK+ followed
3
* the GNOME release schedule.
6
#if !GTK_CHECK_VERSION(2,16,0)
10
#ifdef HAVE_SYS_TIME_H
13
#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
26
#undef GTK_DISABLE_DEPRECATED
27
#include "glomgtkcalendar.h"
29
#include <gtk/gtkdnd.h>
31
#include <gtk/gtkmain.h>
32
#include <gdk/gdkkeysyms.h>
35
/* #include "gtkintl.h" */
36
#include <glib/gi18n-lib.h>
39
#define P_(String) dgettext(GETTEXT_PACKAGE "-properties",String)
41
#define P_(String) (String)
44
/* not really I18N-related, but also a string marker macro */
45
#define I_(string) g_intern_static_string (string)
49
/* #include "gtkmarshalers.h" */
50
#define _gtk_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID
53
/* #include "gtkprivate.h" */
54
#define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
55
#define GTK_PARAM_WRITABLE G_PARAM_WRITABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
56
#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
58
/* GTK - The GIMP Toolkit
59
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
62
* Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson and Mattias Groenlund
65
* Copyright (c) 1995, 1996, 1997, 1998 by Steffen Beyer
67
* This library is free software; you can redistribute it and/or
68
* modify it under the terms of the GNU Lesser General Public
69
* License as published by the Free Software Foundation; either
70
* version 2 of the License, or (at your option) any later version.
72
* This library is distributed in the hope that it will be useful,
73
* but WITHOUT ANY WARRANTY; without even the implied warranty of
74
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
75
* Lesser General Public License for more details.
77
* You should have received a copy of the GNU Lesser General Public
78
* License along with this library; if not, write to the Free
79
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
83
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
84
* file for a list of people on the GTK+ Team. See the ChangeLog
85
* files for a list of changes. These files are distributed with
86
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
91
#ifdef HAVE_SYS_TIME_H
94
#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
107
#undef GTK_DISABLE_DEPRECATED
108
#include "glomgtkcalendar.h"
110
#include <gtk/gtktooltip.h>
112
/***************************************************************************/
113
/* The following date routines are taken from the lib_date package.
114
* They have been minimally edited to avoid conflict with types defined
118
static const guint month_length[2][13] =
120
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
121
{ 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
124
static const guint days_in_months[2][14] =
126
{ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
127
{ 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
130
static glong calc_days(guint year, guint mm, guint dd);
131
static guint day_of_week(guint year, guint mm, guint dd);
132
static glong dates_difference(guint year1, guint mm1, guint dd1,
133
guint year2, guint mm2, guint dd2);
134
static guint weeks_in_year(guint year);
139
return((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0));
143
day_of_week (guint year, guint mm, guint dd)
147
days = calc_days(year, mm, dd);
154
return( (guint) days );
157
static guint weeks_in_year(guint year)
159
return(52 + ((day_of_week(year,1,1)==4) || (day_of_week(year,12,31)==4)));
163
check_date(guint year, guint mm, guint dd)
165
if (year < 1) return FALSE;
166
if ((mm < 1) || (mm > 12)) return FALSE;
167
if ((dd < 1) || (dd > month_length[leap(year)][mm])) return FALSE;
172
week_number(guint year, guint mm, guint dd)
176
first = day_of_week(year,1,1) - 1;
177
return( (guint) ( (dates_difference(year,1,1, year,mm,dd) + first) / 7L ) +
182
year_to_days(guint year)
184
return( year * 365L + (year / 4) - (year / 100) + (year / 400) );
189
calc_days(guint year, guint mm, guint dd)
193
if (year < 1) return(0L);
194
if ((mm < 1) || (mm > 12)) return(0L);
195
if ((dd < 1) || (dd > month_length[(lp = leap(year))][mm])) return(0L);
196
return( year_to_days(--year) + days_in_months[lp][mm] + dd );
200
week_of_year(guint *week, guint *year, guint mm, guint dd)
202
if (check_date(*year,mm,dd))
204
*week = week_number(*year,mm,dd);
206
*week = weeks_in_year(--(*year));
207
else if (*week > weeks_in_year(*year))
218
dates_difference(guint year1, guint mm1, guint dd1,
219
guint year2, guint mm2, guint dd2)
221
return( calc_days(year2, mm2, dd2) - calc_days(year1, mm1, dd1) );
224
/*** END OF lib_date routines ********************************************/
226
/* Spacing around day/week headers and main area, inside those windows */
227
#define CALENDAR_MARGIN 0
228
/* Spacing around day/week headers and main area, outside those windows */
229
#define INNER_BORDER 4
230
/* Separation between day headers and main area */
231
#define CALENDAR_YSEP 4
232
/* Separation between week headers and main area */
233
#define CALENDAR_XSEP 4
235
#define DAY_XSEP 0 /* not really good for small calendar */
236
#define DAY_YSEP 0 /* not really good for small calendar */
238
#define SCROLL_DELAY_FACTOR 5
241
#define HEADER_FG_COLOR(widget) (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
242
#define HEADER_BG_COLOR(widget) (& (widget)->style->bg[GTK_WIDGET_STATE (widget)])
243
#define SELECTED_BG_COLOR(widget) (& (widget)->style->base[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
244
#define SELECTED_FG_COLOR(widget) (& (widget)->style->text[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
245
#define NORMAL_DAY_COLOR(widget) (& (widget)->style->text[GTK_WIDGET_STATE (widget)])
246
#define PREV_MONTH_COLOR(widget) (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
247
#define NEXT_MONTH_COLOR(widget) (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
248
#define MARKED_COLOR(widget) (& (widget)->style->text[GTK_WIDGET_STATE (widget)])
249
#define BACKGROUND_COLOR(widget) (& (widget)->style->base[GTK_WIDGET_STATE (widget)])
250
#define HIGHLIGHT_BACK_COLOR(widget) (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
266
MONTH_CHANGED_SIGNAL,
268
DAY_SELECTED_DOUBLE_CLICK_SIGNAL,
284
PROP_NO_MONTH_CHANGE,
285
PROP_SHOW_WEEK_NUMBERS,
287
PROP_DETAIL_WIDTH_CHARS,
288
PROP_DETAIL_HEIGHT_ROWS,
292
static guint glom_gtk_calendar_signals[LAST_SIGNAL] = { 0 };
294
struct _GlomGtkCalendarPrivate
296
GdkWindow *header_win;
297
GdkWindow *day_name_win;
300
GdkWindow *arrow_win[4];
306
guint arrow_state[4];
308
guint max_month_width;
309
guint max_year_width;
315
guint max_day_char_width;
316
guint max_day_char_ascent;
317
guint max_day_char_descent;
318
guint max_label_char_ascent;
319
guint max_label_char_descent;
320
guint max_week_char_width;
323
guint year_before : 1;
325
guint need_timer : 1;
328
guint drag_highlight : 1;
338
/* Optional callback, used to display extra information for each day. */
339
GlomGtkCalendarDetailFunc detail_func;
340
gpointer detail_func_user_data;
341
GDestroyNotify detail_func_destroy;
343
/* Size requistion for details provided by the hook. */
344
gint detail_height_rows;
345
gint detail_width_chars;
346
gint detail_overflow[6];
349
#define GLOM_GTK_CALENDAR_GET_PRIVATE(widget) (GLOM_GTK_CALENDAR (widget)->priv)
351
static void glom_gtk_calendar_finalize (GObject *calendar);
352
static void glom_gtk_calendar_destroy (GtkObject *calendar);
353
static void glom_gtk_calendar_set_property (GObject *object,
357
static void glom_gtk_calendar_get_property (GObject *object,
362
static void glom_gtk_calendar_realize (GtkWidget *widget);
363
static void glom_gtk_calendar_unrealize (GtkWidget *widget);
364
static void glom_gtk_calendar_size_request (GtkWidget *widget,
365
GtkRequisition *requisition);
366
static void glom_gtk_calendar_size_allocate (GtkWidget *widget,
367
GtkAllocation *allocation);
368
static gboolean glom_gtk_calendar_expose (GtkWidget *widget,
369
GdkEventExpose *event);
370
static gboolean glom_gtk_calendar_button_press (GtkWidget *widget,
371
GdkEventButton *event);
372
static gboolean glom_gtk_calendar_button_release (GtkWidget *widget,
373
GdkEventButton *event);
374
static gboolean glom_gtk_calendar_motion_notify (GtkWidget *widget,
375
GdkEventMotion *event);
376
static gboolean glom_gtk_calendar_enter_notify (GtkWidget *widget,
377
GdkEventCrossing *event);
378
static gboolean glom_gtk_calendar_leave_notify (GtkWidget *widget,
379
GdkEventCrossing *event);
380
static gboolean glom_gtk_calendar_scroll (GtkWidget *widget,
381
GdkEventScroll *event);
382
static gboolean glom_gtk_calendar_key_press (GtkWidget *widget,
384
static gboolean glom_gtk_calendar_focus_out (GtkWidget *widget,
385
GdkEventFocus *event);
386
static void glom_gtk_calendar_grab_notify (GtkWidget *widget,
387
gboolean was_grabbed);
388
static void glom_gtk_calendar_state_changed (GtkWidget *widget,
389
GtkStateType previous_state);
390
static void glom_gtk_calendar_style_set (GtkWidget *widget,
391
GtkStyle *previous_style);
392
static gboolean glom_gtk_calendar_query_tooltip (GtkWidget *widget,
395
gboolean keyboard_mode,
396
GtkTooltip *tooltip);
398
static void glom_gtk_calendar_drag_data_get (GtkWidget *widget,
399
GdkDragContext *context,
400
GtkSelectionData *selection_data,
403
static void glom_gtk_calendar_drag_data_received (GtkWidget *widget,
404
GdkDragContext *context,
407
GtkSelectionData *selection_data,
410
static gboolean glom_gtk_calendar_drag_motion (GtkWidget *widget,
411
GdkDragContext *context,
415
static void glom_gtk_calendar_drag_leave (GtkWidget *widget,
416
GdkDragContext *context,
418
static gboolean glom_gtk_calendar_drag_drop (GtkWidget *widget,
419
GdkDragContext *context,
424
static void calendar_start_spinning (GlomGtkCalendar *calendar,
426
static void calendar_stop_spinning (GlomGtkCalendar *calendar);
428
static void calendar_invalidate_day (GlomGtkCalendar *widget,
431
static void calendar_invalidate_day_num (GlomGtkCalendar *widget,
433
static void calendar_invalidate_arrow (GlomGtkCalendar *widget,
436
static void calendar_compute_days (GlomGtkCalendar *calendar);
438
static char *default_abbreviated_dayname[7];
439
static char *default_monthname[12];
441
G_DEFINE_TYPE (GlomGtkCalendar, glom_gtk_calendar, GTK_TYPE_WIDGET)
444
glom_gtk_calendar_class_init (GlomGtkCalendarClass *class)
446
GObjectClass *gobject_class;
447
GtkObjectClass *object_class;
448
GtkWidgetClass *widget_class;
450
gobject_class = (GObjectClass*) class;
451
object_class = (GtkObjectClass*) class;
452
widget_class = (GtkWidgetClass*) class;
454
gobject_class->set_property = glom_gtk_calendar_set_property;
455
gobject_class->get_property = glom_gtk_calendar_get_property;
456
gobject_class->finalize = glom_gtk_calendar_finalize;
458
object_class->destroy = glom_gtk_calendar_destroy;
460
widget_class->realize = glom_gtk_calendar_realize;
461
widget_class->unrealize = glom_gtk_calendar_unrealize;
462
widget_class->expose_event = glom_gtk_calendar_expose;
463
widget_class->size_request = glom_gtk_calendar_size_request;
464
widget_class->size_allocate = glom_gtk_calendar_size_allocate;
465
widget_class->button_press_event = glom_gtk_calendar_button_press;
466
widget_class->button_release_event = glom_gtk_calendar_button_release;
467
widget_class->motion_notify_event = glom_gtk_calendar_motion_notify;
468
widget_class->enter_notify_event = glom_gtk_calendar_enter_notify;
469
widget_class->leave_notify_event = glom_gtk_calendar_leave_notify;
470
widget_class->key_press_event = glom_gtk_calendar_key_press;
471
widget_class->scroll_event = glom_gtk_calendar_scroll;
472
widget_class->style_set = glom_gtk_calendar_style_set;
473
widget_class->state_changed = glom_gtk_calendar_state_changed;
474
widget_class->grab_notify = glom_gtk_calendar_grab_notify;
475
widget_class->focus_out_event = glom_gtk_calendar_focus_out;
476
widget_class->query_tooltip = glom_gtk_calendar_query_tooltip;
478
widget_class->drag_data_get = glom_gtk_calendar_drag_data_get;
479
widget_class->drag_motion = glom_gtk_calendar_drag_motion;
480
widget_class->drag_leave = glom_gtk_calendar_drag_leave;
481
widget_class->drag_drop = glom_gtk_calendar_drag_drop;
482
widget_class->drag_data_received = glom_gtk_calendar_drag_data_received;
485
* GlomGtkCalendar:year:
488
* This property gets initially set to the current year.
490
g_object_class_install_property (gobject_class,
492
g_param_spec_int ("year",
494
P_("The selected year"),
496
GTK_PARAM_READWRITE));
499
* GlomGtkCalendar:month:
501
* The selected month (as a number between 0 and 11).
502
* This property gets initially set to the current month.
504
g_object_class_install_property (gobject_class,
506
g_param_spec_int ("month",
508
P_("The selected month (as a number between 0 and 11)"),
510
GTK_PARAM_READWRITE));
513
* GlomGtkCalendar:day:
515
* The selected day (as a number between 1 and 31, or 0
516
* to unselect the currently selected day).
517
* This property gets initially set to the current day.
519
g_object_class_install_property (gobject_class,
521
g_param_spec_int ("day",
523
P_("The selected day (as a number between 1 and 31, or 0 to unselect the currently selected day)"),
525
GTK_PARAM_READWRITE));
528
* GlomGtkCalendar:show-heading:
530
* Determines whether a heading is displayed.
534
g_object_class_install_property (gobject_class,
536
g_param_spec_boolean ("show-heading",
538
P_("If TRUE, a heading is displayed"),
540
GTK_PARAM_READWRITE));
543
* GlomGtkCalendar:show-day-names:
545
* Determines whether day names are displayed.
549
g_object_class_install_property (gobject_class,
551
g_param_spec_boolean ("show-day-names",
552
P_("Show Day Names"),
553
P_("If TRUE, day names are displayed"),
555
GTK_PARAM_READWRITE));
557
* GlomGtkCalendar:no-month-change:
559
* Determines whether the selected month can be changed.
563
g_object_class_install_property (gobject_class,
564
PROP_NO_MONTH_CHANGE,
565
g_param_spec_boolean ("no-month-change",
566
P_("No Month Change"),
567
P_("If TRUE, the selected month cannot be changed"),
569
GTK_PARAM_READWRITE));
572
* GlomGtkCalendar:show-week-numbers:
574
* Determines whether week numbers are displayed.
578
g_object_class_install_property (gobject_class,
579
PROP_SHOW_WEEK_NUMBERS,
580
g_param_spec_boolean ("show-week-numbers",
581
P_("Show Week Numbers"),
582
P_("If TRUE, week numbers are displayed"),
584
GTK_PARAM_READWRITE));
587
* GlomGtkCalendar:detail-width-chars:
589
* Width of a detail cell, in characters.
590
* A value of 0 allows any width. See glom_gtk_calendar_set_detail_func().
594
g_object_class_install_property (gobject_class,
595
PROP_DETAIL_WIDTH_CHARS,
596
g_param_spec_int ("detail-width-chars",
598
P_("Details width in characters"),
600
GTK_PARAM_READWRITE));
603
* GlomGtkCalendar:detail-height-rows:
605
* Height of a detail cell, in rows.
606
* A value of 0 allows any width. See glom_gtk_calendar_set_detail_func().
610
g_object_class_install_property (gobject_class,
611
PROP_DETAIL_HEIGHT_ROWS,
612
g_param_spec_int ("detail-height-rows",
613
P_("Details Height"),
614
P_("Details height in rows"),
616
GTK_PARAM_READWRITE));
619
* GlomGtkCalendar:show-details:
621
* Determines whether details are shown directly in the widget, or if they are
622
* available only as tooltip. When this property is set days with details are
627
g_object_class_install_property (gobject_class,
629
g_param_spec_boolean ("show-details",
631
P_("If TRUE, details are shown"),
633
GTK_PARAM_READWRITE));
635
glom_gtk_calendar_signals[MONTH_CHANGED_SIGNAL] =
636
g_signal_new (I_("month_changed"),
637
G_OBJECT_CLASS_TYPE (gobject_class),
639
G_STRUCT_OFFSET (GlomGtkCalendarClass, month_changed),
641
_gtk_marshal_VOID__VOID,
643
glom_gtk_calendar_signals[DAY_SELECTED_SIGNAL] =
644
g_signal_new (I_("day_selected"),
645
G_OBJECT_CLASS_TYPE (gobject_class),
647
G_STRUCT_OFFSET (GlomGtkCalendarClass, day_selected),
649
_gtk_marshal_VOID__VOID,
651
glom_gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL] =
652
g_signal_new (I_("day_selected_double_click"),
653
G_OBJECT_CLASS_TYPE (gobject_class),
655
G_STRUCT_OFFSET (GlomGtkCalendarClass, day_selected_double_click),
657
_gtk_marshal_VOID__VOID,
659
glom_gtk_calendar_signals[PREV_MONTH_SIGNAL] =
660
g_signal_new (I_("prev_month"),
661
G_OBJECT_CLASS_TYPE (gobject_class),
663
G_STRUCT_OFFSET (GlomGtkCalendarClass, prev_month),
665
_gtk_marshal_VOID__VOID,
667
glom_gtk_calendar_signals[NEXT_MONTH_SIGNAL] =
668
g_signal_new (I_("next_month"),
669
G_OBJECT_CLASS_TYPE (gobject_class),
671
G_STRUCT_OFFSET (GlomGtkCalendarClass, next_month),
673
_gtk_marshal_VOID__VOID,
675
glom_gtk_calendar_signals[PREV_YEAR_SIGNAL] =
676
g_signal_new (I_("prev_year"),
677
G_OBJECT_CLASS_TYPE (gobject_class),
679
G_STRUCT_OFFSET (GlomGtkCalendarClass, prev_year),
681
_gtk_marshal_VOID__VOID,
683
glom_gtk_calendar_signals[NEXT_YEAR_SIGNAL] =
684
g_signal_new (I_("next_year"),
685
G_OBJECT_CLASS_TYPE (gobject_class),
687
G_STRUCT_OFFSET (GlomGtkCalendarClass, next_year),
689
_gtk_marshal_VOID__VOID,
692
g_type_class_add_private (gobject_class, sizeof (GlomGtkCalendarPrivate));
696
glom_gtk_calendar_init (GlomGtkCalendar *calendar)
698
GtkWidget *widget = GTK_WIDGET (calendar);
703
wchar_t wbuffer[100];
708
GlomGtkCalendarPrivate *priv;
710
#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
711
union { unsigned int word; char *string; } langinfo;
712
gint week_1stday = 0;
713
gint first_weekday = 1;
719
priv = calendar->priv = G_TYPE_INSTANCE_GET_PRIVATE (calendar,
720
GLOM_GTK_TYPE_CALENDAR,
721
GlomGtkCalendarPrivate);
723
GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
725
if (!default_abbreviated_dayname[0])
729
tmp_time= (i+3)*86400;
730
strftime ( buffer, sizeof (buffer), "%a", gmtime (&tmp_time));
731
default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
733
if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SABBREVDAYNAME1 + (i+6)%7,
734
wbuffer, G_N_ELEMENTS (wbuffer)))
735
default_abbreviated_dayname[i] = g_strdup_printf ("(%d)", i);
737
default_abbreviated_dayname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
741
if (!default_monthname[0])
746
strftime ( buffer, sizeof (buffer), "%B", gmtime (&tmp_time));
747
default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
749
if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SMONTHNAME1 + i,
750
wbuffer, G_N_ELEMENTS (wbuffer)))
751
default_monthname[i] = g_strdup_printf ("(%d)", i);
753
default_monthname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
759
tm = localtime (&secs);
760
calendar->month = tm->tm_mon;
761
calendar->year = 1900 + tm->tm_year;
764
calendar->marked_date[i] = FALSE;
765
calendar->num_marked_dates = 0;
766
calendar->selected_day = tm->tm_mday;
768
calendar->display_flags = (GLOM_GTK_CALENDAR_SHOW_HEADING |
769
GLOM_GTK_CALENDAR_SHOW_DAY_NAMES |
770
GLOM_GTK_CALENDAR_SHOW_DETAILS);
772
calendar->highlight_row = -1;
773
calendar->highlight_col = -1;
775
calendar->focus_row = -1;
776
calendar->focus_col = -1;
778
priv->max_year_width = 0;
779
priv->max_month_width = 0;
780
priv->max_day_char_width = 0;
781
priv->max_week_char_width = 0;
783
priv->max_day_char_ascent = 0;
784
priv->max_day_char_descent = 0;
785
priv->max_label_char_ascent = 0;
786
priv->max_label_char_descent = 0;
788
priv->arrow_width = 10;
790
priv->need_timer = 0;
792
priv->click_child = -1;
795
priv->drag_highlight = 0;
797
gtk_drag_dest_set (widget, 0, NULL, 0, GDK_ACTION_COPY);
798
gtk_drag_dest_add_text_targets (widget);
800
priv->year_before = 0;
802
/* Translate to calendar:YM if you want years to be displayed
803
* before months; otherwise translate to calendar:MY.
804
* Do *not* translate it to anything else, if it
805
* it isn't calendar:YM or calendar:MY it will not work.
807
* Note that this flipping is in top of the text direction flipping,
808
* so if you have a default text direction of RTL and YM, then
809
* the year will appear on the right.
811
year_before = _("calendar:MY");
812
if (strcmp (year_before, "calendar:YM") == 0)
813
priv->year_before = 1;
814
else if (strcmp (year_before, "calendar:MY") != 0)
815
g_warning ("Whoever translated calendar:MY did so wrongly.\n");
818
priv->week_start = 0;
821
if (GetLocaleInfoW (GetThreadLocale (), LOCALE_IFIRSTDAYOFWEEK,
822
wbuffer, G_N_ELEMENTS (wbuffer)))
823
week_start = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
825
if (week_start != NULL)
827
priv->week_start = (week_start[0] - '0' + 1) % 7;
831
#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
832
langinfo.string = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
833
first_weekday = langinfo.string[0];
834
langinfo.string = nl_langinfo (_NL_TIME_WEEK_1STDAY);
835
week_origin = langinfo.word;
836
if (week_origin == 19971130) /* Sunday */
838
else if (week_origin == 19971201) /* Monday */
841
g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY.\n");
843
priv->week_start = (week_1stday + first_weekday - 1) % 7;
845
/* Translate to calendar:week_start:0 if you want Sunday to be the
846
* first day of the week to calendar:week_start:1 if you want Monday
847
* to be the first day of the week, and so on.
849
week_start = _("calendar:week_start:0");
851
if (strncmp (week_start, "calendar:week_start:", 20) == 0)
852
priv->week_start = *(week_start + 20) - '0';
854
priv->week_start = -1;
856
if (priv->week_start < 0 || priv->week_start > 6)
858
g_warning ("Whoever translated calendar:week_start:0 did so wrongly.\n");
859
priv->week_start = 0;
864
calendar_compute_days (calendar);
868
/****************************************
869
* Utility Functions *
870
****************************************/
873
calendar_queue_refresh (GlomGtkCalendar *calendar)
875
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
877
if (!(priv->detail_func) ||
878
!(calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_DETAILS) ||
879
(priv->detail_width_chars && priv->detail_height_rows))
880
gtk_widget_queue_draw (GTK_WIDGET (calendar));
882
gtk_widget_queue_resize (GTK_WIDGET (calendar));
886
calendar_set_month_next (GlomGtkCalendar *calendar)
890
g_return_if_fail (GTK_IS_WIDGET (calendar));
892
if (calendar->display_flags & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
896
if (calendar->month == 11)
904
calendar_compute_days (calendar);
905
g_signal_emit (calendar,
906
glom_gtk_calendar_signals[NEXT_MONTH_SIGNAL],
908
g_signal_emit (calendar,
909
glom_gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
912
month_len = month_length[leap (calendar->year)][calendar->month + 1];
914
if (month_len < calendar->selected_day)
916
calendar->selected_day = 0;
917
glom_gtk_calendar_select_day (calendar, month_len);
920
glom_gtk_calendar_select_day (calendar, calendar->selected_day);
922
calendar_queue_refresh (calendar);
926
calendar_set_year_prev (GlomGtkCalendar *calendar)
930
g_return_if_fail (GTK_IS_WIDGET (calendar));
933
calendar_compute_days (calendar);
934
g_signal_emit (calendar,
935
glom_gtk_calendar_signals[PREV_YEAR_SIGNAL],
937
g_signal_emit (calendar,
938
glom_gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
941
month_len = month_length[leap (calendar->year)][calendar->month + 1];
943
if (month_len < calendar->selected_day)
945
calendar->selected_day = 0;
946
glom_gtk_calendar_select_day (calendar, month_len);
949
glom_gtk_calendar_select_day (calendar, calendar->selected_day);
951
calendar_queue_refresh (calendar);
955
calendar_set_year_next (GlomGtkCalendar *calendar)
959
g_return_if_fail (GTK_IS_WIDGET (calendar));
962
calendar_compute_days (calendar);
963
g_signal_emit (calendar,
964
glom_gtk_calendar_signals[NEXT_YEAR_SIGNAL],
966
g_signal_emit (calendar,
967
glom_gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
970
month_len = month_length[leap (calendar->year)][calendar->month + 1];
972
if (month_len < calendar->selected_day)
974
calendar->selected_day = 0;
975
glom_gtk_calendar_select_day (calendar, month_len);
978
glom_gtk_calendar_select_day (calendar, calendar->selected_day);
980
calendar_queue_refresh (calendar);
984
calendar_compute_days (GlomGtkCalendar *calendar)
986
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (GTK_WIDGET (calendar));
990
gint ndays_in_prev_month;
996
g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
998
year = calendar->year;
999
month = calendar->month + 1;
1001
ndays_in_month = month_length[leap (year)][month];
1003
first_day = day_of_week (year, month, 1);
1004
first_day = (first_day + 7 - priv->week_start) % 7;
1006
/* Compute days of previous month */
1008
ndays_in_prev_month = month_length[leap (year)][month-1];
1010
ndays_in_prev_month = month_length[leap (year)][12];
1011
day = ndays_in_prev_month - first_day + 1;
1016
for (col = 0; col < first_day; col++)
1018
calendar->day[row][col] = day;
1019
calendar->day_month[row][col] = MONTH_PREV;
1024
/* Compute days of current month */
1026
for (day = 1; day <= ndays_in_month; day++)
1028
calendar->day[row][col] = day;
1029
calendar->day_month[row][col] = MONTH_CURRENT;
1039
/* Compute days of next month */
1041
for (; row <= 5; row++)
1043
for (; col <= 6; col++)
1045
calendar->day[row][col] = day;
1046
calendar->day_month[row][col] = MONTH_NEXT;
1054
calendar_select_and_focus_day (GlomGtkCalendar *calendar,
1057
gint old_focus_row = calendar->focus_row;
1058
gint old_focus_col = calendar->focus_col;
1062
for (row = 0; row < 6; row ++)
1063
for (col = 0; col < 7; col++)
1065
if (calendar->day_month[row][col] == MONTH_CURRENT
1066
&& calendar->day[row][col] == day)
1068
calendar->focus_row = row;
1069
calendar->focus_col = col;
1073
if (old_focus_row != -1 && old_focus_col != -1)
1074
calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
1076
glom_gtk_calendar_select_day (calendar, day);
1080
/****************************************
1081
* Layout computation utilities *
1082
****************************************/
1085
calendar_row_height (GlomGtkCalendar *calendar)
1087
return (GLOM_GTK_CALENDAR_GET_PRIVATE (calendar)->main_h - CALENDAR_MARGIN
1088
- ((calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_DAY_NAMES)
1089
? CALENDAR_YSEP : CALENDAR_MARGIN)) / 6;
1093
/* calendar_left_x_for_column: returns the x coordinate
1094
* for the left of the column */
1096
calendar_left_x_for_column (GlomGtkCalendar *calendar,
1102
if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
1103
column = 6 - column;
1105
width = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
1106
if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1107
x_left = CALENDAR_XSEP + (width + DAY_XSEP) * column;
1109
x_left = CALENDAR_MARGIN + (width + DAY_XSEP) * column;
1114
/* column_from_x: returns the column 0-6 that the
1115
* x pixel of the xwindow is in */
1117
calendar_column_from_x (GlomGtkCalendar *calendar,
1121
gint x_left, x_right;
1125
for (c = 0; c < 7; c++)
1127
x_left = calendar_left_x_for_column (calendar, c);
1128
x_right = x_left + GLOM_GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
1130
if (event_x >= x_left && event_x < x_right)
1140
/* calendar_top_y_for_row: returns the y coordinate
1141
* for the top of the row */
1143
calendar_top_y_for_row (GlomGtkCalendar *calendar,
1147
return (GLOM_GTK_CALENDAR_GET_PRIVATE (calendar)->main_h
1148
- (CALENDAR_MARGIN + (6 - row)
1149
* calendar_row_height (calendar)));
1152
/* row_from_y: returns the row 0-5 that the
1153
* y pixel of the xwindow is in */
1155
calendar_row_from_y (GlomGtkCalendar *calendar,
1160
gint y_top, y_bottom;
1162
height = calendar_row_height (calendar);
1165
for (r = 0; r < 6; r++)
1167
y_top = calendar_top_y_for_row (calendar, r);
1168
y_bottom = y_top + height;
1170
if (event_y >= y_top && event_y < y_bottom)
1181
calendar_arrow_rectangle (GlomGtkCalendar *calendar,
1185
GtkWidget *widget = GTK_WIDGET (calendar);
1186
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
1189
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
1190
year_left = priv->year_before;
1192
year_left = !priv->year_before;
1195
rect->width = priv->arrow_width;
1196
rect->height = priv->header_h - 7;
1200
case ARROW_MONTH_LEFT:
1202
rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1203
- (3 + 2*priv->arrow_width
1204
+ priv->max_month_width));
1208
case ARROW_MONTH_RIGHT:
1210
rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1211
- 3 - priv->arrow_width);
1213
rect->x = (priv->arrow_width
1214
+ priv->max_month_width);
1216
case ARROW_YEAR_LEFT:
1220
rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1221
- (3 + 2*priv->arrow_width
1222
+ priv->max_year_width));
1224
case ARROW_YEAR_RIGHT:
1226
rect->x = (priv->arrow_width
1227
+ priv->max_year_width);
1229
rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1230
- 3 - priv->arrow_width);
1236
calendar_day_rectangle (GlomGtkCalendar *calendar,
1241
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
1243
rect->x = calendar_left_x_for_column (calendar, col);
1244
rect->y = calendar_top_y_for_row (calendar, row);
1245
rect->height = calendar_row_height (calendar);
1246
rect->width = priv->day_width;
1250
calendar_set_month_prev (GlomGtkCalendar *calendar)
1254
if (calendar->display_flags & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
1257
if (calendar->month == 0)
1259
calendar->month = 11;
1265
month_len = month_length[leap (calendar->year)][calendar->month + 1];
1267
calendar_compute_days (calendar);
1269
g_signal_emit (calendar,
1270
glom_gtk_calendar_signals[PREV_MONTH_SIGNAL],
1272
g_signal_emit (calendar,
1273
glom_gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
1276
if (month_len < calendar->selected_day)
1278
calendar->selected_day = 0;
1279
glom_gtk_calendar_select_day (calendar, month_len);
1283
if (calendar->selected_day < 0)
1284
calendar->selected_day = calendar->selected_day + 1 + month_length[leap (calendar->year)][calendar->month + 1];
1285
glom_gtk_calendar_select_day (calendar, calendar->selected_day);
1288
calendar_queue_refresh (calendar);
1292
/****************************************
1293
* Basic object methods *
1294
****************************************/
1297
glom_gtk_calendar_finalize (GObject *object)
1299
(* G_OBJECT_CLASS (glom_gtk_calendar_parent_class)->finalize) (object);
1303
glom_gtk_calendar_destroy (GtkObject *object)
1305
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (object);
1307
calendar_stop_spinning (GLOM_GTK_CALENDAR (object));
1309
/* Call the destroy function for the extra display callback: */
1310
if (priv->detail_func_destroy && priv->detail_func_user_data)
1312
priv->detail_func_destroy (priv->detail_func_user_data);
1313
priv->detail_func_user_data = NULL;
1314
priv->detail_func_destroy = NULL;
1317
GTK_OBJECT_CLASS (glom_gtk_calendar_parent_class)->destroy (object);
1322
calendar_set_display_option (GlomGtkCalendar *calendar,
1323
GlomGtkCalendarDisplayOptions flag,
1326
GlomGtkCalendarDisplayOptions flags;
1328
flags = calendar->display_flags | flag;
1330
flags = calendar->display_flags & ~flag;
1331
glom_gtk_calendar_display_options (calendar, flags);
1335
calendar_get_display_option (GlomGtkCalendar *calendar,
1336
GlomGtkCalendarDisplayOptions flag)
1338
return (calendar->display_flags & flag) != 0;
1342
glom_gtk_calendar_set_property (GObject *object,
1344
const GValue *value,
1347
GlomGtkCalendar *calendar;
1349
calendar = GLOM_GTK_CALENDAR (object);
1354
glom_gtk_calendar_select_month (calendar,
1356
g_value_get_int (value));
1359
glom_gtk_calendar_select_month (calendar,
1360
g_value_get_int (value),
1364
glom_gtk_calendar_select_day (calendar,
1365
g_value_get_int (value));
1367
case PROP_SHOW_HEADING:
1368
calendar_set_display_option (calendar,
1369
GLOM_GTK_CALENDAR_SHOW_HEADING,
1370
g_value_get_boolean (value));
1372
case PROP_SHOW_DAY_NAMES:
1373
calendar_set_display_option (calendar,
1374
GLOM_GTK_CALENDAR_SHOW_DAY_NAMES,
1375
g_value_get_boolean (value));
1377
case PROP_NO_MONTH_CHANGE:
1378
calendar_set_display_option (calendar,
1379
GLOM_GTK_CALENDAR_NO_MONTH_CHANGE,
1380
g_value_get_boolean (value));
1382
case PROP_SHOW_WEEK_NUMBERS:
1383
calendar_set_display_option (calendar,
1384
GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS,
1385
g_value_get_boolean (value));
1387
case PROP_SHOW_DETAILS:
1388
calendar_set_display_option (calendar,
1389
GLOM_GTK_CALENDAR_SHOW_DETAILS,
1390
g_value_get_boolean (value));
1392
case PROP_DETAIL_WIDTH_CHARS:
1393
glom_gtk_calendar_set_detail_width_chars (calendar,
1394
g_value_get_int (value));
1396
case PROP_DETAIL_HEIGHT_ROWS:
1397
glom_gtk_calendar_set_detail_height_rows (calendar,
1398
g_value_get_int (value));
1401
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1407
glom_gtk_calendar_get_property (GObject *object,
1412
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (object);
1413
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (object);
1418
g_value_set_int (value, calendar->year);
1421
g_value_set_int (value, calendar->month);
1424
g_value_set_int (value, calendar->selected_day);
1426
case PROP_SHOW_HEADING:
1427
g_value_set_boolean (value, calendar_get_display_option (calendar,
1428
GLOM_GTK_CALENDAR_SHOW_HEADING));
1430
case PROP_SHOW_DAY_NAMES:
1431
g_value_set_boolean (value, calendar_get_display_option (calendar,
1432
GLOM_GTK_CALENDAR_SHOW_DAY_NAMES));
1434
case PROP_NO_MONTH_CHANGE:
1435
g_value_set_boolean (value, calendar_get_display_option (calendar,
1436
GLOM_GTK_CALENDAR_NO_MONTH_CHANGE));
1438
case PROP_SHOW_WEEK_NUMBERS:
1439
g_value_set_boolean (value, calendar_get_display_option (calendar,
1440
GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS));
1442
case PROP_SHOW_DETAILS:
1443
g_value_set_boolean (value, calendar_get_display_option (calendar,
1444
GLOM_GTK_CALENDAR_SHOW_DETAILS));
1446
case PROP_DETAIL_WIDTH_CHARS:
1447
g_value_set_int (value, priv->detail_width_chars);
1449
case PROP_DETAIL_HEIGHT_ROWS:
1450
g_value_set_int (value, priv->detail_height_rows);
1453
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1459
/****************************************
1461
****************************************/
1464
calendar_realize_arrows (GlomGtkCalendar *calendar)
1466
GtkWidget *widget = GTK_WIDGET (calendar);
1467
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
1468
GdkWindowAttr attributes;
1469
gint attributes_mask;
1472
/* Arrow windows ------------------------------------- */
1473
if (! (calendar->display_flags & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
1474
&& (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_HEADING))
1476
attributes.wclass = GDK_INPUT_OUTPUT;
1477
attributes.window_type = GDK_WINDOW_CHILD;
1478
attributes.visual = gtk_widget_get_visual (widget);
1479
attributes.colormap = gtk_widget_get_colormap (widget);
1480
attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1481
| GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1482
| GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1483
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1484
for (i = 0; i < 4; i++)
1487
calendar_arrow_rectangle (calendar, i, &rect);
1489
attributes.x = rect.x;
1490
attributes.y = rect.y;
1491
attributes.width = rect.width;
1492
attributes.height = rect.height;
1493
priv->arrow_win[i] = gdk_window_new (priv->header_win,
1496
if (GTK_WIDGET_IS_SENSITIVE (widget))
1497
priv->arrow_state[i] = GTK_STATE_NORMAL;
1499
priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
1500
gdk_window_set_background (priv->arrow_win[i],
1501
HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1502
gdk_window_show (priv->arrow_win[i]);
1503
gdk_window_set_user_data (priv->arrow_win[i], widget);
1508
for (i = 0; i < 4; i++)
1509
priv->arrow_win[i] = NULL;
1514
calendar_realize_header (GlomGtkCalendar *calendar)
1516
GtkWidget *widget = GTK_WIDGET (calendar);
1517
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
1518
GdkWindowAttr attributes;
1519
gint attributes_mask;
1521
/* Header window ------------------------------------- */
1522
if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_HEADING)
1524
attributes.wclass = GDK_INPUT_OUTPUT;
1525
attributes.window_type = GDK_WINDOW_CHILD;
1526
attributes.visual = gtk_widget_get_visual (widget);
1527
attributes.colormap = gtk_widget_get_colormap (widget);
1528
attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1529
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1530
attributes.x = widget->style->xthickness;
1531
attributes.y = widget->style->ythickness;
1532
attributes.width = widget->allocation.width - 2 * attributes.x;
1533
attributes.height = priv->header_h;
1534
priv->header_win = gdk_window_new (widget->window,
1535
&attributes, attributes_mask);
1537
gdk_window_set_background (priv->header_win,
1538
HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1539
gdk_window_show (priv->header_win);
1540
gdk_window_set_user_data (priv->header_win, widget);
1545
priv->header_win = NULL;
1547
calendar_realize_arrows (calendar);
1551
calendar_realize_day_names (GlomGtkCalendar *calendar)
1553
GtkWidget *widget = GTK_WIDGET (calendar);
1554
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
1555
GdkWindowAttr attributes;
1556
gint attributes_mask;
1558
/* Day names window --------------------------------- */
1559
if ( calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_DAY_NAMES)
1561
attributes.wclass = GDK_INPUT_OUTPUT;
1562
attributes.window_type = GDK_WINDOW_CHILD;
1563
attributes.visual = gtk_widget_get_visual (widget);
1564
attributes.colormap = gtk_widget_get_colormap (widget);
1565
attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1566
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1567
attributes.x = (widget->style->xthickness + INNER_BORDER);
1568
attributes.y = priv->header_h + (widget->style->ythickness
1570
attributes.width = (widget->allocation.width
1571
- (widget->style->xthickness + INNER_BORDER)
1573
attributes.height = priv->day_name_h;
1574
priv->day_name_win = gdk_window_new (widget->window,
1577
gdk_window_set_background (priv->day_name_win,
1578
BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1579
gdk_window_show (priv->day_name_win);
1580
gdk_window_set_user_data (priv->day_name_win, widget);
1584
priv->day_name_win = NULL;
1589
calendar_realize_week_numbers (GlomGtkCalendar *calendar)
1591
GtkWidget *widget = GTK_WIDGET (calendar);
1592
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
1593
GdkWindowAttr attributes;
1594
gint attributes_mask;
1596
/* Week number window -------------------------------- */
1597
if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1599
attributes.wclass = GDK_INPUT_OUTPUT;
1600
attributes.window_type = GDK_WINDOW_CHILD;
1601
attributes.visual = gtk_widget_get_visual (widget);
1602
attributes.colormap = gtk_widget_get_colormap (widget);
1603
attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1605
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1606
attributes.x = widget->style->xthickness + INNER_BORDER;
1607
attributes.y = (priv->header_h + priv->day_name_h
1608
+ (widget->style->ythickness + INNER_BORDER));
1609
attributes.width = priv->week_width;
1610
attributes.height = priv->main_h;
1611
priv->week_win = gdk_window_new (widget->window,
1612
&attributes, attributes_mask);
1613
gdk_window_set_background (priv->week_win,
1614
BACKGROUND_COLOR (GTK_WIDGET (calendar)));
1615
gdk_window_show (priv->week_win);
1616
gdk_window_set_user_data (priv->week_win, widget);
1620
priv->week_win = NULL;
1625
glom_gtk_calendar_realize (GtkWidget *widget)
1627
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
1628
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
1629
GdkWindowAttr attributes;
1630
gint attributes_mask;
1632
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1634
attributes.x = widget->allocation.x;
1635
attributes.y = widget->allocation.y;
1636
attributes.width = widget->allocation.width;
1637
attributes.height = widget->allocation.height;
1638
attributes.wclass = GDK_INPUT_OUTPUT;
1639
attributes.window_type = GDK_WINDOW_CHILD;
1640
attributes.event_mask = (gtk_widget_get_events (widget)
1641
| GDK_EXPOSURE_MASK |GDK_KEY_PRESS_MASK | GDK_SCROLL_MASK);
1642
attributes.visual = gtk_widget_get_visual (widget);
1643
attributes.colormap = gtk_widget_get_colormap (widget);
1645
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1646
widget->window = gdk_window_new (widget->parent->window,
1647
&attributes, attributes_mask);
1649
widget->style = gtk_style_attach (widget->style, widget->window);
1651
/* Header window ------------------------------------- */
1652
calendar_realize_header (calendar);
1653
/* Day names window --------------------------------- */
1654
calendar_realize_day_names (calendar);
1655
/* Week number window -------------------------------- */
1656
calendar_realize_week_numbers (calendar);
1657
/* Main Window -------------------------------------- */
1658
attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1659
| GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1660
| GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1662
attributes.x = priv->week_width + (widget->style->ythickness + INNER_BORDER);
1663
attributes.y = (priv->header_h + priv->day_name_h
1664
+ (widget->style->ythickness + INNER_BORDER));
1665
attributes.width = (widget->allocation.width - attributes.x
1666
- (widget->style->xthickness + INNER_BORDER));
1667
attributes.height = priv->main_h;
1668
priv->main_win = gdk_window_new (widget->window,
1669
&attributes, attributes_mask);
1670
gdk_window_set_background (priv->main_win,
1671
BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1672
gdk_window_show (priv->main_win);
1673
gdk_window_set_user_data (priv->main_win, widget);
1674
gdk_window_set_background (widget->window, BACKGROUND_COLOR (widget));
1675
gdk_window_show (widget->window);
1676
gdk_window_set_user_data (widget->window, widget);
1680
glom_gtk_calendar_unrealize (GtkWidget *widget)
1682
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
1685
if (priv->header_win)
1687
for (i = 0; i < 4; i++)
1689
if (priv->arrow_win[i])
1691
gdk_window_set_user_data (priv->arrow_win[i], NULL);
1692
gdk_window_destroy (priv->arrow_win[i]);
1693
priv->arrow_win[i] = NULL;
1696
gdk_window_set_user_data (priv->header_win, NULL);
1697
gdk_window_destroy (priv->header_win);
1698
priv->header_win = NULL;
1703
gdk_window_set_user_data (priv->week_win, NULL);
1704
gdk_window_destroy (priv->week_win);
1705
priv->week_win = NULL;
1710
gdk_window_set_user_data (priv->main_win, NULL);
1711
gdk_window_destroy (priv->main_win);
1712
priv->main_win = NULL;
1714
if (priv->day_name_win)
1716
gdk_window_set_user_data (priv->day_name_win, NULL);
1717
gdk_window_destroy (priv->day_name_win);
1718
priv->day_name_win = NULL;
1721
if (GTK_WIDGET_CLASS (glom_gtk_calendar_parent_class)->unrealize)
1722
(* GTK_WIDGET_CLASS (glom_gtk_calendar_parent_class)->unrealize) (widget);
1726
glom_gtk_calendar_get_detail (GlomGtkCalendar *calendar,
1730
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
1733
year = calendar->year;
1734
month = calendar->month + calendar->day_month[row][column] - MONTH_CURRENT;
1741
else if (month > 11)
1747
return priv->detail_func (calendar,
1749
calendar->day[row][column],
1750
priv->detail_func_user_data);
1754
glom_gtk_calendar_query_tooltip (GtkWidget *widget,
1757
gboolean keyboard_mode,
1758
GtkTooltip *tooltip)
1760
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
1761
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
1762
gchar *detail = NULL;
1763
GdkRectangle day_rect;
1767
gint x0, y0, row, col;
1769
gdk_window_get_position (priv->main_win, &x0, &y0);
1770
col = calendar_column_from_x (calendar, x - x0);
1771
row = calendar_row_from_y (calendar, y - y0);
1773
if (0 != (priv->detail_overflow[row] & (1 << col)) ||
1774
0 == (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_DETAILS))
1776
detail = glom_gtk_calendar_get_detail (calendar, row, col);
1777
calendar_day_rectangle (calendar, row, col, &day_rect);
1786
gtk_tooltip_set_tip_area (tooltip, &day_rect);
1787
gtk_tooltip_set_markup (tooltip, detail);
1794
if (GTK_WIDGET_CLASS (glom_gtk_calendar_parent_class)->query_tooltip)
1795
return GTK_WIDGET_CLASS (glom_gtk_calendar_parent_class)->query_tooltip (widget, x, y, keyboard_mode, tooltip);
1801
/****************************************
1802
* Size Request and Allocate *
1803
****************************************/
1806
glom_gtk_calendar_size_request (GtkWidget *widget,
1807
GtkRequisition *requisition)
1809
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
1810
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
1811
PangoLayout *layout;
1812
PangoRectangle logical_rect;
1816
gint calendar_margin = CALENDAR_MARGIN;
1817
gint header_width, main_width;
1818
gint max_header_height = 0;
1821
gint max_detail_height;
1823
gtk_widget_style_get (GTK_WIDGET (widget),
1824
"focus-line-width", &focus_width,
1825
"focus-padding", &focus_padding,
1828
layout = gtk_widget_create_pango_layout (widget, NULL);
1831
* Calculate the requisition width for the widget.
1836
if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_HEADING)
1838
priv->max_month_width = 0;
1839
for (i = 0; i < 12; i++)
1841
pango_layout_set_text (layout, default_monthname[i], -1);
1842
pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1843
priv->max_month_width = MAX (priv->max_month_width,
1844
logical_rect.width + 8);
1845
max_header_height = MAX (max_header_height, logical_rect.height);
1848
priv->max_year_width = 0;
1849
/* Translators: This is a text measurement template.
1850
* Translate it to the widest year text.
1852
* Don't include the prefix "year measurement template|"
1853
* in the translation.
1855
* If you don't understand this, leave it as "2000"
1857
pango_layout_set_text (layout, Q_("year measurement template|2000"), -1);
1858
pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1859
priv->max_year_width = MAX (priv->max_year_width,
1860
logical_rect.width + 8);
1861
max_header_height = MAX (max_header_height, logical_rect.height);
1865
priv->max_month_width = 0;
1866
priv->max_year_width = 0;
1869
if (calendar->display_flags & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
1870
header_width = (priv->max_month_width
1871
+ priv->max_year_width
1874
header_width = (priv->max_month_width
1875
+ priv->max_year_width
1876
+ 4 * priv->arrow_width + 3 * 3);
1878
/* Mainwindow labels width */
1880
priv->max_day_char_width = 0;
1881
priv->max_day_char_ascent = 0;
1882
priv->max_day_char_descent = 0;
1883
priv->min_day_width = 0;
1885
for (i = 0; i < 9; i++)
1888
g_snprintf (buffer, sizeof (buffer), Q_("calendar:day:digits|%d"), i * 11);
1889
pango_layout_set_text (layout, buffer, -1);
1890
pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1891
priv->min_day_width = MAX (priv->min_day_width,
1892
logical_rect.width);
1894
priv->max_day_char_ascent = MAX (priv->max_day_char_ascent,
1895
PANGO_ASCENT (logical_rect));
1896
priv->max_day_char_descent = MAX (priv->max_day_char_descent,
1897
PANGO_DESCENT (logical_rect));
1900
priv->max_label_char_ascent = 0;
1901
priv->max_label_char_descent = 0;
1902
if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_DAY_NAMES)
1903
for (i = 0; i < 7; i++)
1905
pango_layout_set_text (layout, default_abbreviated_dayname[i], -1);
1906
pango_layout_line_get_pixel_extents (pango_layout_get_lines_readonly (layout)->data, NULL, &logical_rect);
1908
priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1909
priv->max_label_char_ascent = MAX (priv->max_label_char_ascent,
1910
PANGO_ASCENT (logical_rect));
1911
priv->max_label_char_descent = MAX (priv->max_label_char_descent,
1912
PANGO_DESCENT (logical_rect));
1915
priv->max_week_char_width = 0;
1916
if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1917
for (i = 0; i < 9; i++)
1920
g_snprintf (buffer, sizeof (buffer), Q_("calendar:week:digits|%d"), i * 11);
1921
pango_layout_set_text (layout, buffer, -1);
1922
pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1923
priv->max_week_char_width = MAX (priv->max_week_char_width,
1924
logical_rect.width / 2);
1927
/* Calculate detail extents. Do this as late as possible since
1928
* pango_layout_set_markup is called which alters font settings. */
1929
max_detail_height = 0;
1931
if (priv->detail_func && (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_DETAILS))
1933
gchar *markup, *tail;
1935
if (priv->detail_width_chars || priv->detail_height_rows)
1937
gint rows = MAX (1, priv->detail_height_rows) - 1;
1938
gsize len = priv->detail_width_chars + rows + 16;
1940
markup = tail = g_alloca (len);
1942
memcpy (tail, "<small>", 7);
1945
memset (tail, 'm', priv->detail_width_chars);
1946
tail += priv->detail_width_chars;
1948
memset (tail, '\n', rows);
1951
memcpy (tail, "</small>", 9);
1954
g_assert (len == (tail - markup));
1956
pango_layout_set_markup (layout, markup, -1);
1957
pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1959
if (priv->detail_width_chars)
1960
priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1961
if (priv->detail_height_rows)
1962
max_detail_height = MAX (max_detail_height, logical_rect.height);
1965
if (!priv->detail_width_chars || !priv->detail_height_rows)
1966
for (r = 0; r < 6; r++)
1967
for (c = 0; c < 7; c++)
1969
gchar *detail = glom_gtk_calendar_get_detail (calendar, r, c);
1973
markup = g_strconcat ("<small>", detail, "</small>", NULL);
1974
pango_layout_set_markup (layout, markup, -1);
1976
if (priv->detail_width_chars)
1978
pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
1979
pango_layout_set_width (layout, PANGO_SCALE * priv->min_day_width);
1982
pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1984
if (!priv->detail_width_chars)
1985
priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1986
if (!priv->detail_height_rows)
1987
max_detail_height = MAX (max_detail_height, logical_rect.height);
1995
/* We add one to max_day_char_width to be able to make the marked day "bold" */
1996
priv->max_day_char_width = priv->min_day_width / 2 + 1;
1998
main_width = (7 * (priv->min_day_width + (focus_padding + focus_width) * 2) + (DAY_XSEP * 6) + CALENDAR_MARGIN * 2
1999
+ (priv->max_week_char_width
2000
? priv->max_week_char_width * 2 + (focus_padding + focus_width) * 2 + CALENDAR_XSEP * 2
2004
requisition->width = MAX (header_width, main_width + INNER_BORDER * 2) + widget->style->xthickness * 2;
2007
* Calculate the requisition height for the widget.
2010
if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_HEADING)
2012
priv->header_h = (max_header_height + CALENDAR_YSEP * 2);
2019
if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_DAY_NAMES)
2021
priv->day_name_h = (priv->max_label_char_ascent
2022
+ priv->max_label_char_descent
2023
+ 2 * (focus_padding + focus_width) + calendar_margin);
2024
calendar_margin = CALENDAR_YSEP;
2028
priv->day_name_h = 0;
2031
priv->main_h = (CALENDAR_MARGIN + calendar_margin
2032
+ 6 * (priv->max_day_char_ascent
2033
+ priv->max_day_char_descent
2035
+ 2 * (focus_padding + focus_width))
2038
height = (priv->header_h + priv->day_name_h
2041
requisition->height = height + (widget->style->ythickness + INNER_BORDER) * 2;
2043
g_object_unref (layout);
2047
glom_gtk_calendar_size_allocate (GtkWidget *widget,
2048
GtkAllocation *allocation)
2050
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
2051
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
2052
gint xthickness = widget->style->xthickness;
2053
gint ythickness = widget->style->xthickness;
2056
widget->allocation = *allocation;
2058
if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2060
priv->day_width = (priv->min_day_width
2061
* ((allocation->width - (xthickness + INNER_BORDER) * 2
2062
- (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2))
2063
/ (7 * priv->min_day_width + priv->max_week_char_width * 2));
2064
priv->week_width = ((allocation->width - (xthickness + INNER_BORDER) * 2
2065
- (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2 )
2066
- priv->day_width * 7 + CALENDAR_MARGIN + CALENDAR_XSEP);
2070
priv->day_width = (allocation->width
2071
- (xthickness + INNER_BORDER) * 2
2072
- (CALENDAR_MARGIN * 2)
2073
- (DAY_XSEP * 6))/7;
2074
priv->week_width = 0;
2077
if (GTK_WIDGET_REALIZED (widget))
2079
gdk_window_move_resize (widget->window,
2080
allocation->x, allocation->y,
2081
allocation->width, allocation->height);
2082
if (priv->header_win)
2083
gdk_window_move_resize (priv->header_win,
2084
xthickness, ythickness,
2085
allocation->width - 2 * xthickness, priv->header_h);
2087
for (i = 0 ; i < 4 ; i++)
2089
if (priv->arrow_win[i])
2092
calendar_arrow_rectangle (calendar, i, &rect);
2094
gdk_window_move_resize (priv->arrow_win[i],
2095
rect.x, rect.y, rect.width, rect.height);
2099
if (priv->day_name_win)
2100
gdk_window_move_resize (priv->day_name_win,
2101
xthickness + INNER_BORDER,
2102
priv->header_h + (widget->style->ythickness + INNER_BORDER),
2103
allocation->width - (xthickness + INNER_BORDER) * 2,
2105
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2108
gdk_window_move_resize (priv->week_win,
2109
(xthickness + INNER_BORDER),
2110
priv->header_h + priv->day_name_h
2111
+ (widget->style->ythickness + INNER_BORDER),
2114
gdk_window_move_resize (priv->main_win,
2115
priv->week_width + (xthickness + INNER_BORDER),
2116
priv->header_h + priv->day_name_h
2117
+ (widget->style->ythickness + INNER_BORDER),
2120
- (xthickness + INNER_BORDER) * 2,
2125
gdk_window_move_resize (priv->main_win,
2126
(xthickness + INNER_BORDER),
2127
priv->header_h + priv->day_name_h
2128
+ (widget->style->ythickness + INNER_BORDER),
2131
- (xthickness + INNER_BORDER) * 2,
2134
gdk_window_move_resize (priv->week_win,
2137
- (xthickness + INNER_BORDER),
2138
priv->header_h + priv->day_name_h
2139
+ (widget->style->ythickness + INNER_BORDER),
2147
/****************************************
2149
****************************************/
2152
calendar_paint_header (GlomGtkCalendar *calendar)
2154
GtkWidget *widget = GTK_WIDGET (calendar);
2155
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
2160
gint max_month_width;
2161
gint max_year_width;
2162
PangoLayout *layout;
2163
PangoRectangle logical_rect;
2169
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
2170
year_left = priv->year_before;
2172
year_left = !priv->year_before;
2174
cr = gdk_cairo_create (priv->header_win);
2176
header_width = widget->allocation.width - 2 * widget->style->xthickness;
2178
max_month_width = priv->max_month_width;
2179
max_year_width = priv->max_year_width;
2181
gtk_paint_shadow (widget->style, priv->header_win,
2182
GTK_STATE_NORMAL, GTK_SHADOW_OUT,
2183
NULL, widget, "calendar",
2184
0, 0, header_width, priv->header_h);
2186
tmp_time = 1; /* Jan 1 1970, 00:00:01 UTC */
2187
tm = gmtime (&tmp_time);
2188
tm->tm_year = calendar->year - 1900;
2190
/* Translators: This dictates how the year is displayed in
2191
* gtkcalendar widget. See strftime() manual for the format.
2192
* Use only ASCII in the translation.
2194
* Also look for the msgid "year measurement template|2000".
2195
* Translate that entry to a year with the widest output of this
2198
* Don't include the prefix "calendar year format|" in the
2199
* translation. "%Y" is appropriate for most locales.
2201
strftime (buffer, sizeof (buffer), Q_("calendar year format|%Y"), tm);
2202
str = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
2203
layout = gtk_widget_create_pango_layout (widget, str);
2206
pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2209
y = (priv->header_h - logical_rect.height) / 2;
2211
/* Draw year and its arrows */
2213
if (calendar->display_flags & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
2215
x = 3 + (max_year_width - logical_rect.width)/2;
2217
x = header_width - (3 + max_year_width
2218
- (max_year_width - logical_rect.width)/2);
2221
x = 3 + priv->arrow_width + (max_year_width - logical_rect.width)/2;
2223
x = header_width - (3 + priv->arrow_width + max_year_width
2224
- (max_year_width - logical_rect.width)/2);
2227
gdk_cairo_set_source_color (cr, HEADER_FG_COLOR (GTK_WIDGET (calendar)));
2228
cairo_move_to (cr, x, y);
2229
pango_cairo_show_layout (cr, layout);
2232
g_snprintf (buffer, sizeof (buffer), "%s", default_monthname[calendar->month]);
2233
pango_layout_set_text (layout, buffer, -1);
2234
pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2236
if (calendar->display_flags & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
2238
x = header_width - (3 + max_month_width
2239
- (max_month_width - logical_rect.width)/2);
2241
x = 3 + (max_month_width - logical_rect.width) / 2;
2244
x = header_width - (3 + priv->arrow_width + max_month_width
2245
- (max_month_width - logical_rect.width)/2);
2247
x = 3 + priv->arrow_width + (max_month_width - logical_rect.width)/2;
2249
cairo_move_to (cr, x, y);
2250
pango_cairo_show_layout (cr, layout);
2252
g_object_unref (layout);
2257
calendar_paint_day_names (GlomGtkCalendar *calendar)
2259
GtkWidget *widget = GTK_WIDGET (calendar);
2260
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
2264
int day_width, cal_width;
2266
PangoLayout *layout;
2267
PangoRectangle logical_rect;
2271
cr = gdk_cairo_create (priv->day_name_win);
2273
gtk_widget_style_get (GTK_WIDGET (widget),
2274
"focus-line-width", &focus_width,
2275
"focus-padding", &focus_padding,
2278
day_width = priv->day_width;
2279
cal_width = widget->allocation.width;
2280
day_wid_sep = day_width + DAY_XSEP;
2283
* Draw rectangles as inverted background for the labels.
2286
gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2287
cairo_rectangle (cr,
2288
CALENDAR_MARGIN, CALENDAR_MARGIN,
2289
cal_width-CALENDAR_MARGIN * 2,
2290
priv->day_name_h - CALENDAR_MARGIN);
2293
if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2295
cairo_rectangle (cr,
2297
priv->day_name_h - CALENDAR_YSEP,
2298
priv->week_width - CALENDAR_YSEP - CALENDAR_MARGIN,
2307
layout = gtk_widget_create_pango_layout (widget, NULL);
2309
gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2310
for (i = 0; i < 7; i++)
2312
if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
2316
day = (day + priv->week_start) % 7;
2317
g_snprintf (buffer, sizeof (buffer), "%s", default_abbreviated_dayname[day]);
2319
pango_layout_set_text (layout, buffer, -1);
2320
pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2324
+ (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ?
2325
(priv->week_width + (priv->week_width ? CALENDAR_XSEP : 0))
2328
+ (day_width - logical_rect.width)/2),
2329
CALENDAR_MARGIN + focus_width + focus_padding + logical_rect.y);
2330
pango_cairo_show_layout (cr, layout);
2333
g_object_unref (layout);
2338
calendar_paint_week_numbers (GlomGtkCalendar *calendar)
2340
GtkWidget *widget = GTK_WIDGET (calendar);
2341
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
2344
guint week = 0, year;
2345
gint row, x_loc, y_loc;
2348
PangoLayout *layout;
2349
PangoRectangle logical_rect;
2353
cr = gdk_cairo_create (priv->week_win);
2355
gtk_widget_style_get (GTK_WIDGET (widget),
2356
"focus-line-width", &focus_width,
2357
"focus-padding", &focus_padding,
2361
* Draw a rectangle as inverted background for the labels.
2364
gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2365
if (priv->day_name_win)
2366
cairo_rectangle (cr,
2369
priv->week_width - CALENDAR_MARGIN,
2370
priv->main_h - CALENDAR_MARGIN);
2372
cairo_rectangle (cr,
2375
priv->week_width - CALENDAR_MARGIN,
2376
priv->main_h - 2 * CALENDAR_MARGIN);
2383
layout = gtk_widget_create_pango_layout (widget, NULL);
2385
gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2386
day_height = calendar_row_height (calendar);
2387
for (row = 0; row < 6; row++)
2391
year = calendar->year;
2392
if (calendar->day[row][6] < 15 && row > 3 && calendar->month == 11)
2395
result = week_of_year (&week, &year,
2396
((calendar->day[row][6] < 15 && row > 3 ? 1 : 0)
2397
+ calendar->month) % 12 + 1, calendar->day[row][6]);
2398
g_return_if_fail (result);
2400
/* Translators: this defines whether the week numbers should use
2401
* localized digits or the ones used in English (0123...).
2403
* Translate to "%Id" if you want to use localized digits, or
2404
* translate to "%d" otherwise. Don't include the
2405
* "calendar:week:digits|" part in the translation.
2407
* Note that translating this doesn't guarantee that you get localized
2408
* digits. That needs support from your system and locale definition
2411
g_snprintf (buffer, sizeof (buffer), Q_("calendar:week:digits|%d"), week);
2412
pango_layout_set_text (layout, buffer, -1);
2413
pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2415
y_loc = calendar_top_y_for_row (calendar, row) + (day_height - logical_rect.height) / 2;
2417
x_loc = (priv->week_width
2418
- logical_rect.width
2419
- CALENDAR_XSEP - focus_padding - focus_width);
2421
cairo_move_to (cr, x_loc, y_loc);
2422
pango_cairo_show_layout (cr, layout);
2425
g_object_unref (layout);
2430
calendar_invalidate_day_num (GlomGtkCalendar *calendar,
2433
gint r, c, row, col;
2437
for (r = 0; r < 6; r++)
2438
for (c = 0; c < 7; c++)
2439
if (calendar->day_month[r][c] == MONTH_CURRENT &&
2440
calendar->day[r][c] == day)
2446
g_return_if_fail (row != -1);
2447
g_return_if_fail (col != -1);
2449
calendar_invalidate_day (calendar, row, col);
2453
calendar_invalidate_day (GlomGtkCalendar *calendar,
2457
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
2461
GdkRectangle day_rect;
2463
calendar_day_rectangle (calendar, row, col, &day_rect);
2464
gdk_window_invalidate_rect (priv->main_win, &day_rect, FALSE);
2469
is_color_attribute (PangoAttribute *attribute,
2472
return (attribute->klass->type == PANGO_ATTR_FOREGROUND ||
2473
attribute->klass->type == PANGO_ATTR_BACKGROUND);
2477
calendar_paint_day (GlomGtkCalendar *calendar,
2481
GtkWidget *widget = GTK_WIDGET (calendar);
2482
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
2484
GdkColor *text_color;
2489
GdkRectangle day_rect;
2491
PangoLayout *layout;
2492
PangoRectangle logical_rect;
2493
gboolean overflow = FALSE;
2494
gboolean show_details;
2496
g_return_if_fail (row < 6);
2497
g_return_if_fail (col < 7);
2499
cr = gdk_cairo_create (priv->main_win);
2501
day = calendar->day[row][col];
2502
show_details = (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_DETAILS);
2504
calendar_day_rectangle (calendar, row, col, &day_rect);
2506
if (calendar->day_month[row][col] == MONTH_PREV)
2508
text_color = PREV_MONTH_COLOR (widget);
2510
else if (calendar->day_month[row][col] == MONTH_NEXT)
2512
text_color = NEXT_MONTH_COLOR (widget);
2517
if (calendar->highlight_row == row && calendar->highlight_col == col)
2519
cairo_set_source_color (cr, HIGHLIGHT_BG_COLOR (widget));
2520
gdk_cairo_rectangle (cr, &day_rect);
2524
if (calendar->selected_day == day)
2526
gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2527
gdk_cairo_rectangle (cr, &day_rect);
2530
if (calendar->selected_day == day)
2531
text_color = SELECTED_FG_COLOR (widget);
2532
else if (calendar->marked_date[day-1])
2533
text_color = MARKED_COLOR (widget);
2535
text_color = NORMAL_DAY_COLOR (widget);
2538
/* Translators: this defines whether the day numbers should use
2539
* localized digits or the ones used in English (0123...).
2541
* Translate to "%Id" if you want to use localized digits, or
2542
* translate to "%d" otherwise. Don't include the "calendar:day:digits|"
2543
* part in the translation.
2545
* Note that translating this doesn't guarantee that you get localized
2546
* digits. That needs support from your system and locale definition
2549
g_snprintf (buffer, sizeof (buffer), Q_("calendar:day:digits|%d"), day);
2551
/* Get extra information to show, if any: */
2553
if (priv->detail_func)
2554
detail = glom_gtk_calendar_get_detail (calendar, row, col);
2558
layout = gtk_widget_create_pango_layout (widget, buffer);
2559
pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
2560
pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2562
x_loc = day_rect.x + (day_rect.width - logical_rect.width) / 2;
2565
gdk_cairo_set_source_color (cr, text_color);
2566
cairo_move_to (cr, x_loc, y_loc);
2567
pango_cairo_show_layout (cr, layout);
2569
if (calendar->day_month[row][col] == MONTH_CURRENT &&
2570
(calendar->marked_date[day-1] || (detail && !show_details)))
2572
cairo_move_to (cr, x_loc - 1, y_loc);
2573
pango_cairo_show_layout (cr, layout);
2576
y_loc += priv->max_day_char_descent;
2578
if (priv->detail_func && show_details)
2582
if (calendar->selected_day == day)
2583
gdk_cairo_set_source_color (cr, &widget->style->text[GTK_STATE_ACTIVE]);
2584
else if (calendar->day_month[row][col] == MONTH_CURRENT)
2585
gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_ACTIVE]);
2587
gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_INSENSITIVE]);
2589
cairo_set_line_width (cr, 1);
2590
cairo_move_to (cr, day_rect.x + 2, y_loc + 0.5);
2591
cairo_line_to (cr, day_rect.x + day_rect.width - 2, y_loc + 0.5);
2599
if (detail && show_details)
2603
gchar *markup = g_strconcat ("<small>", detail, "</small>", NULL);
2604
pango_layout_set_markup (layout, markup, -1);
2607
if (day == calendar->selected_day)
2609
/* Stripping colors as they conflict with selection marking. */
2611
PangoAttrList *attrs = pango_layout_get_attributes (layout);
2612
PangoAttrList *colors = NULL;
2615
colors = pango_attr_list_filter (attrs, is_color_attribute, NULL);
2617
pango_attr_list_unref (colors);
2620
if (priv->detail_width_chars)
2622
pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
2623
pango_layout_set_width (layout, PANGO_SCALE * priv->min_day_width);
2626
n_lines = pango_layout_get_line_count (layout);
2628
if (priv->detail_height_rows &&
2629
n_lines > priv->detail_height_rows)
2631
n_lines = priv->detail_height_rows;
2635
for (i = 0; i < n_lines; ++i)
2637
PangoLayoutLine *line = pango_layout_get_line_readonly (layout, i);
2638
pango_layout_line_get_pixel_extents (line, NULL, &logical_rect);
2640
x_loc = day_rect.x + (day_rect.width - logical_rect.width) / 2;
2641
y_loc += PANGO_ASCENT (logical_rect);
2643
cairo_move_to (cr, x_loc, y_loc);
2644
pango_cairo_show_layout_line (cr, line);
2646
y_loc += PANGO_DESCENT (logical_rect);
2650
if (GTK_WIDGET_HAS_FOCUS (calendar)
2651
&& calendar->focus_row == row && calendar->focus_col == col)
2655
if (calendar->selected_day == day)
2656
state = GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
2658
state = GTK_STATE_NORMAL;
2660
gtk_paint_focus (widget->style,
2663
NULL, widget, "calendar-day",
2664
day_rect.x, day_rect.y,
2665
day_rect.width, day_rect.height);
2669
priv->detail_overflow[row] |= (1 << col);
2671
priv->detail_overflow[row] &= ~(1 << col);
2673
g_object_unref (layout);
2679
calendar_paint_main (GlomGtkCalendar *calendar)
2683
for (col = 0; col < 7; col++)
2684
for (row = 0; row < 6; row++)
2685
calendar_paint_day (calendar, row, col);
2689
calendar_invalidate_arrow (GlomGtkCalendar *calendar,
2692
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
2695
window = priv->arrow_win[arrow];
2697
gdk_window_invalidate_rect (window, NULL, FALSE);
2701
calendar_paint_arrow (GlomGtkCalendar *calendar,
2704
GtkWidget *widget = GTK_WIDGET (calendar);
2705
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
2708
window = priv->arrow_win[arrow];
2711
cairo_t *cr = gdk_cairo_create (window);
2715
state = priv->arrow_state[arrow];
2717
gdk_cairo_set_source_color (cr, &widget->style->bg[state]);
2721
gdk_drawable_get_size (window, &width, &height);
2722
if (arrow == ARROW_MONTH_LEFT || arrow == ARROW_YEAR_LEFT)
2723
gtk_paint_arrow (widget->style, window, state,
2724
GTK_SHADOW_OUT, NULL, widget, "calendar",
2725
GTK_ARROW_LEFT, TRUE,
2726
width/2 - 3, height/2 - 4, 8, 8);
2728
gtk_paint_arrow (widget->style, window, state,
2729
GTK_SHADOW_OUT, NULL, widget, "calendar",
2730
GTK_ARROW_RIGHT, TRUE,
2731
width/2 - 4, height/2 - 4, 8, 8);
2736
glom_gtk_calendar_expose (GtkWidget *widget,
2737
GdkEventExpose *event)
2739
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
2740
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
2743
if (GTK_WIDGET_DRAWABLE (widget))
2745
if (event->window == priv->main_win)
2746
calendar_paint_main (calendar);
2748
if (event->window == priv->header_win)
2749
calendar_paint_header (calendar);
2751
for (i = 0; i < 4; i++)
2752
if (event->window == priv->arrow_win[i])
2753
calendar_paint_arrow (calendar, i);
2755
if (event->window == priv->day_name_win)
2756
calendar_paint_day_names (calendar);
2758
if (event->window == priv->week_win)
2759
calendar_paint_week_numbers (calendar);
2760
if (event->window == widget->window)
2762
gtk_paint_shadow (widget->style, widget->window, GTK_WIDGET_STATE (widget),
2763
GTK_SHADOW_IN, NULL, widget, "calendar",
2764
0, 0, widget->allocation.width, widget->allocation.height);
2772
/****************************************
2774
****************************************/
2777
calendar_arrow_action (GlomGtkCalendar *calendar,
2782
case ARROW_YEAR_LEFT:
2783
calendar_set_year_prev (calendar);
2785
case ARROW_YEAR_RIGHT:
2786
calendar_set_year_next (calendar);
2788
case ARROW_MONTH_LEFT:
2789
calendar_set_month_prev (calendar);
2791
case ARROW_MONTH_RIGHT:
2792
calendar_set_month_next (calendar);
2800
calendar_timer (gpointer data)
2802
GlomGtkCalendar *calendar = data;
2803
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
2804
gboolean retval = FALSE;
2808
calendar_arrow_action (calendar, priv->click_child);
2810
if (priv->need_timer)
2812
GtkSettings *settings;
2815
settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2816
g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
2818
priv->need_timer = FALSE;
2819
priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2820
timeout * SCROLL_DELAY_FACTOR,
2821
(GSourceFunc) calendar_timer,
2822
(gpointer) calendar, NULL);
2832
calendar_start_spinning (GlomGtkCalendar *calendar,
2835
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
2837
priv->click_child = click_child;
2841
GtkSettings *settings;
2844
settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2845
g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
2847
priv->need_timer = TRUE;
2848
priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2850
(GSourceFunc) calendar_timer,
2851
(gpointer) calendar, NULL);
2856
calendar_stop_spinning (GlomGtkCalendar *calendar)
2858
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
2862
g_source_remove (priv->timer);
2864
priv->need_timer = FALSE;
2869
calendar_main_button_press (GlomGtkCalendar *calendar,
2870
GdkEventButton *event)
2872
GtkWidget *widget = GTK_WIDGET (calendar);
2873
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
2879
x = (gint) (event->x);
2880
y = (gint) (event->y);
2882
row = calendar_row_from_y (calendar, y);
2883
col = calendar_column_from_x (calendar, x);
2885
/* If row or column isn't found, just return. */
2886
if (row == -1 || col == -1)
2889
day_month = calendar->day_month[row][col];
2891
if (event->type == GDK_BUTTON_PRESS)
2893
day = calendar->day[row][col];
2895
if (day_month == MONTH_PREV)
2896
calendar_set_month_prev (calendar);
2897
else if (day_month == MONTH_NEXT)
2898
calendar_set_month_next (calendar);
2900
if (!GTK_WIDGET_HAS_FOCUS (widget))
2901
gtk_widget_grab_focus (widget);
2903
if (event->button == 1)
2906
priv->drag_start_x = x;
2907
priv->drag_start_y = y;
2910
calendar_select_and_focus_day (calendar, day);
2912
else if (event->type == GDK_2BUTTON_PRESS)
2915
if (day_month == MONTH_CURRENT)
2916
g_signal_emit (calendar,
2917
glom_gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL],
2923
glom_gtk_calendar_button_press (GtkWidget *widget,
2924
GdkEventButton *event)
2926
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
2927
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
2930
if (event->window == priv->main_win)
2931
calendar_main_button_press (calendar, event);
2933
if (!GTK_WIDGET_HAS_FOCUS (widget))
2934
gtk_widget_grab_focus (widget);
2936
for (arrow = ARROW_YEAR_LEFT; arrow <= ARROW_MONTH_RIGHT; arrow++)
2938
if (event->window == priv->arrow_win[arrow])
2941
/* only call the action on single click, not double */
2942
if (event->type == GDK_BUTTON_PRESS)
2944
if (event->button == 1)
2945
calendar_start_spinning (calendar, arrow);
2947
calendar_arrow_action (calendar, arrow);
2958
glom_gtk_calendar_button_release (GtkWidget *widget,
2959
GdkEventButton *event)
2961
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
2962
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
2964
if (event->button == 1)
2966
calendar_stop_spinning (calendar);
2976
glom_gtk_calendar_motion_notify (GtkWidget *widget,
2977
GdkEventMotion *event)
2979
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
2980
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
2981
gint event_x, event_y;
2983
gint old_row, old_col;
2985
event_x = (gint) (event->x);
2986
event_y = (gint) (event->y);
2988
if (event->window == priv->main_win)
2993
if (gtk_drag_check_threshold (widget,
2994
priv->drag_start_x, priv->drag_start_y,
2995
event->x, event->y))
2997
GdkDragContext *context;
2998
GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
2999
gtk_target_list_add_text_targets (target_list, 0);
3000
context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY,
3001
1, (GdkEvent *)event);
3006
gtk_target_list_unref (target_list);
3007
gtk_drag_set_icon_default (context);
3012
row = calendar_row_from_y (calendar, event_y);
3013
col = calendar_column_from_x (calendar, event_x);
3015
if (row != calendar->highlight_row || calendar->highlight_col != col)
3017
old_row = calendar->highlight_row;
3018
old_col = calendar->highlight_col;
3019
if (old_row > -1 && old_col > -1)
3021
calendar->highlight_row = -1;
3022
calendar->highlight_col = -1;
3023
calendar_invalidate_day (calendar, old_row, old_col);
3026
calendar->highlight_row = row;
3027
calendar->highlight_col = col;
3029
if (row > -1 && col > -1)
3030
calendar_invalidate_day (calendar, row, col);
3038
glom_gtk_calendar_enter_notify (GtkWidget *widget,
3039
GdkEventCrossing *event)
3041
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
3042
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
3044
if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
3046
priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_PRELIGHT;
3047
calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
3050
if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
3052
priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_PRELIGHT;
3053
calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
3056
if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
3058
priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_PRELIGHT;
3059
calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
3062
if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
3064
priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_PRELIGHT;
3065
calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
3072
glom_gtk_calendar_leave_notify (GtkWidget *widget,
3073
GdkEventCrossing *event)
3075
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
3076
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
3080
if (event->window == priv->main_win)
3082
row = calendar->highlight_row;
3083
col = calendar->highlight_col;
3084
calendar->highlight_row = -1;
3085
calendar->highlight_col = -1;
3086
if (row > -1 && col > -1)
3087
calendar_invalidate_day (calendar, row, col);
3090
if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
3092
priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_NORMAL;
3093
calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
3096
if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
3098
priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_NORMAL;
3099
calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
3102
if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
3104
priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_NORMAL;
3105
calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
3108
if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
3110
priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_NORMAL;
3111
calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
3118
glom_gtk_calendar_scroll (GtkWidget *widget,
3119
GdkEventScroll *event)
3121
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
3123
if (event->direction == GDK_SCROLL_UP)
3125
if (!GTK_WIDGET_HAS_FOCUS (widget))
3126
gtk_widget_grab_focus (widget);
3127
calendar_set_month_prev (calendar);
3129
else if (event->direction == GDK_SCROLL_DOWN)
3131
if (!GTK_WIDGET_HAS_FOCUS (widget))
3132
gtk_widget_grab_focus (widget);
3133
calendar_set_month_next (calendar);
3142
/****************************************
3144
****************************************/
3147
move_focus (GlomGtkCalendar *calendar,
3150
GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
3152
if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
3153
(text_dir == GTK_TEXT_DIR_RTL && direction == 1))
3155
if (calendar->focus_col > 0)
3156
calendar->focus_col--;
3157
else if (calendar->focus_row > 0)
3159
calendar->focus_col = 6;
3160
calendar->focus_row--;
3163
if (calendar->focus_col < 0)
3164
calendar->focus_col = 6;
3165
if (calendar->focus_row < 0)
3166
calendar->focus_row = 5;
3170
if (calendar->focus_col < 6)
3171
calendar->focus_col++;
3172
else if (calendar->focus_row < 5)
3174
calendar->focus_col = 0;
3175
calendar->focus_row++;
3178
if (calendar->focus_col < 0)
3179
calendar->focus_col = 0;
3180
if (calendar->focus_row < 0)
3181
calendar->focus_row = 0;
3186
glom_gtk_calendar_key_press (GtkWidget *widget,
3189
GlomGtkCalendar *calendar;
3195
calendar = GLOM_GTK_CALENDAR (widget);
3198
old_focus_row = calendar->focus_row;
3199
old_focus_col = calendar->focus_col;
3201
switch (event->keyval)
3206
if (event->state & GDK_CONTROL_MASK)
3207
calendar_set_month_prev (calendar);
3210
move_focus (calendar, -1);
3211
calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3212
calendar_invalidate_day (calendar, calendar->focus_row,
3213
calendar->focus_col);
3219
if (event->state & GDK_CONTROL_MASK)
3220
calendar_set_month_next (calendar);
3223
move_focus (calendar, 1);
3224
calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3225
calendar_invalidate_day (calendar, calendar->focus_row,
3226
calendar->focus_col);
3232
if (event->state & GDK_CONTROL_MASK)
3233
calendar_set_year_prev (calendar);
3236
if (calendar->focus_row > 0)
3237
calendar->focus_row--;
3238
if (calendar->focus_row < 0)
3239
calendar->focus_row = 5;
3240
if (calendar->focus_col < 0)
3241
calendar->focus_col = 6;
3242
calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3243
calendar_invalidate_day (calendar, calendar->focus_row,
3244
calendar->focus_col);
3250
if (event->state & GDK_CONTROL_MASK)
3251
calendar_set_year_next (calendar);
3254
if (calendar->focus_row < 5)
3255
calendar->focus_row++;
3256
if (calendar->focus_col < 0)
3257
calendar->focus_col = 0;
3258
calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3259
calendar_invalidate_day (calendar, calendar->focus_row,
3260
calendar->focus_col);
3265
row = calendar->focus_row;
3266
col = calendar->focus_col;
3268
if (row > -1 && col > -1)
3272
day = calendar->day[row][col];
3273
if (calendar->day_month[row][col] == MONTH_PREV)
3274
calendar_set_month_prev (calendar);
3275
else if (calendar->day_month[row][col] == MONTH_NEXT)
3276
calendar_set_month_next (calendar);
3278
calendar_select_and_focus_day (calendar, day);
3286
/****************************************
3287
* Misc widget methods *
3288
****************************************/
3291
calendar_set_background (GtkWidget *widget)
3293
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
3296
if (GTK_WIDGET_REALIZED (widget))
3298
for (i = 0; i < 4; i++)
3300
if (priv->arrow_win[i])
3301
gdk_window_set_background (priv->arrow_win[i],
3302
HEADER_BG_COLOR (widget));
3304
if (priv->header_win)
3305
gdk_window_set_background (priv->header_win,
3306
HEADER_BG_COLOR (widget));
3307
if (priv->day_name_win)
3308
gdk_window_set_background (priv->day_name_win,
3309
BACKGROUND_COLOR (widget));
3311
gdk_window_set_background (priv->week_win,
3312
BACKGROUND_COLOR (widget));
3314
gdk_window_set_background (priv->main_win,
3315
BACKGROUND_COLOR (widget));
3317
gdk_window_set_background (widget->window,
3318
BACKGROUND_COLOR (widget));
3323
glom_gtk_calendar_style_set (GtkWidget *widget,
3324
GtkStyle *previous_style)
3326
if (previous_style && GTK_WIDGET_REALIZED (widget))
3327
calendar_set_background (widget);
3331
glom_gtk_calendar_state_changed (GtkWidget *widget,
3332
GtkStateType previous_state)
3334
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
3335
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
3338
if (!GTK_WIDGET_IS_SENSITIVE (widget))
3341
calendar_stop_spinning (calendar);
3344
for (i = 0; i < 4; i++)
3345
if (GTK_WIDGET_IS_SENSITIVE (widget))
3346
priv->arrow_state[i] = GTK_STATE_NORMAL;
3348
priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
3350
calendar_set_background (widget);
3354
glom_gtk_calendar_grab_notify (GtkWidget *widget,
3355
gboolean was_grabbed)
3358
calendar_stop_spinning (GLOM_GTK_CALENDAR (widget));
3362
glom_gtk_calendar_focus_out (GtkWidget *widget,
3363
GdkEventFocus *event)
3365
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
3366
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
3368
calendar_queue_refresh (calendar);
3369
calendar_stop_spinning (calendar);
3377
/****************************************
3379
****************************************/
3382
glom_gtk_calendar_drag_data_get (GtkWidget *widget,
3383
GdkDragContext *context,
3384
GtkSelectionData *selection_data,
3388
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
3393
date = g_date_new_dmy (calendar->selected_day, calendar->month + 1, calendar->year);
3394
len = g_date_strftime (str, 127, "%x", date);
3395
gtk_selection_data_set_text (selection_data, str, len);
3400
/* Get/set whether drag_motion requested the drag data and
3401
* drag_data_received should thus not actually insert the data,
3402
* since the data doesn't result from a drop.
3405
set_status_pending (GdkDragContext *context,
3406
GdkDragAction suggested_action)
3408
g_object_set_data (G_OBJECT (context),
3409
I_("gtk-calendar-status-pending"),
3410
GINT_TO_POINTER (suggested_action));
3413
static GdkDragAction
3414
get_status_pending (GdkDragContext *context)
3416
return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
3417
"gtk-calendar-status-pending"));
3421
glom_gtk_calendar_drag_leave (GtkWidget *widget,
3422
GdkDragContext *context,
3425
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
3427
priv->drag_highlight = 0;
3428
gtk_drag_unhighlight (widget);
3433
glom_gtk_calendar_drag_motion (GtkWidget *widget,
3434
GdkDragContext *context,
3439
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
3442
if (!priv->drag_highlight)
3444
priv->drag_highlight = 1;
3445
gtk_drag_highlight (widget);
3448
target = gtk_drag_dest_find_target (widget, context, NULL);
3449
if (target == GDK_NONE || context->suggested_action == 0)
3450
gdk_drag_status (context, 0, time);
3453
set_status_pending (context, context->suggested_action);
3454
gtk_drag_get_data (widget, context, target, time);
3461
glom_gtk_calendar_drag_drop (GtkWidget *widget,
3462
GdkDragContext *context,
3469
target = gtk_drag_dest_find_target (widget, context, NULL);
3470
if (target != GDK_NONE)
3472
gtk_drag_get_data (widget, context,
3482
glom_gtk_calendar_drag_data_received (GtkWidget *widget,
3483
GdkDragContext *context,
3486
GtkSelectionData *selection_data,
3490
GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
3491
guint day, month, year;
3494
GdkDragAction suggested_action;
3496
suggested_action = get_status_pending (context);
3498
if (suggested_action)
3500
set_status_pending (context, 0);
3502
/* We are getting this data due to a request in drag_motion,
3503
* rather than due to a request in drag_drop, so we are just
3504
* supposed to call drag_status, not actually paste in the
3507
str = (gchar*) gtk_selection_data_get_text (selection_data);
3511
date = g_date_new ();
3512
g_date_set_parse (date, str);
3513
if (!g_date_valid (date))
3514
suggested_action = 0;
3519
suggested_action = 0;
3521
gdk_drag_status (context, suggested_action, time);
3526
date = g_date_new ();
3527
str = (gchar*) gtk_selection_data_get_text (selection_data);
3530
g_date_set_parse (date, str);
3534
if (!g_date_valid (date))
3536
g_warning ("Received invalid date data\n");
3538
gtk_drag_finish (context, FALSE, FALSE, time);
3542
day = g_date_get_day (date);
3543
month = g_date_get_month (date);
3544
year = g_date_get_year (date);
3547
gtk_drag_finish (context, TRUE, FALSE, time);
3550
g_object_freeze_notify (G_OBJECT (calendar));
3551
if (!(calendar->display_flags & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
3552
&& (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_HEADING))
3553
glom_gtk_calendar_select_month (calendar, month - 1, year);
3554
glom_gtk_calendar_select_day (calendar, day);
3555
g_object_thaw_notify (G_OBJECT (calendar));
3559
/****************************************
3561
****************************************/
3564
* glom_gtk_calendar_new:
3566
* Creates a new calendar, with the current date being selected.
3568
* Return value: a newly #GlomGtkCalendar widget
3571
glom_gtk_calendar_new (void)
3573
return g_object_new (GLOM_GTK_TYPE_CALENDAR, NULL);
3577
* glom_gtk_calendar_display_options:
3578
* @calendar: a #GlomGtkCalendar.
3579
* @flags: the display options to set.
3581
* Sets display options (whether to display the heading and the month headings).
3583
* Deprecated: 2.4: Use glom_gtk_calendar_set_display_options() instead
3586
glom_gtk_calendar_display_options (GlomGtkCalendar *calendar,
3587
GlomGtkCalendarDisplayOptions flags)
3589
glom_gtk_calendar_set_display_options (calendar, flags);
3593
* glom_gtk_calendar_get_display_options:
3594
* @calendar: a #GlomGtkCalendar
3596
* Returns the current display options of @calendar.
3598
* Return value: the display options.
3602
GlomGtkCalendarDisplayOptions
3603
glom_gtk_calendar_get_display_options (GlomGtkCalendar *calendar)
3605
g_return_val_if_fail (GLOM_GTK_IS_CALENDAR (calendar), 0);
3607
return calendar->display_flags;
3611
* glom_gtk_calendar_set_display_options:
3612
* @calendar: a #GlomGtkCalendar
3613
* @flags: the display options to set
3615
* Sets display options (whether to display the heading and the month
3621
glom_gtk_calendar_set_display_options (GlomGtkCalendar *calendar,
3622
GlomGtkCalendarDisplayOptions flags)
3624
GtkWidget *widget = GTK_WIDGET (calendar);
3625
GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
3628
GlomGtkCalendarDisplayOptions old_flags;
3630
g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
3632
old_flags = calendar->display_flags;
3634
if (GTK_WIDGET_REALIZED (widget))
3636
if ((flags ^ calendar->display_flags) & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
3639
if (! (flags & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
3640
&& (priv->header_win))
3642
calendar->display_flags &= ~GLOM_GTK_CALENDAR_NO_MONTH_CHANGE;
3643
calendar_realize_arrows (calendar);
3647
for (i = 0; i < 4; i++)
3649
if (priv->arrow_win[i])
3651
gdk_window_set_user_data (priv->arrow_win[i],
3653
gdk_window_destroy (priv->arrow_win[i]);
3654
priv->arrow_win[i] = NULL;
3660
if ((flags ^ calendar->display_flags) & GLOM_GTK_CALENDAR_SHOW_HEADING)
3664
if (flags & GLOM_GTK_CALENDAR_SHOW_HEADING)
3666
calendar->display_flags |= GLOM_GTK_CALENDAR_SHOW_HEADING;
3667
calendar_realize_header (calendar);
3671
for (i = 0; i < 4; i++)
3673
if (priv->arrow_win[i])
3675
gdk_window_set_user_data (priv->arrow_win[i],
3677
gdk_window_destroy (priv->arrow_win[i]);
3678
priv->arrow_win[i] = NULL;
3681
gdk_window_set_user_data (priv->header_win, NULL);
3682
gdk_window_destroy (priv->header_win);
3683
priv->header_win = NULL;
3688
if ((flags ^ calendar->display_flags) & GLOM_GTK_CALENDAR_SHOW_DAY_NAMES)
3692
if (flags & GLOM_GTK_CALENDAR_SHOW_DAY_NAMES)
3694
calendar->display_flags |= GLOM_GTK_CALENDAR_SHOW_DAY_NAMES;
3695
calendar_realize_day_names (calendar);
3699
gdk_window_set_user_data (priv->day_name_win, NULL);
3700
gdk_window_destroy (priv->day_name_win);
3701
priv->day_name_win = NULL;
3705
if ((flags ^ calendar->display_flags) & GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3709
if (flags & GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3711
calendar->display_flags |= GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS;
3712
calendar_realize_week_numbers (calendar);
3716
gdk_window_set_user_data (priv->week_win, NULL);
3717
gdk_window_destroy (priv->week_win);
3718
priv->week_win = NULL;
3722
if ((flags ^ calendar->display_flags) & GLOM_GTK_CALENDAR_WEEK_START_MONDAY)
3723
g_warning ("GLOM_GTK_CALENDAR_WEEK_START_MONDAY is ignored; the first day of the week is determined from the locale");
3725
if ((flags ^ calendar->display_flags) & GLOM_GTK_CALENDAR_SHOW_DETAILS)
3728
calendar->display_flags = flags;
3730
gtk_widget_queue_resize (GTK_WIDGET (calendar));
3734
calendar->display_flags = flags;
3736
g_object_freeze_notify (G_OBJECT (calendar));
3737
if ((old_flags ^ calendar->display_flags) & GLOM_GTK_CALENDAR_SHOW_HEADING)
3738
g_object_notify (G_OBJECT (calendar), "show-heading");
3739
if ((old_flags ^ calendar->display_flags) & GLOM_GTK_CALENDAR_SHOW_DAY_NAMES)
3740
g_object_notify (G_OBJECT (calendar), "show-day-names");
3741
if ((old_flags ^ calendar->display_flags) & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
3742
g_object_notify (G_OBJECT (calendar), "no-month-change");
3743
if ((old_flags ^ calendar->display_flags) & GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3744
g_object_notify (G_OBJECT (calendar), "show-week-numbers");
3745
g_object_thaw_notify (G_OBJECT (calendar));
3749
* glom_gtk_calendar_select_month:
3750
* @calendar: a #GlomGtkCalendar
3751
* @month: a month number between 0 and 11.
3752
* @year: the year the month is in.
3754
* Shifts the calendar to a different month.
3756
* Return value: %TRUE, always
3759
glom_gtk_calendar_select_month (GlomGtkCalendar *calendar,
3763
g_return_val_if_fail (GLOM_GTK_IS_CALENDAR (calendar), FALSE);
3764
g_return_val_if_fail (month <= 11, FALSE);
3766
calendar->month = month;
3767
calendar->year = year;
3769
calendar_compute_days (calendar);
3770
calendar_queue_refresh (calendar);
3772
g_object_freeze_notify (G_OBJECT (calendar));
3773
g_object_notify (G_OBJECT (calendar), "month");
3774
g_object_notify (G_OBJECT (calendar), "year");
3775
g_object_thaw_notify (G_OBJECT (calendar));
3777
g_signal_emit (calendar,
3778
glom_gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
3784
* glom_gtk_calendar_select_day:
3785
* @calendar: a #GlomGtkCalendar.
3786
* @day: the day number between 1 and 31, or 0 to unselect
3787
* the currently selected day.
3789
* Selects a day from the current month.
3792
glom_gtk_calendar_select_day (GlomGtkCalendar *calendar,
3795
g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
3796
g_return_if_fail (day <= 31);
3798
/* Deselect the old day */
3799
if (calendar->selected_day > 0)
3803
selected_day = calendar->selected_day;
3804
calendar->selected_day = 0;
3805
if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3806
calendar_invalidate_day_num (calendar, selected_day);
3809
calendar->selected_day = day;
3811
/* Select the new day */
3814
if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3815
calendar_invalidate_day_num (calendar, day);
3818
g_object_notify (G_OBJECT (calendar), "day");
3820
g_signal_emit (calendar,
3821
glom_gtk_calendar_signals[DAY_SELECTED_SIGNAL],
3826
* glom_gtk_calendar_clear_marks:
3827
* @calendar: a #GlomGtkCalendar
3829
* Remove all visual markers.
3832
glom_gtk_calendar_clear_marks (GlomGtkCalendar *calendar)
3836
g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
3838
for (day = 0; day < 31; day++)
3840
calendar->marked_date[day] = FALSE;
3843
calendar->num_marked_dates = 0;
3844
calendar_queue_refresh (calendar);
3848
* glom_gtk_calendar_mark_day:
3849
* @calendar: a #GlomGtkCalendar
3850
* @day: the day number to mark between 1 and 31.
3852
* Places a visual marker on a particular day.
3854
* Return value: %TRUE, always
3857
glom_gtk_calendar_mark_day (GlomGtkCalendar *calendar,
3860
g_return_val_if_fail (GLOM_GTK_IS_CALENDAR (calendar), FALSE);
3862
if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == FALSE)
3864
calendar->marked_date[day - 1] = TRUE;
3865
calendar->num_marked_dates++;
3866
calendar_invalidate_day_num (calendar, day);
3873
* glom_gtk_calendar_unmark_day:
3874
* @calendar: a #GlomGtkCalendar.
3875
* @day: the day number to unmark between 1 and 31.
3877
* Removes the visual marker from a particular day.
3879
* Return value: %TRUE, always
3882
glom_gtk_calendar_unmark_day (GlomGtkCalendar *calendar,
3885
g_return_val_if_fail (GLOM_GTK_IS_CALENDAR (calendar), FALSE);
3887
if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == TRUE)
3889
calendar->marked_date[day - 1] = FALSE;
3890
calendar->num_marked_dates--;
3891
calendar_invalidate_day_num (calendar, day);
3898
* glom_gtk_calendar_get_date:
3899
* @calendar: a #GlomGtkCalendar
3900
* @year: location to store the year number, or %NULL
3901
* @month: location to store the month number (between 0 and 11), or %NULL
3902
* @day: location to store the day number (between 1 and 31), or %NULL
3904
* Obtains the selected date from a #GlomGtkCalendar.
3907
glom_gtk_calendar_get_date (GlomGtkCalendar *calendar,
3912
g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
3915
*year = calendar->year;
3918
*month = calendar->month;
3921
*day = calendar->selected_day;
3925
* glom_gtk_calendar_set_detail_func:
3926
* @calendar: a #GlomGtkCalendar.
3927
* @func: a function providing details for each day.
3928
* @data: data to pass to @func invokations.
3929
* @destroy: a function for releasing @data.
3931
* Installs a function which provides Pango markup with detail information
3932
* for each day. Examples for such details are holidays or appointments. That
3933
* information is shown below each day when #GlomGtkCalendar:show-details is set.
3934
* A tooltip containing with full detail information is provided, if the entire
3935
* text should not fit into the details area, or if #GlomGtkCalendar:show-details
3938
* The size of the details area can be restricted by setting the
3939
* #GlomGtkCalendar:detail-width-chars and #GlomGtkCalendar:detail-height-rows
3945
glom_gtk_calendar_set_detail_func (GlomGtkCalendar *calendar,
3946
GlomGtkCalendarDetailFunc func,
3948
GDestroyNotify destroy)
3950
GlomGtkCalendarPrivate *priv;
3952
g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
3954
priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
3956
if (priv->detail_func_destroy)
3957
priv->detail_func_destroy (priv->detail_func_user_data);
3959
priv->detail_func = func;
3960
priv->detail_func_user_data = data;
3961
priv->detail_func_destroy = destroy;
3963
gtk_widget_set_has_tooltip (GTK_WIDGET (calendar),
3964
NULL != priv->detail_func);
3965
gtk_widget_queue_resize (GTK_WIDGET (calendar));
3969
* glom_gtk_calendar_set_detail_width_chars:
3970
* @calendar: a #GlomGtkCalendar.
3971
* @chars: detail width in characters.
3973
* Updates the width of detail cells.
3974
* See #GlomGtkCalendar:detail-width-chars.
3979
glom_gtk_calendar_set_detail_width_chars (GlomGtkCalendar *calendar,
3982
GlomGtkCalendarPrivate *priv;
3984
g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
3986
priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
3988
if (chars != priv->detail_width_chars)
3990
priv->detail_width_chars = chars;
3991
g_object_notify (G_OBJECT (calendar), "detail-width-chars");
3992
gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
3997
* glom_gtk_calendar_set_detail_height_rows:
3998
* @calendar: a #GlomGtkCalendar.
3999
* @rows: detail height in rows.
4001
* Updates the height of detail cells.
4002
* See #GlomGtkCalendar:detail-height-rows.
4007
glom_gtk_calendar_set_detail_height_rows (GlomGtkCalendar *calendar,
4010
GlomGtkCalendarPrivate *priv;
4012
g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
4014
priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
4016
if (rows != priv->detail_height_rows)
4018
priv->detail_height_rows = rows;
4019
g_object_notify (G_OBJECT (calendar), "detail-height-rows");
4020
gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
4025
* glom_gtk_calendar_get_detail_width_chars:
4026
* @calendar: a #GlomGtkCalendar.
4028
* Queries the width of detail cells, in characters.
4029
* See #GlomGtkCalendar:detail-width-chars.
4033
* Return value: The width of detail cells, in characters.
4036
glom_gtk_calendar_get_detail_width_chars (GlomGtkCalendar *calendar)
4038
g_return_val_if_fail (GLOM_GTK_IS_CALENDAR (calendar), 0);
4039
return GLOM_GTK_CALENDAR_GET_PRIVATE (calendar)->detail_width_chars;
4043
* glom_gtk_calendar_get_detail_height_rows:
4044
* @calendar: a #GlomGtkCalendar.
4046
* Queries the height of detail cells, in rows.
4047
* See #GlomGtkCalendar:detail-width-chars.
4051
* Return value: The height of detail cells, in rows.
4054
glom_gtk_calendar_get_detail_height_rows (GlomGtkCalendar *calendar)
4056
g_return_val_if_fail (GLOM_GTK_IS_CALENDAR (calendar), 0);
4057
return GLOM_GTK_CALENDAR_GET_PRIVATE (calendar)->detail_height_rows;
4061
* glom_gtk_calendar_freeze:
4062
* @calendar: a #GlomGtkCalendar
4064
* Does nothing. Previously locked the display of the calendar until
4065
* it was thawed with glom_gtk_calendar_thaw().
4070
glom_gtk_calendar_freeze (GlomGtkCalendar *calendar)
4072
g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
4076
* glom_gtk_calendar_thaw:
4077
* @calendar: a #GlomGtkCalendar
4079
* Does nothing. Previously defrosted a calendar; all the changes made
4080
* since the last glom_gtk_calendar_freeze() were displayed.
4085
glom_gtk_calendar_thaw (GlomGtkCalendar *calendar)
4087
g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
4090
#endif //!GTK_CHECK_VERSION(2,16,0)