~ubuntu-branches/ubuntu/quantal/glom/quantal

« back to all changes in this revision

Viewing changes to glom/utility_widgets/calendar/glomgtkcalendar.c

  • Committer: Bazaar Package Importer
  • Author(s): Iain Lane
  • Date: 2009-01-11 17:12:01 UTC
  • mfrom: (1.1.39 upstream)
  • Revision ID: james.westby@ubuntu.com-20090111171201-0ov9zh1fxfueshxc
Tags: 1.8.5-0ubuntu1
* New upstream release (LP: #256701), fixes LP bugs: 
  + Clear the text in property dialogs for text items (LP: #309147)
  + Don't crash when showing and then hiding the grid (LP: #303453)
  + Temporarily remember the sort order so it is the same when navigating 
    away and back (LP: #303422)
  + Use the list's sort order for the top-level records in the report 
    (LP: #303425)
  + Users/Groups: Disable drag-and-drop for the treeview, because it is
    useless and confusing (LP: #299573)
  + Import: Sort the fields list alphabetically (LP: #306593)
  + delete primary key make unusuable the database (LP: #299549)
  + Spanish translate incomplete (LP: #299556)
  + import date format error (LP: #299591)
  + can't delete data from table list view (LP: #299853)
  + Field definition: default value not saved (LP: #299896)
  + reports crashing (LP: #300054)
  + Year error with date fields (LP: #300057)
  + list view: 2 records added instead of 1 (LP: #300819)
* debian/control: Refreshed dependencies for libglom-dev.
* debian/control: Updated build-deps to match configure checks. goocavnasmm
  build-dep bumped to version without .la files.
* debian/rules: Don't delete the directory containing the templates.
* debian/control, debian/rules, debian/glom-doc.*: Split out
  arch-independent manual and examples into separate package glom-doc, which
  is now recommended by glom (glom will still work if they're not available)
* debian/copyright: Rewritten to new machine-readable format. 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.
 
4
 */
 
5
#include <gtk/gtk.h>
 
6
#if !GTK_CHECK_VERSION(2,16,0)
 
7
 
 
8
#include <config.h>
 
9
 
 
10
#ifdef HAVE_SYS_TIME_H
 
11
#include <sys/time.h>
 
12
#endif
 
13
#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
 
14
#include <langinfo.h>
 
15
#endif
 
16
#include <string.h>
 
17
#include <stdlib.h>
 
18
#include <time.h>
 
19
 
 
20
#include <glib.h>
 
21
 
 
22
#ifdef G_OS_WIN32
 
23
#include <windows.h>
 
24
#endif
 
25
 
 
26
#undef GTK_DISABLE_DEPRECATED
 
27
#include "glomgtkcalendar.h"
 
28
 
 
29
#include <gtk/gtkdnd.h>
 
30
 
 
31
#include <gtk/gtkmain.h>
 
32
#include <gdk/gdkkeysyms.h>
 
33
 
 
34
 
 
35
/* #include "gtkintl.h" */
 
36
#include <glib/gi18n-lib.h>
 
37
 
 
38
#ifdef ENABLE_NLS
 
39
#define P_(String) dgettext(GETTEXT_PACKAGE "-properties",String)
 
40
#else 
 
41
#define P_(String) (String)
 
42
#endif
 
43
 
 
44
/* not really I18N-related, but also a string marker macro */
 
45
#define I_(string) g_intern_static_string (string)
 
46
 
 
47
 
 
48
 
 
49
/* #include "gtkmarshalers.h" */
 
50
#define _gtk_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID
 
51
 
 
52
 
 
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
 
57
 
 
58
/* GTK - The GIMP Toolkit
 
59
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 
60
 *
 
61
 * GTK Calendar Widget
 
62
 * Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson and Mattias Groenlund
 
63
 * 
 
64
 * lib_date routines
 
65
 * Copyright (c) 1995, 1996, 1997, 1998 by Steffen Beyer
 
66
 *
 
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.
 
71
 *
 
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.
 
76
 *
 
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.
 
80
 */
 
81
 
 
82
/*
 
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/. 
 
87
 */
 
88
 
 
89
#include <config.h>
 
90
 
 
91
#ifdef HAVE_SYS_TIME_H
 
92
#include <sys/time.h>
 
93
#endif
 
94
#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
 
95
#include <langinfo.h>
 
96
#endif
 
97
#include <string.h>
 
98
#include <stdlib.h>
 
99
#include <time.h>
 
100
 
 
101
#include <glib.h>
 
102
 
 
103
#ifdef G_OS_WIN32
 
104
#include <windows.h>
 
105
#endif
 
106
 
 
107
#undef GTK_DISABLE_DEPRECATED
 
108
#include "glomgtkcalendar.h"
 
109
 
 
110
#include <gtk/gtktooltip.h>
 
111
 
 
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
 
115
 * in win32 headers.
 
116
 */
 
117
 
 
118
static const guint month_length[2][13] =
 
119
{
 
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 }
 
122
};
 
123
 
 
124
static const guint days_in_months[2][14] =
 
125
{
 
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 }
 
128
};
 
129
 
 
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);
 
135
 
 
136
static gboolean 
 
137
leap (guint year)
 
138
{
 
139
  return((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0));
 
140
}
 
141
 
 
142
static guint 
 
143
day_of_week (guint year, guint mm, guint dd)
 
144
{
 
145
  glong  days;
 
146
  
 
147
  days = calc_days(year, mm, dd);
 
148
  if (days > 0L)
 
149
    {
 
150
      days--;
 
151
      days %= 7L;
 
152
      days++;
 
153
    }
 
154
  return( (guint) days );
 
155
}
 
156
 
 
157
static guint weeks_in_year(guint year)
 
158
{
 
159
  return(52 + ((day_of_week(year,1,1)==4) || (day_of_week(year,12,31)==4)));
 
160
}
 
161
 
 
162
static gboolean 
 
163
check_date(guint year, guint mm, guint dd)
 
164
{
 
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;
 
168
  return TRUE;
 
169
}
 
170
 
 
171
static guint 
 
172
week_number(guint year, guint mm, guint dd)
 
173
{
 
174
  guint first;
 
175
  
 
176
  first = day_of_week(year,1,1) - 1;
 
177
  return( (guint) ( (dates_difference(year,1,1, year,mm,dd) + first) / 7L ) +
 
178
          (first < 4) );
 
179
}
 
180
 
 
181
static glong 
 
182
year_to_days(guint year)
 
183
{
 
184
  return( year * 365L + (year / 4) - (year / 100) + (year / 400) );
 
185
}
 
186
 
 
187
 
 
188
static glong 
 
189
calc_days(guint year, guint mm, guint dd)
 
190
{
 
191
  gboolean lp;
 
192
  
 
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 );
 
197
}
 
198
 
 
199
static gboolean 
 
200
week_of_year(guint *week, guint *year, guint mm, guint dd)
 
201
{
 
202
  if (check_date(*year,mm,dd))
 
203
    {
 
204
      *week = week_number(*year,mm,dd);
 
205
      if (*week == 0) 
 
206
        *week = weeks_in_year(--(*year));
 
207
      else if (*week > weeks_in_year(*year))
 
208
        {
 
209
          *week = 1;
 
210
          (*year)++;
 
211
        }
 
212
      return TRUE;
 
213
    }
 
214
  return FALSE;
 
215
}
 
216
 
 
217
static glong 
 
218
dates_difference(guint year1, guint mm1, guint dd1,
 
219
                 guint year2, guint mm2, guint dd2)
 
220
{
 
221
  return( calc_days(year2, mm2, dd2) - calc_days(year1, mm1, dd1) );
 
222
}
 
223
 
 
224
/*** END OF lib_date routines ********************************************/
 
225
 
 
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
 
234
 
 
235
#define DAY_XSEP                 0 /* not really good for small calendar */
 
236
#define DAY_YSEP                 0 /* not really good for small calendar */
 
237
 
 
238
#define SCROLL_DELAY_FACTOR      5
 
239
 
 
240
/* Color usage */
 
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)])
 
251
 
 
252
enum {
 
253
  ARROW_YEAR_LEFT,
 
254
  ARROW_YEAR_RIGHT,
 
255
  ARROW_MONTH_LEFT,
 
256
  ARROW_MONTH_RIGHT
 
257
};
 
258
 
 
259
enum {
 
260
  MONTH_PREV,
 
261
  MONTH_CURRENT,
 
262
  MONTH_NEXT
 
263
};
 
264
 
 
265
enum {
 
266
  MONTH_CHANGED_SIGNAL,
 
267
  DAY_SELECTED_SIGNAL,
 
268
  DAY_SELECTED_DOUBLE_CLICK_SIGNAL,
 
269
  PREV_MONTH_SIGNAL,
 
270
  NEXT_MONTH_SIGNAL,
 
271
  PREV_YEAR_SIGNAL,
 
272
  NEXT_YEAR_SIGNAL,
 
273
  LAST_SIGNAL
 
274
};
 
275
 
 
276
enum
 
277
{
 
278
  PROP_0,
 
279
  PROP_YEAR,
 
280
  PROP_MONTH,
 
281
  PROP_DAY,
 
282
  PROP_SHOW_HEADING,
 
283
  PROP_SHOW_DAY_NAMES,
 
284
  PROP_NO_MONTH_CHANGE,
 
285
  PROP_SHOW_WEEK_NUMBERS,
 
286
  PROP_SHOW_DETAILS,
 
287
  PROP_DETAIL_WIDTH_CHARS,
 
288
  PROP_DETAIL_HEIGHT_ROWS,
 
289
  PROP_LAST
 
290
};
 
291
 
 
292
static guint glom_gtk_calendar_signals[LAST_SIGNAL] = { 0 };
 
293
 
 
294
struct _GlomGtkCalendarPrivate
 
295
{
 
296
  GdkWindow *header_win;
 
297
  GdkWindow *day_name_win;
 
298
  GdkWindow *main_win;
 
299
  GdkWindow *week_win;
 
300
  GdkWindow *arrow_win[4];
 
301
 
 
302
  guint header_h;
 
303
  guint day_name_h;
 
304
  guint main_h;
 
305
 
 
306
  guint      arrow_state[4];
 
307
  guint      arrow_width;
 
308
  guint      max_month_width;
 
309
  guint      max_year_width;
 
310
  
 
311
  guint day_width;
 
312
  guint week_width;
 
313
 
 
314
  guint min_day_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;
 
321
  
 
322
  /* flags */
 
323
  guint year_before : 1;
 
324
 
 
325
  guint need_timer  : 1;
 
326
 
 
327
  guint in_drag : 1;
 
328
  guint drag_highlight : 1;
 
329
 
 
330
  guint32 timer;
 
331
  gint click_child;
 
332
 
 
333
  gint week_start;
 
334
 
 
335
  gint drag_start_x;
 
336
  gint drag_start_y;
 
337
 
 
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;
 
342
 
 
343
  /* Size requistion for details provided by the hook. */
 
344
  gint detail_height_rows;
 
345
  gint detail_width_chars;
 
346
  gint detail_overflow[6];
 
347
};
 
348
 
 
349
#define GLOM_GTK_CALENDAR_GET_PRIVATE(widget)  (GLOM_GTK_CALENDAR (widget)->priv)
 
350
 
 
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,
 
354
                                       guint         prop_id,
 
355
                                       const GValue *value,
 
356
                                       GParamSpec   *pspec);
 
357
static void glom_gtk_calendar_get_property (GObject      *object,
 
358
                                       guint         prop_id,
 
359
                                       GValue       *value,
 
360
                                       GParamSpec   *pspec);
 
361
 
 
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,
 
383
                                             GdkEventKey      *event);
 
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,
 
393
                                             gint              x,
 
394
                                             gint              y,
 
395
                                             gboolean          keyboard_mode,
 
396
                                             GtkTooltip       *tooltip);
 
397
 
 
398
static void     glom_gtk_calendar_drag_data_get      (GtkWidget        *widget,
 
399
                                                 GdkDragContext   *context,
 
400
                                                 GtkSelectionData *selection_data,
 
401
                                                 guint             info,
 
402
                                                 guint             time);
 
403
static void     glom_gtk_calendar_drag_data_received (GtkWidget        *widget,
 
404
                                                 GdkDragContext   *context,
 
405
                                                 gint              x,
 
406
                                                 gint              y,
 
407
                                                 GtkSelectionData *selection_data,
 
408
                                                 guint             info,
 
409
                                                 guint             time);
 
410
static gboolean glom_gtk_calendar_drag_motion        (GtkWidget        *widget,
 
411
                                                 GdkDragContext   *context,
 
412
                                                 gint              x,
 
413
                                                 gint              y,
 
414
                                                 guint             time);
 
415
static void     glom_gtk_calendar_drag_leave         (GtkWidget        *widget,
 
416
                                                 GdkDragContext   *context,
 
417
                                                 guint             time);
 
418
static gboolean glom_gtk_calendar_drag_drop          (GtkWidget        *widget,
 
419
                                                 GdkDragContext   *context,
 
420
                                                 gint              x,
 
421
                                                 gint              y,
 
422
                                                 guint             time);
 
423
 
 
424
static void calendar_start_spinning (GlomGtkCalendar *calendar,
 
425
                                     gint         click_child);
 
426
static void calendar_stop_spinning  (GlomGtkCalendar *calendar);
 
427
 
 
428
static void calendar_invalidate_day     (GlomGtkCalendar *widget,
 
429
                                         gint       row,
 
430
                                         gint       col);
 
431
static void calendar_invalidate_day_num (GlomGtkCalendar *widget,
 
432
                                         gint       day);
 
433
static void calendar_invalidate_arrow   (GlomGtkCalendar *widget,
 
434
                                         guint      arrow);
 
435
 
 
436
static void calendar_compute_days      (GlomGtkCalendar *calendar);
 
437
     
 
438
static char    *default_abbreviated_dayname[7];
 
439
static char    *default_monthname[12];
 
440
 
 
441
G_DEFINE_TYPE (GlomGtkCalendar, glom_gtk_calendar, GTK_TYPE_WIDGET)
 
442
 
 
443
static void
 
444
glom_gtk_calendar_class_init (GlomGtkCalendarClass *class)
 
445
{
 
446
  GObjectClass   *gobject_class;
 
447
  GtkObjectClass   *object_class;
 
448
  GtkWidgetClass *widget_class;
 
449
 
 
450
  gobject_class = (GObjectClass*)  class;
 
451
  object_class = (GtkObjectClass*)  class;
 
452
  widget_class = (GtkWidgetClass*) class;
 
453
  
 
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;
 
457
 
 
458
  object_class->destroy = glom_gtk_calendar_destroy;
 
459
 
 
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;
 
477
 
 
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;
 
483
  
 
484
  /**
 
485
   * GlomGtkCalendar:year:
 
486
   *
 
487
   * The selected year. 
 
488
   * This property gets initially set to the current year.
 
489
   */  
 
490
  g_object_class_install_property (gobject_class,
 
491
                                   PROP_YEAR,
 
492
                                   g_param_spec_int ("year",
 
493
                                                     P_("Year"),
 
494
                                                     P_("The selected year"),
 
495
                                                     0, G_MAXINT, 0,
 
496
                                                     GTK_PARAM_READWRITE));
 
497
 
 
498
  /**
 
499
   * GlomGtkCalendar:month:
 
500
   *
 
501
   * The selected month (as a number between 0 and 11). 
 
502
   * This property gets initially set to the current month.
 
503
   */
 
504
  g_object_class_install_property (gobject_class,
 
505
                                   PROP_MONTH,
 
506
                                   g_param_spec_int ("month",
 
507
                                                     P_("Month"),
 
508
                                                     P_("The selected month (as a number between 0 and 11)"),
 
509
                                                     0, 11, 0,
 
510
                                                     GTK_PARAM_READWRITE));
 
511
 
 
512
  /**
 
513
   * GlomGtkCalendar:day:
 
514
   *
 
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.
 
518
   */
 
519
  g_object_class_install_property (gobject_class,
 
520
                                   PROP_DAY,
 
521
                                   g_param_spec_int ("day",
 
522
                                                     P_("Day"),
 
523
                                                     P_("The selected day (as a number between 1 and 31, or 0 to unselect the currently selected day)"),
 
524
                                                     0, 31, 0,
 
525
                                                     GTK_PARAM_READWRITE));
 
526
 
 
527
/**
 
528
 * GlomGtkCalendar:show-heading:
 
529
 *
 
530
 * Determines whether a heading is displayed.
 
531
 *
 
532
 * Since: 2.4
 
533
 */
 
534
  g_object_class_install_property (gobject_class,
 
535
                                   PROP_SHOW_HEADING,
 
536
                                   g_param_spec_boolean ("show-heading",
 
537
                                                         P_("Show Heading"),
 
538
                                                         P_("If TRUE, a heading is displayed"),
 
539
                                                         TRUE,
 
540
                                                         GTK_PARAM_READWRITE));
 
541
 
 
542
/**
 
543
 * GlomGtkCalendar:show-day-names:
 
544
 *
 
545
 * Determines whether day names are displayed.
 
546
 *
 
547
 * Since: 2.4
 
548
 */
 
549
  g_object_class_install_property (gobject_class,
 
550
                                   PROP_SHOW_DAY_NAMES,
 
551
                                   g_param_spec_boolean ("show-day-names",
 
552
                                                         P_("Show Day Names"),
 
553
                                                         P_("If TRUE, day names are displayed"),
 
554
                                                         TRUE,
 
555
                                                         GTK_PARAM_READWRITE));
 
556
/**
 
557
 * GlomGtkCalendar:no-month-change:
 
558
 *
 
559
 * Determines whether the selected month can be changed.
 
560
 *
 
561
 * Since: 2.4
 
562
 */
 
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"),
 
568
                                                         FALSE,
 
569
                                                         GTK_PARAM_READWRITE));
 
570
 
 
571
/**
 
572
 * GlomGtkCalendar:show-week-numbers:
 
573
 *
 
574
 * Determines whether week numbers are displayed.
 
575
 *
 
576
 * Since: 2.4
 
577
 */
 
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"),
 
583
                                                         FALSE,
 
584
                                                         GTK_PARAM_READWRITE));
 
585
 
 
586
/**
 
587
 * GlomGtkCalendar:detail-width-chars:
 
588
 *
 
589
 * Width of a detail cell, in characters.
 
590
 * A value of 0 allows any width. See glom_gtk_calendar_set_detail_func().
 
591
 *
 
592
 * Since: 2.16
 
593
 */
 
594
  g_object_class_install_property (gobject_class,
 
595
                                   PROP_DETAIL_WIDTH_CHARS,
 
596
                                   g_param_spec_int ("detail-width-chars",
 
597
                                                     P_("Details Width"),
 
598
                                                     P_("Details width in characters"),
 
599
                                                     0, 127, 0,
 
600
                                                     GTK_PARAM_READWRITE));
 
601
 
 
602
/**
 
603
 * GlomGtkCalendar:detail-height-rows:
 
604
 *
 
605
 * Height of a detail cell, in rows.
 
606
 * A value of 0 allows any width. See glom_gtk_calendar_set_detail_func().
 
607
 *
 
608
 * Since: 2.16
 
609
 */
 
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"),
 
615
                                                     0, 127, 0,
 
616
                                                     GTK_PARAM_READWRITE));
 
617
 
 
618
/**
 
619
 * GlomGtkCalendar:show-details:
 
620
 *
 
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
 
623
 * marked.
 
624
 *
 
625
 * Since: 2.16
 
626
 */
 
627
  g_object_class_install_property (gobject_class,
 
628
                                   PROP_SHOW_DETAILS,
 
629
                                   g_param_spec_boolean ("show-details",
 
630
                                                         P_("Show Details"),
 
631
                                                         P_("If TRUE, details are shown"),
 
632
                                                         TRUE,
 
633
                                                         GTK_PARAM_READWRITE));
 
634
 
 
635
  glom_gtk_calendar_signals[MONTH_CHANGED_SIGNAL] =
 
636
    g_signal_new (I_("month_changed"),
 
637
                  G_OBJECT_CLASS_TYPE (gobject_class),
 
638
                  G_SIGNAL_RUN_FIRST,
 
639
                  G_STRUCT_OFFSET (GlomGtkCalendarClass, month_changed),
 
640
                  NULL, NULL,
 
641
                  _gtk_marshal_VOID__VOID,
 
642
                  G_TYPE_NONE, 0);
 
643
  glom_gtk_calendar_signals[DAY_SELECTED_SIGNAL] =
 
644
    g_signal_new (I_("day_selected"),
 
645
                  G_OBJECT_CLASS_TYPE (gobject_class),
 
646
                  G_SIGNAL_RUN_FIRST,
 
647
                  G_STRUCT_OFFSET (GlomGtkCalendarClass, day_selected),
 
648
                  NULL, NULL,
 
649
                  _gtk_marshal_VOID__VOID,
 
650
                  G_TYPE_NONE, 0);
 
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),
 
654
                  G_SIGNAL_RUN_FIRST,
 
655
                  G_STRUCT_OFFSET (GlomGtkCalendarClass, day_selected_double_click),
 
656
                  NULL, NULL,
 
657
                  _gtk_marshal_VOID__VOID,
 
658
                  G_TYPE_NONE, 0);
 
659
  glom_gtk_calendar_signals[PREV_MONTH_SIGNAL] =
 
660
    g_signal_new (I_("prev_month"),
 
661
                  G_OBJECT_CLASS_TYPE (gobject_class),
 
662
                  G_SIGNAL_RUN_FIRST,
 
663
                  G_STRUCT_OFFSET (GlomGtkCalendarClass, prev_month),
 
664
                  NULL, NULL,
 
665
                  _gtk_marshal_VOID__VOID,
 
666
                  G_TYPE_NONE, 0);
 
667
  glom_gtk_calendar_signals[NEXT_MONTH_SIGNAL] =
 
668
    g_signal_new (I_("next_month"),
 
669
                  G_OBJECT_CLASS_TYPE (gobject_class),
 
670
                  G_SIGNAL_RUN_FIRST,
 
671
                  G_STRUCT_OFFSET (GlomGtkCalendarClass, next_month),
 
672
                  NULL, NULL,
 
673
                  _gtk_marshal_VOID__VOID,
 
674
                  G_TYPE_NONE, 0);
 
675
  glom_gtk_calendar_signals[PREV_YEAR_SIGNAL] =
 
676
    g_signal_new (I_("prev_year"),
 
677
                  G_OBJECT_CLASS_TYPE (gobject_class),
 
678
                  G_SIGNAL_RUN_FIRST,
 
679
                  G_STRUCT_OFFSET (GlomGtkCalendarClass, prev_year),
 
680
                  NULL, NULL,
 
681
                  _gtk_marshal_VOID__VOID,
 
682
                  G_TYPE_NONE, 0);
 
683
  glom_gtk_calendar_signals[NEXT_YEAR_SIGNAL] =
 
684
    g_signal_new (I_("next_year"),
 
685
                  G_OBJECT_CLASS_TYPE (gobject_class),
 
686
                  G_SIGNAL_RUN_FIRST,
 
687
                  G_STRUCT_OFFSET (GlomGtkCalendarClass, next_year),
 
688
                  NULL, NULL,
 
689
                  _gtk_marshal_VOID__VOID,
 
690
                  G_TYPE_NONE, 0);
 
691
  
 
692
  g_type_class_add_private (gobject_class, sizeof (GlomGtkCalendarPrivate));
 
693
}
 
694
 
 
695
static void
 
696
glom_gtk_calendar_init (GlomGtkCalendar *calendar)
 
697
{
 
698
  GtkWidget *widget = GTK_WIDGET (calendar);
 
699
  time_t secs;
 
700
  struct tm *tm;
 
701
  gint i;
 
702
#ifdef G_OS_WIN32
 
703
  wchar_t wbuffer[100];
 
704
#else
 
705
  char buffer[255];
 
706
  time_t tmp_time;
 
707
#endif
 
708
  GlomGtkCalendarPrivate *priv;
 
709
  gchar *year_before;
 
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;
 
714
  guint week_origin;
 
715
#else
 
716
  gchar *week_start;
 
717
#endif
 
718
 
 
719
  priv = calendar->priv = G_TYPE_INSTANCE_GET_PRIVATE (calendar,
 
720
                                                       GLOM_GTK_TYPE_CALENDAR,
 
721
                                                       GlomGtkCalendarPrivate);
 
722
 
 
723
  GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
 
724
  
 
725
  if (!default_abbreviated_dayname[0])
 
726
    for (i=0; i<7; i++)
 
727
      {
 
728
#ifndef G_OS_WIN32
 
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);
 
732
#else
 
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);
 
736
        else
 
737
          default_abbreviated_dayname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
 
738
#endif
 
739
      }
 
740
  
 
741
  if (!default_monthname[0])
 
742
    for (i=0; i<12; i++)
 
743
      {
 
744
#ifndef G_OS_WIN32
 
745
        tmp_time=i*2764800;
 
746
        strftime ( buffer, sizeof (buffer), "%B", gmtime (&tmp_time));
 
747
        default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
 
748
#else
 
749
        if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SMONTHNAME1 + i,
 
750
                             wbuffer, G_N_ELEMENTS (wbuffer)))
 
751
          default_monthname[i] = g_strdup_printf ("(%d)", i);
 
752
        else
 
753
          default_monthname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
 
754
#endif
 
755
      }
 
756
  
 
757
  /* Set defaults */
 
758
  secs = time (NULL);
 
759
  tm = localtime (&secs);
 
760
  calendar->month = tm->tm_mon;
 
761
  calendar->year  = 1900 + tm->tm_year;
 
762
 
 
763
  for (i=0;i<31;i++)
 
764
    calendar->marked_date[i] = FALSE;
 
765
  calendar->num_marked_dates = 0;
 
766
  calendar->selected_day = tm->tm_mday;
 
767
  
 
768
  calendar->display_flags = (GLOM_GTK_CALENDAR_SHOW_HEADING |
 
769
                             GLOM_GTK_CALENDAR_SHOW_DAY_NAMES |
 
770
                             GLOM_GTK_CALENDAR_SHOW_DETAILS);
 
771
  
 
772
  calendar->highlight_row = -1;
 
773
  calendar->highlight_col = -1;
 
774
  
 
775
  calendar->focus_row = -1;
 
776
  calendar->focus_col = -1;
 
777
 
 
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;
 
782
 
 
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;
 
787
 
 
788
  priv->arrow_width = 10;
 
789
 
 
790
  priv->need_timer = 0;
 
791
  priv->timer = 0;
 
792
  priv->click_child = -1;
 
793
 
 
794
  priv->in_drag = 0;
 
795
  priv->drag_highlight = 0;
 
796
 
 
797
  gtk_drag_dest_set (widget, 0, NULL, 0, GDK_ACTION_COPY);
 
798
  gtk_drag_dest_add_text_targets (widget);
 
799
 
 
800
  priv->year_before = 0;
 
801
 
 
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.
 
806
   *
 
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.
 
810
   */
 
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");
 
816
 
 
817
#ifdef G_OS_WIN32
 
818
  priv->week_start = 0;
 
819
  week_start = NULL;
 
820
 
 
821
  if (GetLocaleInfoW (GetThreadLocale (), LOCALE_IFIRSTDAYOFWEEK,
 
822
                      wbuffer, G_N_ELEMENTS (wbuffer)))
 
823
    week_start = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
 
824
      
 
825
  if (week_start != NULL)
 
826
    {
 
827
      priv->week_start = (week_start[0] - '0' + 1) % 7;
 
828
      g_free(week_start);
 
829
    }
 
830
#else
 
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 */
 
837
    week_1stday = 0;
 
838
  else if (week_origin == 19971201) /* Monday */
 
839
    week_1stday = 1;
 
840
  else
 
841
    g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY.\n");
 
842
 
 
843
  priv->week_start = (week_1stday + first_weekday - 1) % 7;
 
844
#else
 
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.
 
848
   */  
 
849
  week_start = _("calendar:week_start:0");
 
850
 
 
851
  if (strncmp (week_start, "calendar:week_start:", 20) == 0)
 
852
    priv->week_start = *(week_start + 20) - '0';
 
853
  else 
 
854
    priv->week_start = -1;
 
855
  
 
856
  if (priv->week_start < 0 || priv->week_start > 6)
 
857
    {
 
858
      g_warning ("Whoever translated calendar:week_start:0 did so wrongly.\n");
 
859
      priv->week_start = 0;
 
860
    }
 
861
#endif
 
862
#endif
 
863
 
 
864
  calendar_compute_days (calendar);
 
865
}
 
866
 
 
867
 
 
868
/****************************************
 
869
 *          Utility Functions           *
 
870
 ****************************************/
 
871
 
 
872
static void
 
873
calendar_queue_refresh (GlomGtkCalendar *calendar)
 
874
{
 
875
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
876
 
 
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));
 
881
  else
 
882
    gtk_widget_queue_resize (GTK_WIDGET (calendar));
 
883
}
 
884
 
 
885
static void
 
886
calendar_set_month_next (GlomGtkCalendar *calendar)
 
887
{
 
888
  gint month_len;
 
889
  
 
890
  g_return_if_fail (GTK_IS_WIDGET (calendar));
 
891
  
 
892
  if (calendar->display_flags & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
 
893
    return;
 
894
  
 
895
  
 
896
  if (calendar->month == 11)
 
897
    {
 
898
      calendar->month = 0;
 
899
      calendar->year++;
 
900
    } 
 
901
  else 
 
902
    calendar->month++;
 
903
  
 
904
  calendar_compute_days (calendar);
 
905
  g_signal_emit (calendar,
 
906
                 glom_gtk_calendar_signals[NEXT_MONTH_SIGNAL],
 
907
                 0);
 
908
  g_signal_emit (calendar,
 
909
                 glom_gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
 
910
                 0);
 
911
  
 
912
  month_len = month_length[leap (calendar->year)][calendar->month + 1];
 
913
  
 
914
  if (month_len < calendar->selected_day)
 
915
    {
 
916
      calendar->selected_day = 0;
 
917
      glom_gtk_calendar_select_day (calendar, month_len);
 
918
    }
 
919
  else
 
920
    glom_gtk_calendar_select_day (calendar, calendar->selected_day);
 
921
 
 
922
  calendar_queue_refresh (calendar);
 
923
}
 
924
 
 
925
static void
 
926
calendar_set_year_prev (GlomGtkCalendar *calendar)
 
927
{
 
928
  gint month_len;
 
929
  
 
930
  g_return_if_fail (GTK_IS_WIDGET (calendar));
 
931
  
 
932
  calendar->year--;
 
933
  calendar_compute_days (calendar);
 
934
  g_signal_emit (calendar,
 
935
                 glom_gtk_calendar_signals[PREV_YEAR_SIGNAL],
 
936
                 0);
 
937
  g_signal_emit (calendar,
 
938
                 glom_gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
 
939
                 0);
 
940
  
 
941
  month_len = month_length[leap (calendar->year)][calendar->month + 1];
 
942
  
 
943
  if (month_len < calendar->selected_day)
 
944
    {
 
945
      calendar->selected_day = 0;
 
946
      glom_gtk_calendar_select_day (calendar, month_len);
 
947
    }
 
948
  else
 
949
    glom_gtk_calendar_select_day (calendar, calendar->selected_day);
 
950
  
 
951
  calendar_queue_refresh (calendar);
 
952
}
 
953
 
 
954
static void
 
955
calendar_set_year_next (GlomGtkCalendar *calendar)
 
956
{
 
957
  gint month_len;
 
958
  
 
959
  g_return_if_fail (GTK_IS_WIDGET (calendar));
 
960
  
 
961
  calendar->year++;
 
962
  calendar_compute_days (calendar);
 
963
  g_signal_emit (calendar,
 
964
                 glom_gtk_calendar_signals[NEXT_YEAR_SIGNAL],
 
965
                 0);
 
966
  g_signal_emit (calendar,
 
967
                 glom_gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
 
968
                 0);
 
969
  
 
970
  month_len = month_length[leap (calendar->year)][calendar->month + 1];
 
971
  
 
972
  if (month_len < calendar->selected_day)
 
973
    {
 
974
      calendar->selected_day = 0;
 
975
      glom_gtk_calendar_select_day (calendar, month_len);
 
976
    }
 
977
  else
 
978
    glom_gtk_calendar_select_day (calendar, calendar->selected_day);
 
979
  
 
980
  calendar_queue_refresh (calendar);
 
981
}
 
982
 
 
983
static void
 
984
calendar_compute_days (GlomGtkCalendar *calendar)
 
985
{
 
986
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (GTK_WIDGET (calendar));
 
987
  gint month;
 
988
  gint year;
 
989
  gint ndays_in_month;
 
990
  gint ndays_in_prev_month;
 
991
  gint first_day;
 
992
  gint row;
 
993
  gint col;
 
994
  gint day;
 
995
 
 
996
  g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
 
997
 
 
998
  year = calendar->year;
 
999
  month = calendar->month + 1;
 
1000
  
 
1001
  ndays_in_month = month_length[leap (year)][month];
 
1002
  
 
1003
  first_day = day_of_week (year, month, 1);
 
1004
  first_day = (first_day + 7 - priv->week_start) % 7;
 
1005
  
 
1006
  /* Compute days of previous month */
 
1007
  if (month > 1)
 
1008
    ndays_in_prev_month = month_length[leap (year)][month-1];
 
1009
  else
 
1010
    ndays_in_prev_month = month_length[leap (year)][12];
 
1011
  day = ndays_in_prev_month - first_day + 1;
 
1012
  
 
1013
  row = 0;
 
1014
  if (first_day > 0)
 
1015
    {
 
1016
      for (col = 0; col < first_day; col++)
 
1017
        {
 
1018
          calendar->day[row][col] = day;
 
1019
          calendar->day_month[row][col] = MONTH_PREV;
 
1020
          day++;
 
1021
        }
 
1022
    }
 
1023
  
 
1024
  /* Compute days of current month */
 
1025
  col = first_day;
 
1026
  for (day = 1; day <= ndays_in_month; day++)
 
1027
    {
 
1028
      calendar->day[row][col] = day;
 
1029
      calendar->day_month[row][col] = MONTH_CURRENT;
 
1030
      
 
1031
      col++;
 
1032
      if (col == 7)
 
1033
        {
 
1034
          row++;
 
1035
          col = 0;
 
1036
        }
 
1037
    }
 
1038
  
 
1039
  /* Compute days of next month */
 
1040
  day = 1;
 
1041
  for (; row <= 5; row++)
 
1042
    {
 
1043
      for (; col <= 6; col++)
 
1044
        {
 
1045
          calendar->day[row][col] = day;
 
1046
          calendar->day_month[row][col] = MONTH_NEXT;
 
1047
          day++;
 
1048
        }
 
1049
      col = 0;
 
1050
    }
 
1051
}
 
1052
 
 
1053
static void
 
1054
calendar_select_and_focus_day (GlomGtkCalendar *calendar,
 
1055
                               guint        day)
 
1056
{
 
1057
  gint old_focus_row = calendar->focus_row;
 
1058
  gint old_focus_col = calendar->focus_col;
 
1059
  gint row;
 
1060
  gint col;
 
1061
  
 
1062
  for (row = 0; row < 6; row ++)
 
1063
    for (col = 0; col < 7; col++)
 
1064
      {
 
1065
        if (calendar->day_month[row][col] == MONTH_CURRENT 
 
1066
            && calendar->day[row][col] == day)
 
1067
          {
 
1068
            calendar->focus_row = row;
 
1069
            calendar->focus_col = col;
 
1070
          }
 
1071
      }
 
1072
 
 
1073
  if (old_focus_row != -1 && old_focus_col != -1)
 
1074
    calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
 
1075
  
 
1076
  glom_gtk_calendar_select_day (calendar, day);
 
1077
}
 
1078
 
 
1079
 
 
1080
/****************************************
 
1081
 *     Layout computation utilities     *
 
1082
 ****************************************/
 
1083
 
 
1084
static gint
 
1085
calendar_row_height (GlomGtkCalendar *calendar)
 
1086
{
 
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;
 
1090
}
 
1091
 
 
1092
 
 
1093
/* calendar_left_x_for_column: returns the x coordinate
 
1094
 * for the left of the column */
 
1095
static gint
 
1096
calendar_left_x_for_column (GlomGtkCalendar *calendar,
 
1097
                            gint         column)
 
1098
{
 
1099
  gint width;
 
1100
  gint x_left;
 
1101
  
 
1102
  if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
 
1103
    column = 6 - column;
 
1104
 
 
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;
 
1108
  else
 
1109
    x_left = CALENDAR_MARGIN + (width + DAY_XSEP) * column;
 
1110
  
 
1111
  return x_left;
 
1112
}
 
1113
 
 
1114
/* column_from_x: returns the column 0-6 that the
 
1115
 * x pixel of the xwindow is in */
 
1116
static gint
 
1117
calendar_column_from_x (GlomGtkCalendar *calendar,
 
1118
                        gint         event_x)
 
1119
{
 
1120
  gint c, column;
 
1121
  gint x_left, x_right;
 
1122
  
 
1123
  column = -1;
 
1124
  
 
1125
  for (c = 0; c < 7; c++)
 
1126
    {
 
1127
      x_left = calendar_left_x_for_column (calendar, c);
 
1128
      x_right = x_left + GLOM_GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
 
1129
      
 
1130
      if (event_x >= x_left && event_x < x_right)
 
1131
        {
 
1132
          column = c;
 
1133
          break;
 
1134
        }
 
1135
    }
 
1136
  
 
1137
  return column;
 
1138
}
 
1139
 
 
1140
/* calendar_top_y_for_row: returns the y coordinate
 
1141
 * for the top of the row */
 
1142
static gint
 
1143
calendar_top_y_for_row (GlomGtkCalendar *calendar,
 
1144
                        gint         row)
 
1145
{
 
1146
  
 
1147
  return (GLOM_GTK_CALENDAR_GET_PRIVATE (calendar)->main_h 
 
1148
          - (CALENDAR_MARGIN + (6 - row)
 
1149
             * calendar_row_height (calendar)));
 
1150
}
 
1151
 
 
1152
/* row_from_y: returns the row 0-5 that the
 
1153
 * y pixel of the xwindow is in */
 
1154
static gint
 
1155
calendar_row_from_y (GlomGtkCalendar *calendar,
 
1156
                     gint         event_y)
 
1157
{
 
1158
  gint r, row;
 
1159
  gint height;
 
1160
  gint y_top, y_bottom;
 
1161
  
 
1162
  height = calendar_row_height (calendar);
 
1163
  row = -1;
 
1164
  
 
1165
  for (r = 0; r < 6; r++)
 
1166
    {
 
1167
      y_top = calendar_top_y_for_row (calendar, r);
 
1168
      y_bottom = y_top + height;
 
1169
      
 
1170
      if (event_y >= y_top && event_y < y_bottom)
 
1171
        {
 
1172
          row = r;
 
1173
          break;
 
1174
        }
 
1175
    }
 
1176
  
 
1177
  return row;
 
1178
}
 
1179
 
 
1180
static void
 
1181
calendar_arrow_rectangle (GlomGtkCalendar  *calendar,
 
1182
                          guint         arrow,
 
1183
                          GdkRectangle *rect)
 
1184
{
 
1185
  GtkWidget *widget = GTK_WIDGET (calendar);
 
1186
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
1187
  gboolean year_left;
 
1188
 
 
1189
  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
 
1190
    year_left = priv->year_before;
 
1191
  else
 
1192
    year_left = !priv->year_before;
 
1193
    
 
1194
  rect->y = 3;
 
1195
  rect->width = priv->arrow_width;
 
1196
  rect->height = priv->header_h - 7;
 
1197
  
 
1198
  switch (arrow)
 
1199
    {
 
1200
    case ARROW_MONTH_LEFT:
 
1201
      if (year_left) 
 
1202
        rect->x = (widget->allocation.width - 2 * widget->style->xthickness
 
1203
                   - (3 + 2*priv->arrow_width 
 
1204
                      + priv->max_month_width));
 
1205
      else
 
1206
        rect->x = 3;
 
1207
      break;
 
1208
    case ARROW_MONTH_RIGHT:
 
1209
      if (year_left) 
 
1210
        rect->x = (widget->allocation.width - 2 * widget->style->xthickness 
 
1211
                   - 3 - priv->arrow_width);
 
1212
      else
 
1213
        rect->x = (priv->arrow_width 
 
1214
                   + priv->max_month_width);
 
1215
      break;
 
1216
    case ARROW_YEAR_LEFT:
 
1217
      if (year_left) 
 
1218
        rect->x = 3;
 
1219
      else
 
1220
        rect->x = (widget->allocation.width - 2 * widget->style->xthickness
 
1221
                   - (3 + 2*priv->arrow_width 
 
1222
                      + priv->max_year_width));
 
1223
      break;
 
1224
    case ARROW_YEAR_RIGHT:
 
1225
      if (year_left) 
 
1226
        rect->x = (priv->arrow_width 
 
1227
                   + priv->max_year_width);
 
1228
      else
 
1229
        rect->x = (widget->allocation.width - 2 * widget->style->xthickness 
 
1230
                   - 3 - priv->arrow_width);
 
1231
      break;
 
1232
    }
 
1233
}
 
1234
 
 
1235
static void
 
1236
calendar_day_rectangle (GlomGtkCalendar  *calendar,
 
1237
                        gint          row,
 
1238
                        gint          col,
 
1239
                        GdkRectangle *rect)
 
1240
{
 
1241
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
1242
 
 
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;
 
1247
}
 
1248
 
 
1249
static void
 
1250
calendar_set_month_prev (GlomGtkCalendar *calendar)
 
1251
{
 
1252
  gint month_len;
 
1253
  
 
1254
  if (calendar->display_flags & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
 
1255
    return;
 
1256
  
 
1257
  if (calendar->month == 0)
 
1258
    {
 
1259
      calendar->month = 11;
 
1260
      calendar->year--;
 
1261
    } 
 
1262
  else 
 
1263
    calendar->month--;
 
1264
  
 
1265
  month_len = month_length[leap (calendar->year)][calendar->month + 1];
 
1266
  
 
1267
  calendar_compute_days (calendar);
 
1268
  
 
1269
  g_signal_emit (calendar,
 
1270
                 glom_gtk_calendar_signals[PREV_MONTH_SIGNAL],
 
1271
                 0);
 
1272
  g_signal_emit (calendar,
 
1273
                 glom_gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
 
1274
                 0);
 
1275
  
 
1276
  if (month_len < calendar->selected_day)
 
1277
    {
 
1278
      calendar->selected_day = 0;
 
1279
      glom_gtk_calendar_select_day (calendar, month_len);
 
1280
    }
 
1281
  else
 
1282
    {
 
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);
 
1286
    }
 
1287
 
 
1288
  calendar_queue_refresh (calendar);
 
1289
}
 
1290
 
 
1291
 
 
1292
/****************************************
 
1293
 *           Basic object methods       *
 
1294
 ****************************************/
 
1295
 
 
1296
static void
 
1297
glom_gtk_calendar_finalize (GObject *object)
 
1298
{
 
1299
  (* G_OBJECT_CLASS (glom_gtk_calendar_parent_class)->finalize) (object);
 
1300
}
 
1301
 
 
1302
static void
 
1303
glom_gtk_calendar_destroy (GtkObject *object)
 
1304
{
 
1305
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (object);
 
1306
 
 
1307
  calendar_stop_spinning (GLOM_GTK_CALENDAR (object));
 
1308
  
 
1309
  /* Call the destroy function for the extra display callback: */
 
1310
  if (priv->detail_func_destroy && priv->detail_func_user_data)
 
1311
    {
 
1312
      priv->detail_func_destroy (priv->detail_func_user_data);
 
1313
      priv->detail_func_user_data = NULL;
 
1314
      priv->detail_func_destroy = NULL;
 
1315
    }
 
1316
 
 
1317
  GTK_OBJECT_CLASS (glom_gtk_calendar_parent_class)->destroy (object);
 
1318
}
 
1319
 
 
1320
 
 
1321
static void
 
1322
calendar_set_display_option (GlomGtkCalendar              *calendar,
 
1323
                             GlomGtkCalendarDisplayOptions flag,
 
1324
                             gboolean                  setting)
 
1325
{
 
1326
  GlomGtkCalendarDisplayOptions flags;
 
1327
  if (setting) 
 
1328
    flags = calendar->display_flags | flag;
 
1329
  else
 
1330
    flags = calendar->display_flags & ~flag; 
 
1331
  glom_gtk_calendar_display_options (calendar, flags);
 
1332
}
 
1333
 
 
1334
static gboolean
 
1335
calendar_get_display_option (GlomGtkCalendar              *calendar,
 
1336
                             GlomGtkCalendarDisplayOptions flag)
 
1337
{
 
1338
  return (calendar->display_flags & flag) != 0;
 
1339
}
 
1340
 
 
1341
static void 
 
1342
glom_gtk_calendar_set_property (GObject      *object,
 
1343
                           guint         prop_id,
 
1344
                           const GValue *value,
 
1345
                           GParamSpec   *pspec)
 
1346
{
 
1347
  GlomGtkCalendar *calendar;
 
1348
 
 
1349
  calendar = GLOM_GTK_CALENDAR (object);
 
1350
 
 
1351
  switch (prop_id) 
 
1352
    {
 
1353
    case PROP_YEAR:
 
1354
      glom_gtk_calendar_select_month (calendar,
 
1355
                                 calendar->month,
 
1356
                                 g_value_get_int (value));
 
1357
      break;
 
1358
    case PROP_MONTH:
 
1359
      glom_gtk_calendar_select_month (calendar,
 
1360
                                 g_value_get_int (value),
 
1361
                                 calendar->year);
 
1362
      break;
 
1363
    case PROP_DAY:
 
1364
      glom_gtk_calendar_select_day (calendar,
 
1365
                               g_value_get_int (value));
 
1366
      break;
 
1367
    case PROP_SHOW_HEADING:
 
1368
      calendar_set_display_option (calendar,
 
1369
                                   GLOM_GTK_CALENDAR_SHOW_HEADING,
 
1370
                                   g_value_get_boolean (value));
 
1371
      break;
 
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));
 
1376
      break;
 
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));
 
1381
      break;
 
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));
 
1386
      break;
 
1387
    case PROP_SHOW_DETAILS:
 
1388
      calendar_set_display_option (calendar,
 
1389
                                   GLOM_GTK_CALENDAR_SHOW_DETAILS,
 
1390
                                   g_value_get_boolean (value));
 
1391
      break;
 
1392
    case PROP_DETAIL_WIDTH_CHARS:
 
1393
      glom_gtk_calendar_set_detail_width_chars (calendar,
 
1394
                                           g_value_get_int (value));
 
1395
      break;
 
1396
    case PROP_DETAIL_HEIGHT_ROWS:
 
1397
      glom_gtk_calendar_set_detail_height_rows (calendar,
 
1398
                                           g_value_get_int (value));
 
1399
      break;
 
1400
    default:
 
1401
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
1402
      break;
 
1403
    }
 
1404
}
 
1405
 
 
1406
static void 
 
1407
glom_gtk_calendar_get_property (GObject      *object,
 
1408
                           guint         prop_id,
 
1409
                           GValue       *value,
 
1410
                           GParamSpec   *pspec)
 
1411
{
 
1412
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (object);
 
1413
  GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (object);
 
1414
 
 
1415
  switch (prop_id) 
 
1416
    {
 
1417
    case PROP_YEAR:
 
1418
      g_value_set_int (value, calendar->year);
 
1419
      break;
 
1420
    case PROP_MONTH:
 
1421
      g_value_set_int (value, calendar->month);
 
1422
      break;
 
1423
    case PROP_DAY:
 
1424
      g_value_set_int (value, calendar->selected_day);
 
1425
      break;
 
1426
    case PROP_SHOW_HEADING:
 
1427
      g_value_set_boolean (value, calendar_get_display_option (calendar,
 
1428
                                                               GLOM_GTK_CALENDAR_SHOW_HEADING));
 
1429
      break;
 
1430
    case PROP_SHOW_DAY_NAMES:
 
1431
      g_value_set_boolean (value, calendar_get_display_option (calendar,
 
1432
                                                               GLOM_GTK_CALENDAR_SHOW_DAY_NAMES));
 
1433
      break;
 
1434
    case PROP_NO_MONTH_CHANGE:
 
1435
      g_value_set_boolean (value, calendar_get_display_option (calendar,
 
1436
                                                               GLOM_GTK_CALENDAR_NO_MONTH_CHANGE));
 
1437
      break;
 
1438
    case PROP_SHOW_WEEK_NUMBERS:
 
1439
      g_value_set_boolean (value, calendar_get_display_option (calendar,
 
1440
                                                               GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS));
 
1441
      break;
 
1442
    case PROP_SHOW_DETAILS:
 
1443
      g_value_set_boolean (value, calendar_get_display_option (calendar,
 
1444
                                                               GLOM_GTK_CALENDAR_SHOW_DETAILS));
 
1445
      break;
 
1446
    case PROP_DETAIL_WIDTH_CHARS:
 
1447
      g_value_set_int (value, priv->detail_width_chars);
 
1448
      break;
 
1449
    case PROP_DETAIL_HEIGHT_ROWS:
 
1450
      g_value_set_int (value, priv->detail_height_rows);
 
1451
      break;
 
1452
    default:
 
1453
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
1454
      break;
 
1455
    }
 
1456
}
 
1457
 
 
1458
 
 
1459
/****************************************
 
1460
 *             Realization              *
 
1461
 ****************************************/
 
1462
 
 
1463
static void
 
1464
calendar_realize_arrows (GlomGtkCalendar *calendar)
 
1465
{
 
1466
  GtkWidget *widget = GTK_WIDGET (calendar);
 
1467
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
1468
  GdkWindowAttr attributes;
 
1469
  gint attributes_mask;
 
1470
  gint i;
 
1471
  
 
1472
  /* Arrow windows ------------------------------------- */
 
1473
  if (! (calendar->display_flags & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
 
1474
      && (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_HEADING))
 
1475
    {
 
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++)
 
1485
        {
 
1486
          GdkRectangle rect;
 
1487
          calendar_arrow_rectangle (calendar, i, &rect);
 
1488
          
 
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,
 
1494
                                               &attributes, 
 
1495
                                               attributes_mask);
 
1496
          if (GTK_WIDGET_IS_SENSITIVE (widget))
 
1497
            priv->arrow_state[i] = GTK_STATE_NORMAL;
 
1498
          else 
 
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);
 
1504
        }
 
1505
    }
 
1506
  else
 
1507
    {
 
1508
      for (i = 0; i < 4; i++)
 
1509
        priv->arrow_win[i] = NULL;
 
1510
    }
 
1511
}
 
1512
 
 
1513
static void
 
1514
calendar_realize_header (GlomGtkCalendar *calendar)
 
1515
{
 
1516
  GtkWidget *widget = GTK_WIDGET (calendar);
 
1517
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
1518
  GdkWindowAttr attributes;
 
1519
  gint attributes_mask;
 
1520
  
 
1521
  /* Header window ------------------------------------- */
 
1522
  if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_HEADING)
 
1523
    {
 
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);
 
1536
      
 
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);
 
1541
      
 
1542
    }
 
1543
  else
 
1544
    {
 
1545
      priv->header_win = NULL;
 
1546
    }
 
1547
  calendar_realize_arrows (calendar);
 
1548
}
 
1549
 
 
1550
static void
 
1551
calendar_realize_day_names (GlomGtkCalendar *calendar)
 
1552
{
 
1553
  GtkWidget *widget = GTK_WIDGET (calendar);
 
1554
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
1555
  GdkWindowAttr attributes;
 
1556
  gint attributes_mask;
 
1557
  
 
1558
  /* Day names  window --------------------------------- */
 
1559
  if ( calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_DAY_NAMES)
 
1560
    {
 
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 
 
1569
                                           + INNER_BORDER);
 
1570
      attributes.width = (widget->allocation.width 
 
1571
                          - (widget->style->xthickness + INNER_BORDER) 
 
1572
                          * 2);
 
1573
      attributes.height = priv->day_name_h;
 
1574
      priv->day_name_win = gdk_window_new (widget->window,
 
1575
                                           &attributes, 
 
1576
                                           attributes_mask);
 
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);
 
1581
    }
 
1582
  else
 
1583
    {
 
1584
      priv->day_name_win = NULL;
 
1585
    }
 
1586
}
 
1587
 
 
1588
static void
 
1589
calendar_realize_week_numbers (GlomGtkCalendar *calendar)
 
1590
{
 
1591
  GtkWidget *widget = GTK_WIDGET (calendar);
 
1592
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
1593
  GdkWindowAttr attributes;
 
1594
  gint attributes_mask;
 
1595
  
 
1596
  /* Week number window -------------------------------- */
 
1597
  if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS)
 
1598
    {
 
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;
 
1604
      
 
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);
 
1617
    } 
 
1618
  else
 
1619
    {
 
1620
      priv->week_win = NULL;
 
1621
    }
 
1622
}
 
1623
 
 
1624
static void
 
1625
glom_gtk_calendar_realize (GtkWidget *widget)
 
1626
{
 
1627
  GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
 
1628
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
1629
  GdkWindowAttr attributes;
 
1630
  gint attributes_mask;
 
1631
 
 
1632
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
 
1633
  
 
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);
 
1644
  
 
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);
 
1648
  
 
1649
  widget->style = gtk_style_attach (widget->style, widget->window);
 
1650
  
 
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);
 
1661
  
 
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);
 
1677
}
 
1678
 
 
1679
static void
 
1680
glom_gtk_calendar_unrealize (GtkWidget *widget)
 
1681
{
 
1682
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
1683
  gint i;
 
1684
  
 
1685
  if (priv->header_win)
 
1686
    {
 
1687
      for (i = 0; i < 4; i++)
 
1688
        {
 
1689
          if (priv->arrow_win[i])
 
1690
            {
 
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;
 
1694
            }
 
1695
        }
 
1696
      gdk_window_set_user_data (priv->header_win, NULL);
 
1697
      gdk_window_destroy (priv->header_win);
 
1698
      priv->header_win = NULL;
 
1699
    }
 
1700
  
 
1701
  if (priv->week_win)
 
1702
    {
 
1703
      gdk_window_set_user_data (priv->week_win, NULL);
 
1704
      gdk_window_destroy (priv->week_win);
 
1705
      priv->week_win = NULL;      
 
1706
    }
 
1707
  
 
1708
  if (priv->main_win)
 
1709
    {
 
1710
      gdk_window_set_user_data (priv->main_win, NULL);
 
1711
      gdk_window_destroy (priv->main_win);
 
1712
      priv->main_win = NULL;      
 
1713
    }
 
1714
  if (priv->day_name_win)
 
1715
    {
 
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;      
 
1719
    }
 
1720
  
 
1721
  if (GTK_WIDGET_CLASS (glom_gtk_calendar_parent_class)->unrealize)
 
1722
    (* GTK_WIDGET_CLASS (glom_gtk_calendar_parent_class)->unrealize) (widget);
 
1723
}
 
1724
 
 
1725
static gchar*
 
1726
glom_gtk_calendar_get_detail (GlomGtkCalendar *calendar,
 
1727
                         gint         row,
 
1728
                         gint         column)
 
1729
{
 
1730
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
1731
  gint year, month;
 
1732
 
 
1733
  year = calendar->year;
 
1734
  month = calendar->month + calendar->day_month[row][column] - MONTH_CURRENT;
 
1735
 
 
1736
  if (month < 0)
 
1737
    {
 
1738
      month += 12;
 
1739
      year -= 1;
 
1740
    }
 
1741
  else if (month > 11)
 
1742
    {
 
1743
      month -= 12;
 
1744
      year += 1;
 
1745
    }
 
1746
 
 
1747
  return priv->detail_func (calendar,
 
1748
                            year, month,
 
1749
                            calendar->day[row][column],
 
1750
                            priv->detail_func_user_data);
 
1751
}
 
1752
 
 
1753
static gboolean
 
1754
glom_gtk_calendar_query_tooltip (GtkWidget  *widget,
 
1755
                            gint        x,
 
1756
                            gint        y,
 
1757
                            gboolean    keyboard_mode,
 
1758
                            GtkTooltip *tooltip)
 
1759
{
 
1760
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
1761
  GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
 
1762
  gchar *detail = NULL;
 
1763
  GdkRectangle day_rect;
 
1764
 
 
1765
  if (priv->main_win)
 
1766
    {
 
1767
      gint x0, y0, row, col;
 
1768
 
 
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);
 
1772
 
 
1773
      if (0 != (priv->detail_overflow[row] & (1 << col)) ||
 
1774
          0 == (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_DETAILS))
 
1775
        {
 
1776
          detail = glom_gtk_calendar_get_detail (calendar, row, col);
 
1777
          calendar_day_rectangle (calendar, row, col, &day_rect);
 
1778
 
 
1779
          day_rect.x += x0;
 
1780
          day_rect.y += y0;
 
1781
        }
 
1782
    }
 
1783
 
 
1784
  if (detail)
 
1785
    {
 
1786
      gtk_tooltip_set_tip_area (tooltip, &day_rect);
 
1787
      gtk_tooltip_set_markup (tooltip, detail);
 
1788
 
 
1789
      g_free (detail);
 
1790
 
 
1791
      return TRUE;
 
1792
    }
 
1793
 
 
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);
 
1796
 
 
1797
  return FALSE;
 
1798
}
 
1799
 
 
1800
 
 
1801
/****************************************
 
1802
 *       Size Request and Allocate      *
 
1803
 ****************************************/
 
1804
 
 
1805
static void
 
1806
glom_gtk_calendar_size_request (GtkWidget         *widget,
 
1807
                           GtkRequisition *requisition)
 
1808
{
 
1809
  GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
 
1810
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
1811
  PangoLayout *layout;
 
1812
  PangoRectangle logical_rect;
 
1813
 
 
1814
  gint height;
 
1815
  gint i, r, c;
 
1816
  gint calendar_margin = CALENDAR_MARGIN;
 
1817
  gint header_width, main_width;
 
1818
  gint max_header_height = 0;
 
1819
  gint focus_width;
 
1820
  gint focus_padding;
 
1821
  gint max_detail_height;
 
1822
 
 
1823
  gtk_widget_style_get (GTK_WIDGET (widget),
 
1824
                        "focus-line-width", &focus_width,
 
1825
                        "focus-padding", &focus_padding,
 
1826
                        NULL);
 
1827
 
 
1828
  layout = gtk_widget_create_pango_layout (widget, NULL);
 
1829
  
 
1830
  /*
 
1831
   * Calculate the requisition  width for the widget.
 
1832
   */
 
1833
  
 
1834
  /* Header width */
 
1835
  
 
1836
  if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_HEADING)
 
1837
    {
 
1838
      priv->max_month_width = 0;
 
1839
      for (i = 0; i < 12; i++)
 
1840
        {
 
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); 
 
1846
        }
 
1847
 
 
1848
      priv->max_year_width = 0;
 
1849
      /* Translators:  This is a text measurement template.
 
1850
       * Translate it to the widest year text. 
 
1851
       * 
 
1852
       * Don't include the prefix "year measurement template|" 
 
1853
       * in the translation.
 
1854
       *
 
1855
       * If you don't understand this, leave it as "2000"
 
1856
       */
 
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); 
 
1862
    } 
 
1863
  else 
 
1864
    {
 
1865
      priv->max_month_width = 0;
 
1866
      priv->max_year_width = 0;
 
1867
    }
 
1868
  
 
1869
  if (calendar->display_flags & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
 
1870
    header_width = (priv->max_month_width 
 
1871
                    + priv->max_year_width
 
1872
                    + 3 * 3);
 
1873
  else
 
1874
    header_width = (priv->max_month_width 
 
1875
                    + priv->max_year_width
 
1876
                    + 4 * priv->arrow_width + 3 * 3);
 
1877
 
 
1878
  /* Mainwindow labels width */
 
1879
  
 
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;
 
1884
 
 
1885
  for (i = 0; i < 9; i++)
 
1886
    {
 
1887
      gchar buffer[32];
 
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);
 
1893
 
 
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));
 
1898
    }
 
1899
  
 
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++)
 
1904
      {
 
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);
 
1907
 
 
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));
 
1913
      }
 
1914
  
 
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++)
 
1918
      {
 
1919
        gchar buffer[32];
 
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);
 
1925
      }
 
1926
  
 
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;
 
1930
 
 
1931
  if (priv->detail_func && (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_DETAILS))
 
1932
    {
 
1933
      gchar *markup, *tail;
 
1934
 
 
1935
      if (priv->detail_width_chars || priv->detail_height_rows)
 
1936
        {
 
1937
          gint rows = MAX (1, priv->detail_height_rows) - 1;
 
1938
          gsize len = priv->detail_width_chars + rows + 16;
 
1939
 
 
1940
          markup = tail = g_alloca (len);
 
1941
 
 
1942
          memcpy (tail,     "<small>", 7);
 
1943
          tail += 7;
 
1944
 
 
1945
          memset (tail, 'm', priv->detail_width_chars);
 
1946
          tail += priv->detail_width_chars;
 
1947
 
 
1948
          memset (tail, '\n', rows);
 
1949
          tail += rows;
 
1950
 
 
1951
          memcpy (tail,     "</small>", 9);
 
1952
          tail += 9;
 
1953
 
 
1954
          g_assert (len == (tail - markup));
 
1955
 
 
1956
          pango_layout_set_markup (layout, markup, -1);
 
1957
          pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
 
1958
 
 
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);
 
1963
        }
 
1964
 
 
1965
      if (!priv->detail_width_chars || !priv->detail_height_rows)
 
1966
        for (r = 0; r < 6; r++)
 
1967
          for (c = 0; c < 7; c++)
 
1968
            {
 
1969
              gchar *detail = glom_gtk_calendar_get_detail (calendar, r, c);
 
1970
 
 
1971
              if (detail)
 
1972
                {
 
1973
                  markup = g_strconcat ("<small>", detail, "</small>", NULL);
 
1974
                  pango_layout_set_markup (layout, markup, -1);
 
1975
 
 
1976
                  if (priv->detail_width_chars)
 
1977
                    {
 
1978
                      pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
 
1979
                      pango_layout_set_width (layout, PANGO_SCALE * priv->min_day_width);
 
1980
                    }
 
1981
 
 
1982
                  pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
 
1983
 
 
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);
 
1988
 
 
1989
                  g_free (markup);
 
1990
                  g_free (detail);
 
1991
                }
 
1992
            }
 
1993
    }
 
1994
 
 
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;
 
1997
 
 
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
 
2001
                   : 0));
 
2002
  
 
2003
  
 
2004
  requisition->width = MAX (header_width, main_width + INNER_BORDER * 2) + widget->style->xthickness * 2;
 
2005
  
 
2006
  /*
 
2007
   * Calculate the requisition height for the widget.
 
2008
   */
 
2009
  
 
2010
  if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_HEADING)
 
2011
    {
 
2012
      priv->header_h = (max_header_height + CALENDAR_YSEP * 2);
 
2013
    }
 
2014
  else
 
2015
    {
 
2016
      priv->header_h = 0;
 
2017
    }
 
2018
  
 
2019
  if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_DAY_NAMES)
 
2020
    {
 
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;
 
2025
    } 
 
2026
  else
 
2027
    {
 
2028
      priv->day_name_h = 0;
 
2029
    }
 
2030
 
 
2031
  priv->main_h = (CALENDAR_MARGIN + calendar_margin
 
2032
                          + 6 * (priv->max_day_char_ascent
 
2033
                                 + priv->max_day_char_descent 
 
2034
                                 + max_detail_height
 
2035
                                 + 2 * (focus_padding + focus_width))
 
2036
                          + DAY_YSEP * 5);
 
2037
  
 
2038
  height = (priv->header_h + priv->day_name_h 
 
2039
            + priv->main_h);
 
2040
  
 
2041
  requisition->height = height + (widget->style->ythickness + INNER_BORDER) * 2;
 
2042
 
 
2043
  g_object_unref (layout);
 
2044
}
 
2045
 
 
2046
static void
 
2047
glom_gtk_calendar_size_allocate (GtkWidget        *widget,
 
2048
                            GtkAllocation *allocation)
 
2049
{
 
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;
 
2054
  guint i;
 
2055
  
 
2056
  widget->allocation = *allocation;
 
2057
    
 
2058
  if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS)
 
2059
    {
 
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);
 
2067
    } 
 
2068
  else 
 
2069
    {
 
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;
 
2075
    }
 
2076
  
 
2077
  if (GTK_WIDGET_REALIZED (widget))
 
2078
    {
 
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);
 
2086
 
 
2087
      for (i = 0 ; i < 4 ; i++)
 
2088
        {
 
2089
          if (priv->arrow_win[i])
 
2090
            {
 
2091
              GdkRectangle rect;
 
2092
              calendar_arrow_rectangle (calendar, i, &rect);
 
2093
          
 
2094
              gdk_window_move_resize (priv->arrow_win[i],
 
2095
                                      rect.x, rect.y, rect.width, rect.height);
 
2096
            }
 
2097
        }
 
2098
      
 
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,
 
2104
                                priv->day_name_h);
 
2105
      if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
 
2106
        {
 
2107
          if (priv->week_win)
 
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),
 
2112
                                    priv->week_width,
 
2113
                                    priv->main_h);
 
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),
 
2118
                                  allocation->width 
 
2119
                                  - priv->week_width 
 
2120
                                  - (xthickness + INNER_BORDER) * 2,
 
2121
                                  priv->main_h);
 
2122
        }
 
2123
      else 
 
2124
        {
 
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),
 
2129
                                  allocation->width 
 
2130
                                  - priv->week_width 
 
2131
                                  - (xthickness + INNER_BORDER) * 2,
 
2132
                                  priv->main_h);
 
2133
          if (priv->week_win)
 
2134
            gdk_window_move_resize (priv->week_win,
 
2135
                                    allocation->width 
 
2136
                                    - priv->week_width 
 
2137
                                    - (xthickness + INNER_BORDER),
 
2138
                                    priv->header_h + priv->day_name_h
 
2139
                                    + (widget->style->ythickness + INNER_BORDER),
 
2140
                                    priv->week_width,
 
2141
                                    priv->main_h);
 
2142
        }
 
2143
    }
 
2144
}
 
2145
 
 
2146
 
 
2147
/****************************************
 
2148
 *              Repainting              *
 
2149
 ****************************************/
 
2150
 
 
2151
static void
 
2152
calendar_paint_header (GlomGtkCalendar *calendar)
 
2153
{
 
2154
  GtkWidget *widget = GTK_WIDGET (calendar);
 
2155
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
2156
  cairo_t *cr;
 
2157
  char buffer[255];
 
2158
  int x, y;
 
2159
  gint header_width;
 
2160
  gint max_month_width;
 
2161
  gint max_year_width;
 
2162
  PangoLayout *layout;
 
2163
  PangoRectangle logical_rect;
 
2164
  gboolean year_left;
 
2165
  time_t tmp_time;
 
2166
  struct tm *tm;
 
2167
  gchar *str;
 
2168
 
 
2169
  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
 
2170
    year_left = priv->year_before;
 
2171
  else
 
2172
    year_left = !priv->year_before;
 
2173
 
 
2174
  cr = gdk_cairo_create (priv->header_win);
 
2175
  
 
2176
  header_width = widget->allocation.width - 2 * widget->style->xthickness;
 
2177
  
 
2178
  max_month_width = priv->max_month_width;
 
2179
  max_year_width = priv->max_year_width;
 
2180
  
 
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);
 
2185
 
 
2186
  tmp_time = 1;  /* Jan 1 1970, 00:00:01 UTC */
 
2187
  tm = gmtime (&tmp_time);
 
2188
  tm->tm_year = calendar->year - 1900;
 
2189
 
 
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.
 
2193
   *
 
2194
   * Also look for the msgid "year measurement template|2000".  
 
2195
   * Translate that entry to a year with the widest output of this
 
2196
   * msgid. 
 
2197
   * 
 
2198
   * Don't include the prefix "calendar year format|" in the 
 
2199
   * translation. "%Y" is appropriate for most locales.
 
2200
   */
 
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);
 
2204
  g_free (str);
 
2205
  
 
2206
  pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
 
2207
  
 
2208
  /* Draw title */
 
2209
  y = (priv->header_h - logical_rect.height) / 2;
 
2210
  
 
2211
  /* Draw year and its arrows */
 
2212
  
 
2213
  if (calendar->display_flags & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
 
2214
    if (year_left)
 
2215
      x = 3 + (max_year_width - logical_rect.width)/2;
 
2216
    else
 
2217
      x = header_width - (3 + max_year_width
 
2218
                          - (max_year_width - logical_rect.width)/2);
 
2219
  else
 
2220
    if (year_left)
 
2221
      x = 3 + priv->arrow_width + (max_year_width - logical_rect.width)/2;
 
2222
    else
 
2223
      x = header_width - (3 + priv->arrow_width + max_year_width
 
2224
                          - (max_year_width - logical_rect.width)/2);
 
2225
  
 
2226
 
 
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);
 
2230
  
 
2231
  /* Draw month */
 
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);
 
2235
 
 
2236
  if (calendar->display_flags & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
 
2237
    if (year_left)
 
2238
      x = header_width - (3 + max_month_width
 
2239
                          - (max_month_width - logical_rect.width)/2);      
 
2240
    else
 
2241
    x = 3 + (max_month_width - logical_rect.width) / 2;
 
2242
  else
 
2243
    if (year_left)
 
2244
      x = header_width - (3 + priv->arrow_width + max_month_width
 
2245
                          - (max_month_width - logical_rect.width)/2);
 
2246
    else
 
2247
    x = 3 + priv->arrow_width + (max_month_width - logical_rect.width)/2;
 
2248
 
 
2249
  cairo_move_to (cr, x, y);
 
2250
  pango_cairo_show_layout (cr, layout);
 
2251
 
 
2252
  g_object_unref (layout);
 
2253
  cairo_destroy (cr);
 
2254
}
 
2255
 
 
2256
static void
 
2257
calendar_paint_day_names (GlomGtkCalendar *calendar)
 
2258
{
 
2259
  GtkWidget *widget = GTK_WIDGET (calendar);
 
2260
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
2261
  cairo_t *cr;
 
2262
  char buffer[255];
 
2263
  int day,i;
 
2264
  int day_width, cal_width;
 
2265
  int day_wid_sep;
 
2266
  PangoLayout *layout;
 
2267
  PangoRectangle logical_rect;
 
2268
  gint focus_padding;
 
2269
  gint focus_width;
 
2270
  
 
2271
  cr = gdk_cairo_create (priv->day_name_win);
 
2272
  
 
2273
  gtk_widget_style_get (GTK_WIDGET (widget),
 
2274
                        "focus-line-width", &focus_width,
 
2275
                        "focus-padding", &focus_padding,
 
2276
                        NULL);
 
2277
  
 
2278
  day_width = priv->day_width;
 
2279
  cal_width = widget->allocation.width;
 
2280
  day_wid_sep = day_width + DAY_XSEP;
 
2281
  
 
2282
  /*
 
2283
   * Draw rectangles as inverted background for the labels.
 
2284
   */
 
2285
 
 
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);
 
2291
  cairo_fill (cr);
 
2292
  
 
2293
  if (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS)
 
2294
    {
 
2295
      cairo_rectangle (cr, 
 
2296
                       CALENDAR_MARGIN,
 
2297
                       priv->day_name_h - CALENDAR_YSEP,
 
2298
                       priv->week_width - CALENDAR_YSEP - CALENDAR_MARGIN,
 
2299
                       CALENDAR_YSEP);
 
2300
      cairo_fill (cr);
 
2301
    }
 
2302
  
 
2303
  /*
 
2304
   * Write the labels
 
2305
   */
 
2306
 
 
2307
  layout = gtk_widget_create_pango_layout (widget, NULL);
 
2308
 
 
2309
  gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
 
2310
  for (i = 0; i < 7; i++)
 
2311
    {
 
2312
      if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
 
2313
        day = 6 - i;
 
2314
      else
 
2315
        day = i;
 
2316
      day = (day + priv->week_start) % 7;
 
2317
      g_snprintf (buffer, sizeof (buffer), "%s", default_abbreviated_dayname[day]);
 
2318
 
 
2319
      pango_layout_set_text (layout, buffer, -1);
 
2320
      pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
 
2321
 
 
2322
      cairo_move_to (cr, 
 
2323
                     (CALENDAR_MARGIN +
 
2324
                      + (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ?
 
2325
                         (priv->week_width + (priv->week_width ? CALENDAR_XSEP : 0))
 
2326
                         : 0)
 
2327
                      + day_wid_sep * i
 
2328
                      + (day_width - logical_rect.width)/2),
 
2329
                     CALENDAR_MARGIN + focus_width + focus_padding + logical_rect.y);
 
2330
      pango_cairo_show_layout (cr, layout);
 
2331
    }
 
2332
  
 
2333
  g_object_unref (layout);
 
2334
  cairo_destroy (cr);
 
2335
}
 
2336
 
 
2337
static void
 
2338
calendar_paint_week_numbers (GlomGtkCalendar *calendar)
 
2339
{
 
2340
  GtkWidget *widget = GTK_WIDGET (calendar);
 
2341
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
2342
  cairo_t *cr;
 
2343
 
 
2344
  guint week = 0, year;
 
2345
  gint row, x_loc, y_loc;
 
2346
  gint day_height;
 
2347
  char buffer[32];
 
2348
  PangoLayout *layout;
 
2349
  PangoRectangle logical_rect;
 
2350
  gint focus_padding;
 
2351
  gint focus_width;
 
2352
  
 
2353
  cr = gdk_cairo_create (priv->week_win);
 
2354
  
 
2355
  gtk_widget_style_get (GTK_WIDGET (widget),
 
2356
                        "focus-line-width", &focus_width,
 
2357
                        "focus-padding", &focus_padding,
 
2358
                        NULL);
 
2359
  
 
2360
  /*
 
2361
   * Draw a rectangle as inverted background for the labels.
 
2362
   */
 
2363
 
 
2364
  gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
 
2365
  if (priv->day_name_win)
 
2366
    cairo_rectangle (cr, 
 
2367
                     CALENDAR_MARGIN,
 
2368
                     0,
 
2369
                     priv->week_width - CALENDAR_MARGIN,
 
2370
                     priv->main_h - CALENDAR_MARGIN);
 
2371
  else
 
2372
    cairo_rectangle (cr,
 
2373
                     CALENDAR_MARGIN,
 
2374
                     CALENDAR_MARGIN,
 
2375
                     priv->week_width - CALENDAR_MARGIN,
 
2376
                     priv->main_h - 2 * CALENDAR_MARGIN);
 
2377
  cairo_fill (cr);
 
2378
  
 
2379
  /*
 
2380
   * Write the labels
 
2381
   */
 
2382
  
 
2383
  layout = gtk_widget_create_pango_layout (widget, NULL);
 
2384
  
 
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++)
 
2388
    {
 
2389
      gboolean result;
 
2390
      
 
2391
      year = calendar->year;
 
2392
      if (calendar->day[row][6] < 15 && row > 3 && calendar->month == 11)
 
2393
        year++;
 
2394
 
 
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);
 
2399
 
 
2400
      /* Translators: this defines whether the week numbers should use
 
2401
       * localized digits or the ones used in English (0123...).
 
2402
       *
 
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.
 
2406
       *
 
2407
       * Note that translating this doesn't guarantee that you get localized
 
2408
       * digits.  That needs support from your system and locale definition
 
2409
       * too.
 
2410
       */
 
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);
 
2414
 
 
2415
      y_loc = calendar_top_y_for_row (calendar, row) + (day_height - logical_rect.height) / 2;
 
2416
 
 
2417
      x_loc = (priv->week_width
 
2418
               - logical_rect.width
 
2419
               - CALENDAR_XSEP - focus_padding - focus_width);
 
2420
 
 
2421
      cairo_move_to (cr, x_loc, y_loc);
 
2422
      pango_cairo_show_layout (cr, layout);
 
2423
    }
 
2424
  
 
2425
  g_object_unref (layout);
 
2426
  cairo_destroy (cr);
 
2427
}
 
2428
 
 
2429
static void
 
2430
calendar_invalidate_day_num (GlomGtkCalendar *calendar,
 
2431
                             gint         day)
 
2432
{
 
2433
  gint r, c, row, col;
 
2434
  
 
2435
  row = -1;
 
2436
  col = -1;
 
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)
 
2441
        {
 
2442
          row = r;
 
2443
          col = c;
 
2444
        }
 
2445
  
 
2446
  g_return_if_fail (row != -1);
 
2447
  g_return_if_fail (col != -1);
 
2448
  
 
2449
  calendar_invalidate_day (calendar, row, col);
 
2450
}
 
2451
 
 
2452
static void
 
2453
calendar_invalidate_day (GlomGtkCalendar *calendar,
 
2454
                         gint         row,
 
2455
                         gint         col)
 
2456
{
 
2457
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
2458
 
 
2459
  if (priv->main_win)
 
2460
    {
 
2461
      GdkRectangle day_rect;
 
2462
      
 
2463
      calendar_day_rectangle (calendar, row, col, &day_rect);
 
2464
      gdk_window_invalidate_rect (priv->main_win, &day_rect, FALSE);
 
2465
    }
 
2466
}
 
2467
 
 
2468
static gboolean
 
2469
is_color_attribute (PangoAttribute *attribute,
 
2470
                    gpointer        data)
 
2471
{
 
2472
  return (attribute->klass->type == PANGO_ATTR_FOREGROUND ||
 
2473
          attribute->klass->type == PANGO_ATTR_BACKGROUND);
 
2474
}
 
2475
 
 
2476
static void
 
2477
calendar_paint_day (GlomGtkCalendar *calendar,
 
2478
                    gint             row,
 
2479
                    gint             col)
 
2480
{
 
2481
  GtkWidget *widget = GTK_WIDGET (calendar);
 
2482
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
2483
  cairo_t *cr;
 
2484
  GdkColor *text_color;
 
2485
  gchar *detail;
 
2486
  gchar buffer[32];
 
2487
  gint day;
 
2488
  gint x_loc, y_loc;
 
2489
  GdkRectangle day_rect;
 
2490
 
 
2491
  PangoLayout *layout;
 
2492
  PangoRectangle logical_rect;
 
2493
  gboolean overflow = FALSE;
 
2494
  gboolean show_details;
 
2495
 
 
2496
  g_return_if_fail (row < 6);
 
2497
  g_return_if_fail (col < 7);
 
2498
 
 
2499
  cr = gdk_cairo_create (priv->main_win);
 
2500
 
 
2501
  day = calendar->day[row][col];
 
2502
  show_details = (calendar->display_flags & GLOM_GTK_CALENDAR_SHOW_DETAILS);
 
2503
 
 
2504
  calendar_day_rectangle (calendar, row, col, &day_rect);
 
2505
  
 
2506
  if (calendar->day_month[row][col] == MONTH_PREV)
 
2507
    {
 
2508
      text_color = PREV_MONTH_COLOR (widget);
 
2509
    } 
 
2510
  else if (calendar->day_month[row][col] == MONTH_NEXT)
 
2511
    {
 
2512
      text_color =  NEXT_MONTH_COLOR (widget);
 
2513
    } 
 
2514
  else 
 
2515
    {
 
2516
#if 0      
 
2517
      if (calendar->highlight_row == row && calendar->highlight_col == col)
 
2518
        {
 
2519
          cairo_set_source_color (cr, HIGHLIGHT_BG_COLOR (widget));
 
2520
          gdk_cairo_rectangle (cr, &day_rect);
 
2521
          cairo_fill (cr);
 
2522
        }
 
2523
#endif     
 
2524
      if (calendar->selected_day == day)
 
2525
        {
 
2526
          gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
 
2527
          gdk_cairo_rectangle (cr, &day_rect);
 
2528
          cairo_fill (cr);
 
2529
        }
 
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);
 
2534
      else
 
2535
        text_color = NORMAL_DAY_COLOR (widget);
 
2536
    }
 
2537
 
 
2538
  /* Translators: this defines whether the day numbers should use
 
2539
   * localized digits or the ones used in English (0123...).
 
2540
   *
 
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.
 
2544
   *
 
2545
   * Note that translating this doesn't guarantee that you get localized
 
2546
   * digits.  That needs support from your system and locale definition
 
2547
   * too.
 
2548
   */
 
2549
  g_snprintf (buffer, sizeof (buffer), Q_("calendar:day:digits|%d"), day);
 
2550
 
 
2551
  /* Get extra information to show, if any: */
 
2552
 
 
2553
  if (priv->detail_func)
 
2554
    detail = glom_gtk_calendar_get_detail (calendar, row, col);
 
2555
  else
 
2556
    detail = NULL;
 
2557
 
 
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);
 
2561
  
 
2562
  x_loc = day_rect.x + (day_rect.width - logical_rect.width) / 2;
 
2563
  y_loc = day_rect.y;
 
2564
 
 
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);
 
2568
 
 
2569
  if (calendar->day_month[row][col] == MONTH_CURRENT &&
 
2570
     (calendar->marked_date[day-1] || (detail && !show_details)))
 
2571
    {
 
2572
      cairo_move_to (cr, x_loc - 1, y_loc);
 
2573
      pango_cairo_show_layout (cr, layout);
 
2574
    }
 
2575
 
 
2576
  y_loc += priv->max_day_char_descent;
 
2577
 
 
2578
  if (priv->detail_func && show_details)
 
2579
    {
 
2580
      cairo_save (cr);
 
2581
 
 
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]);
 
2586
      else
 
2587
        gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_INSENSITIVE]);
 
2588
 
 
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);
 
2592
      cairo_stroke (cr);
 
2593
 
 
2594
      cairo_restore (cr);
 
2595
 
 
2596
      y_loc += 2;
 
2597
    }
 
2598
 
 
2599
  if (detail && show_details)
 
2600
    {
 
2601
      gint i, n_lines;
 
2602
 
 
2603
      gchar *markup = g_strconcat ("<small>", detail, "</small>", NULL);
 
2604
      pango_layout_set_markup (layout, markup, -1);
 
2605
      g_free (markup);
 
2606
 
 
2607
      if (day == calendar->selected_day)
 
2608
        {
 
2609
          /* Stripping colors as they conflict with selection marking. */
 
2610
 
 
2611
          PangoAttrList *attrs = pango_layout_get_attributes (layout);
 
2612
          PangoAttrList *colors = NULL;
 
2613
 
 
2614
          if (attrs)
 
2615
            colors = pango_attr_list_filter (attrs, is_color_attribute, NULL);
 
2616
          if (colors)
 
2617
            pango_attr_list_unref (colors);
 
2618
        }
 
2619
 
 
2620
      if (priv->detail_width_chars)
 
2621
        {
 
2622
          pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
 
2623
          pango_layout_set_width (layout, PANGO_SCALE * priv->min_day_width);
 
2624
        }
 
2625
 
 
2626
      n_lines = pango_layout_get_line_count (layout);
 
2627
 
 
2628
      if (priv->detail_height_rows &&
 
2629
          n_lines > priv->detail_height_rows)
 
2630
        {
 
2631
          n_lines = priv->detail_height_rows;
 
2632
          overflow = TRUE;
 
2633
        }
 
2634
 
 
2635
      for (i = 0; i < n_lines; ++i)
 
2636
        {
 
2637
          PangoLayoutLine *line = pango_layout_get_line_readonly (layout, i);
 
2638
          pango_layout_line_get_pixel_extents (line, NULL, &logical_rect);
 
2639
 
 
2640
          x_loc  = day_rect.x + (day_rect.width - logical_rect.width) / 2;
 
2641
          y_loc += PANGO_ASCENT (logical_rect);
 
2642
 
 
2643
          cairo_move_to (cr, x_loc, y_loc);
 
2644
          pango_cairo_show_layout_line (cr, line);
 
2645
 
 
2646
          y_loc += PANGO_DESCENT (logical_rect);
 
2647
        }
 
2648
    }
 
2649
 
 
2650
  if (GTK_WIDGET_HAS_FOCUS (calendar) 
 
2651
      && calendar->focus_row == row && calendar->focus_col == col)
 
2652
    {
 
2653
      GtkStateType state;
 
2654
 
 
2655
      if (calendar->selected_day == day)
 
2656
        state = GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
 
2657
      else
 
2658
        state = GTK_STATE_NORMAL;
 
2659
      
 
2660
      gtk_paint_focus (widget->style, 
 
2661
                       priv->main_win,
 
2662
                       state,
 
2663
                       NULL, widget, "calendar-day",
 
2664
                       day_rect.x,     day_rect.y, 
 
2665
                       day_rect.width, day_rect.height);
 
2666
    }
 
2667
 
 
2668
  if (overflow)
 
2669
    priv->detail_overflow[row] |= (1 << col);
 
2670
  else
 
2671
    priv->detail_overflow[row] &= ~(1 << col);
 
2672
 
 
2673
  g_object_unref (layout);
 
2674
  cairo_destroy (cr);
 
2675
  g_free (detail);
 
2676
}
 
2677
 
 
2678
static void
 
2679
calendar_paint_main (GlomGtkCalendar *calendar)
 
2680
{
 
2681
  gint row, col;
 
2682
  
 
2683
  for (col = 0; col < 7; col++)
 
2684
    for (row = 0; row < 6; row++)
 
2685
      calendar_paint_day (calendar, row, col);
 
2686
}
 
2687
 
 
2688
static void
 
2689
calendar_invalidate_arrow (GlomGtkCalendar *calendar,
 
2690
                           guint        arrow)
 
2691
{
 
2692
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
2693
  GdkWindow *window;
 
2694
  
 
2695
  window = priv->arrow_win[arrow];
 
2696
  if (window)
 
2697
    gdk_window_invalidate_rect (window, NULL, FALSE);
 
2698
}
 
2699
 
 
2700
static void
 
2701
calendar_paint_arrow (GlomGtkCalendar *calendar,
 
2702
                      guint            arrow)
 
2703
{
 
2704
  GtkWidget *widget = GTK_WIDGET (calendar);
 
2705
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
2706
  GdkWindow *window;
 
2707
  
 
2708
  window = priv->arrow_win[arrow];
 
2709
  if (window)
 
2710
    {
 
2711
      cairo_t *cr = gdk_cairo_create (window);
 
2712
      gint width, height;
 
2713
      gint state;
 
2714
        
 
2715
      state = priv->arrow_state[arrow];
 
2716
 
 
2717
      gdk_cairo_set_source_color (cr, &widget->style->bg[state]);
 
2718
      cairo_paint (cr);
 
2719
      cairo_destroy (cr);
 
2720
      
 
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);
 
2727
      else 
 
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);
 
2732
    }
 
2733
}
 
2734
 
 
2735
static gboolean
 
2736
glom_gtk_calendar_expose (GtkWidget         *widget,
 
2737
                     GdkEventExpose *event)
 
2738
{
 
2739
  GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
 
2740
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
2741
  int i;
 
2742
 
 
2743
  if (GTK_WIDGET_DRAWABLE (widget))
 
2744
    {
 
2745
      if (event->window == priv->main_win)
 
2746
        calendar_paint_main (calendar);
 
2747
      
 
2748
      if (event->window == priv->header_win)
 
2749
        calendar_paint_header (calendar);
 
2750
 
 
2751
      for (i = 0; i < 4; i++)
 
2752
        if (event->window == priv->arrow_win[i])
 
2753
          calendar_paint_arrow (calendar, i);
 
2754
      
 
2755
      if (event->window == priv->day_name_win)
 
2756
        calendar_paint_day_names (calendar);
 
2757
      
 
2758
      if (event->window == priv->week_win)
 
2759
        calendar_paint_week_numbers (calendar);
 
2760
      if (event->window == widget->window)
 
2761
        {
 
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);
 
2765
        }
 
2766
    }
 
2767
  
 
2768
  return FALSE;
 
2769
}
 
2770
 
 
2771
 
 
2772
/****************************************
 
2773
 *           Mouse handling             *
 
2774
 ****************************************/
 
2775
 
 
2776
static void
 
2777
calendar_arrow_action (GlomGtkCalendar *calendar,
 
2778
                       guint        arrow)
 
2779
{
 
2780
  switch (arrow)
 
2781
    {
 
2782
    case ARROW_YEAR_LEFT:
 
2783
      calendar_set_year_prev (calendar);
 
2784
      break;
 
2785
    case ARROW_YEAR_RIGHT:
 
2786
      calendar_set_year_next (calendar);
 
2787
      break;
 
2788
    case ARROW_MONTH_LEFT:
 
2789
      calendar_set_month_prev (calendar);
 
2790
      break;
 
2791
    case ARROW_MONTH_RIGHT:
 
2792
      calendar_set_month_next (calendar);
 
2793
      break;
 
2794
    default:;
 
2795
      /* do nothing */
 
2796
    }
 
2797
}
 
2798
 
 
2799
static gboolean
 
2800
calendar_timer (gpointer data)
 
2801
{
 
2802
  GlomGtkCalendar *calendar = data;
 
2803
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
2804
  gboolean retval = FALSE;
 
2805
  
 
2806
  if (priv->timer)
 
2807
    {
 
2808
      calendar_arrow_action (calendar, priv->click_child);
 
2809
 
 
2810
      if (priv->need_timer)
 
2811
        {
 
2812
          GtkSettings *settings;
 
2813
          guint        timeout;
 
2814
 
 
2815
          settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
 
2816
          g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
 
2817
 
 
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);
 
2823
        }
 
2824
      else 
 
2825
        retval = TRUE;
 
2826
    }
 
2827
 
 
2828
  return retval;
 
2829
}
 
2830
 
 
2831
static void
 
2832
calendar_start_spinning (GlomGtkCalendar *calendar,
 
2833
                         gint         click_child)
 
2834
{
 
2835
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
2836
 
 
2837
  priv->click_child = click_child;
 
2838
  
 
2839
  if (!priv->timer)
 
2840
    {
 
2841
      GtkSettings *settings;
 
2842
      guint        timeout;
 
2843
 
 
2844
      settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
 
2845
      g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
 
2846
 
 
2847
      priv->need_timer = TRUE;
 
2848
      priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
 
2849
                                        timeout,
 
2850
                                        (GSourceFunc) calendar_timer,
 
2851
                                        (gpointer) calendar, NULL);
 
2852
    }
 
2853
}
 
2854
 
 
2855
static void
 
2856
calendar_stop_spinning (GlomGtkCalendar *calendar)
 
2857
{
 
2858
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
2859
 
 
2860
  if (priv->timer)
 
2861
    {
 
2862
      g_source_remove (priv->timer);
 
2863
      priv->timer = 0;
 
2864
      priv->need_timer = FALSE;
 
2865
    }
 
2866
}
 
2867
 
 
2868
static void
 
2869
calendar_main_button_press (GlomGtkCalendar        *calendar,
 
2870
                            GdkEventButton *event)
 
2871
{
 
2872
  GtkWidget *widget = GTK_WIDGET (calendar);
 
2873
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
2874
  gint x, y;
 
2875
  gint row, col;
 
2876
  gint day_month;
 
2877
  gint day;
 
2878
  
 
2879
  x = (gint) (event->x);
 
2880
  y = (gint) (event->y);
 
2881
  
 
2882
  row = calendar_row_from_y (calendar, y);
 
2883
  col = calendar_column_from_x (calendar, x);
 
2884
 
 
2885
  /* If row or column isn't found, just return. */
 
2886
  if (row == -1 || col == -1)
 
2887
    return;
 
2888
  
 
2889
  day_month = calendar->day_month[row][col];
 
2890
 
 
2891
  if (event->type == GDK_BUTTON_PRESS)
 
2892
    {
 
2893
      day = calendar->day[row][col];
 
2894
      
 
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);
 
2899
      
 
2900
      if (!GTK_WIDGET_HAS_FOCUS (widget))
 
2901
        gtk_widget_grab_focus (widget);
 
2902
          
 
2903
      if (event->button == 1) 
 
2904
        {
 
2905
          priv->in_drag = 1;
 
2906
          priv->drag_start_x = x;
 
2907
          priv->drag_start_y = y;
 
2908
        }
 
2909
 
 
2910
      calendar_select_and_focus_day (calendar, day);
 
2911
    }
 
2912
  else if (event->type == GDK_2BUTTON_PRESS)
 
2913
    {
 
2914
      priv->in_drag = 0;
 
2915
      if (day_month == MONTH_CURRENT)
 
2916
        g_signal_emit (calendar,
 
2917
                       glom_gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL],
 
2918
                       0);
 
2919
    }
 
2920
}
 
2921
 
 
2922
static gboolean
 
2923
glom_gtk_calendar_button_press (GtkWidget         *widget,
 
2924
                           GdkEventButton *event)
 
2925
{
 
2926
  GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
 
2927
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
2928
  gint arrow = -1;
 
2929
  
 
2930
  if (event->window == priv->main_win)
 
2931
    calendar_main_button_press (calendar, event);
 
2932
 
 
2933
  if (!GTK_WIDGET_HAS_FOCUS (widget))
 
2934
    gtk_widget_grab_focus (widget);
 
2935
 
 
2936
  for (arrow = ARROW_YEAR_LEFT; arrow <= ARROW_MONTH_RIGHT; arrow++)
 
2937
    {
 
2938
      if (event->window == priv->arrow_win[arrow])
 
2939
        {
 
2940
          
 
2941
          /* only call the action on single click, not double */
 
2942
          if (event->type == GDK_BUTTON_PRESS)
 
2943
            {
 
2944
              if (event->button == 1)
 
2945
                calendar_start_spinning (calendar, arrow);
 
2946
 
 
2947
              calendar_arrow_action (calendar, arrow);        
 
2948
            }
 
2949
 
 
2950
          return TRUE;
 
2951
        }
 
2952
    }
 
2953
 
 
2954
  return FALSE;
 
2955
}
 
2956
 
 
2957
static gboolean
 
2958
glom_gtk_calendar_button_release (GtkWidget       *widget,
 
2959
                             GdkEventButton *event)
 
2960
{
 
2961
  GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
 
2962
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
2963
 
 
2964
  if (event->button == 1) 
 
2965
    {
 
2966
      calendar_stop_spinning (calendar);
 
2967
 
 
2968
      if (priv->in_drag)
 
2969
        priv->in_drag = 0;
 
2970
    }
 
2971
 
 
2972
  return TRUE;
 
2973
}
 
2974
 
 
2975
static gboolean
 
2976
glom_gtk_calendar_motion_notify (GtkWidget         *widget,
 
2977
                            GdkEventMotion *event)
 
2978
{
 
2979
  GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
 
2980
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
2981
  gint event_x, event_y;
 
2982
  gint row, col;
 
2983
  gint old_row, old_col;
 
2984
  
 
2985
  event_x = (gint) (event->x);
 
2986
  event_y = (gint) (event->y);
 
2987
  
 
2988
  if (event->window == priv->main_win)
 
2989
    {
 
2990
      
 
2991
      if (priv->in_drag) 
 
2992
        {
 
2993
          if (gtk_drag_check_threshold (widget,
 
2994
                                        priv->drag_start_x, priv->drag_start_y,
 
2995
                                        event->x, event->y))
 
2996
            {
 
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);
 
3002
 
 
3003
          
 
3004
              priv->in_drag = 0;
 
3005
              
 
3006
              gtk_target_list_unref (target_list);
 
3007
              gtk_drag_set_icon_default (context);
 
3008
            }
 
3009
        }
 
3010
      else 
 
3011
        {
 
3012
          row = calendar_row_from_y (calendar, event_y);
 
3013
          col = calendar_column_from_x (calendar, event_x);
 
3014
          
 
3015
          if (row != calendar->highlight_row || calendar->highlight_col != col)
 
3016
            {
 
3017
              old_row = calendar->highlight_row;
 
3018
              old_col = calendar->highlight_col;
 
3019
              if (old_row > -1 && old_col > -1)
 
3020
                {
 
3021
                  calendar->highlight_row = -1;
 
3022
                  calendar->highlight_col = -1;
 
3023
                  calendar_invalidate_day (calendar, old_row, old_col);
 
3024
                }
 
3025
              
 
3026
              calendar->highlight_row = row;
 
3027
              calendar->highlight_col = col;
 
3028
              
 
3029
              if (row > -1 && col > -1)
 
3030
                calendar_invalidate_day (calendar, row, col);
 
3031
            }
 
3032
        }
 
3033
    }
 
3034
  return TRUE;
 
3035
}
 
3036
 
 
3037
static gboolean
 
3038
glom_gtk_calendar_enter_notify (GtkWidget           *widget,
 
3039
                           GdkEventCrossing *event)
 
3040
{
 
3041
  GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
 
3042
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
3043
  
 
3044
  if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
 
3045
    {
 
3046
      priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_PRELIGHT;
 
3047
      calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
 
3048
    }
 
3049
  
 
3050
  if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
 
3051
    {
 
3052
      priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_PRELIGHT;
 
3053
      calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
 
3054
    }
 
3055
  
 
3056
  if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
 
3057
    {
 
3058
      priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_PRELIGHT;
 
3059
      calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
 
3060
    }
 
3061
  
 
3062
  if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
 
3063
    {
 
3064
      priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_PRELIGHT;
 
3065
      calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
 
3066
    }
 
3067
  
 
3068
  return TRUE;
 
3069
}
 
3070
 
 
3071
static gboolean
 
3072
glom_gtk_calendar_leave_notify (GtkWidget           *widget,
 
3073
                           GdkEventCrossing *event)
 
3074
{
 
3075
  GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
 
3076
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
3077
  gint row;
 
3078
  gint col;
 
3079
  
 
3080
  if (event->window == priv->main_win)
 
3081
    {
 
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);
 
3088
    }
 
3089
  
 
3090
  if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
 
3091
    {
 
3092
      priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_NORMAL;
 
3093
      calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
 
3094
    }
 
3095
  
 
3096
  if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
 
3097
    {
 
3098
      priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_NORMAL;
 
3099
      calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
 
3100
    }
 
3101
  
 
3102
  if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
 
3103
    {
 
3104
      priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_NORMAL;
 
3105
      calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
 
3106
    }
 
3107
  
 
3108
  if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
 
3109
    {
 
3110
      priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_NORMAL;
 
3111
      calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
 
3112
    }
 
3113
  
 
3114
  return TRUE;
 
3115
}
 
3116
 
 
3117
static gboolean
 
3118
glom_gtk_calendar_scroll (GtkWidget      *widget,
 
3119
                     GdkEventScroll *event)
 
3120
{
 
3121
  GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
 
3122
 
 
3123
  if (event->direction == GDK_SCROLL_UP) 
 
3124
    {
 
3125
      if (!GTK_WIDGET_HAS_FOCUS (widget))
 
3126
        gtk_widget_grab_focus (widget);
 
3127
      calendar_set_month_prev (calendar);
 
3128
    }
 
3129
  else if (event->direction == GDK_SCROLL_DOWN) 
 
3130
    {
 
3131
      if (!GTK_WIDGET_HAS_FOCUS (widget))
 
3132
        gtk_widget_grab_focus (widget);
 
3133
      calendar_set_month_next (calendar);
 
3134
    }
 
3135
  else
 
3136
    return FALSE;
 
3137
 
 
3138
  return TRUE;
 
3139
}
 
3140
 
 
3141
 
 
3142
/****************************************
 
3143
 *             Key handling              *
 
3144
 ****************************************/
 
3145
 
 
3146
static void 
 
3147
move_focus (GlomGtkCalendar *calendar, 
 
3148
            gint         direction)
 
3149
{
 
3150
  GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
 
3151
 
 
3152
  if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
 
3153
      (text_dir == GTK_TEXT_DIR_RTL && direction == 1)) 
 
3154
    {
 
3155
      if (calendar->focus_col > 0)
 
3156
          calendar->focus_col--;
 
3157
      else if (calendar->focus_row > 0)
 
3158
        {
 
3159
          calendar->focus_col = 6;
 
3160
          calendar->focus_row--;
 
3161
        }
 
3162
 
 
3163
      if (calendar->focus_col < 0)
 
3164
        calendar->focus_col = 6;
 
3165
      if (calendar->focus_row < 0)
 
3166
        calendar->focus_row = 5;
 
3167
    }
 
3168
  else 
 
3169
    {
 
3170
      if (calendar->focus_col < 6)
 
3171
        calendar->focus_col++;
 
3172
      else if (calendar->focus_row < 5)
 
3173
        {
 
3174
          calendar->focus_col = 0;
 
3175
          calendar->focus_row++;
 
3176
        }
 
3177
 
 
3178
      if (calendar->focus_col < 0)
 
3179
        calendar->focus_col = 0;
 
3180
      if (calendar->focus_row < 0)
 
3181
        calendar->focus_row = 0;
 
3182
    }
 
3183
}
 
3184
 
 
3185
static gboolean
 
3186
glom_gtk_calendar_key_press (GtkWidget   *widget,
 
3187
                        GdkEventKey *event)
 
3188
{
 
3189
  GlomGtkCalendar *calendar;
 
3190
  gint return_val;
 
3191
  gint old_focus_row;
 
3192
  gint old_focus_col;
 
3193
  gint row, col, day;
 
3194
  
 
3195
  calendar = GLOM_GTK_CALENDAR (widget);
 
3196
  return_val = FALSE;
 
3197
  
 
3198
  old_focus_row = calendar->focus_row;
 
3199
  old_focus_col = calendar->focus_col;
 
3200
 
 
3201
  switch (event->keyval)
 
3202
    {
 
3203
    case GDK_KP_Left:
 
3204
    case GDK_Left:
 
3205
      return_val = TRUE;
 
3206
      if (event->state & GDK_CONTROL_MASK)
 
3207
        calendar_set_month_prev (calendar);
 
3208
      else
 
3209
        {
 
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);
 
3214
        }
 
3215
      break;
 
3216
    case GDK_KP_Right:
 
3217
    case GDK_Right:
 
3218
      return_val = TRUE;
 
3219
      if (event->state & GDK_CONTROL_MASK)
 
3220
        calendar_set_month_next (calendar);
 
3221
      else
 
3222
        {
 
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);
 
3227
        }
 
3228
      break;
 
3229
    case GDK_KP_Up:
 
3230
    case GDK_Up:
 
3231
      return_val = TRUE;
 
3232
      if (event->state & GDK_CONTROL_MASK)
 
3233
        calendar_set_year_prev (calendar);
 
3234
      else
 
3235
        {
 
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);
 
3245
        }
 
3246
      break;
 
3247
    case GDK_KP_Down:
 
3248
    case GDK_Down:
 
3249
      return_val = TRUE;
 
3250
      if (event->state & GDK_CONTROL_MASK)
 
3251
        calendar_set_year_next (calendar);
 
3252
      else
 
3253
        {
 
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);
 
3261
        }
 
3262
      break;
 
3263
    case GDK_KP_Space:
 
3264
    case GDK_space:
 
3265
      row = calendar->focus_row;
 
3266
      col = calendar->focus_col;
 
3267
      
 
3268
      if (row > -1 && col > -1)
 
3269
        {
 
3270
          return_val = TRUE;
 
3271
 
 
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);
 
3277
 
 
3278
          calendar_select_and_focus_day (calendar, day);
 
3279
        }
 
3280
    }   
 
3281
  
 
3282
  return return_val;
 
3283
}
 
3284
 
 
3285
 
 
3286
/****************************************
 
3287
 *           Misc widget methods        *
 
3288
 ****************************************/
 
3289
 
 
3290
static void
 
3291
calendar_set_background (GtkWidget *widget)
 
3292
{
 
3293
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
3294
  gint i;
 
3295
  
 
3296
  if (GTK_WIDGET_REALIZED (widget))
 
3297
    {
 
3298
      for (i = 0; i < 4; i++)
 
3299
        {
 
3300
          if (priv->arrow_win[i])
 
3301
            gdk_window_set_background (priv->arrow_win[i], 
 
3302
                                       HEADER_BG_COLOR (widget));
 
3303
        }
 
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));
 
3310
      if (priv->week_win)
 
3311
        gdk_window_set_background (priv->week_win,
 
3312
                                   BACKGROUND_COLOR (widget));
 
3313
      if (priv->main_win)
 
3314
        gdk_window_set_background (priv->main_win,
 
3315
                                   BACKGROUND_COLOR (widget));
 
3316
      if (widget->window)
 
3317
        gdk_window_set_background (widget->window,
 
3318
                                   BACKGROUND_COLOR (widget)); 
 
3319
    }
 
3320
}
 
3321
 
 
3322
static void
 
3323
glom_gtk_calendar_style_set (GtkWidget *widget,
 
3324
                        GtkStyle  *previous_style)
 
3325
{
 
3326
  if (previous_style && GTK_WIDGET_REALIZED (widget))
 
3327
    calendar_set_background (widget);
 
3328
}
 
3329
 
 
3330
static void
 
3331
glom_gtk_calendar_state_changed (GtkWidget         *widget,
 
3332
                            GtkStateType    previous_state)
 
3333
{
 
3334
  GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
 
3335
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
3336
  int i;
 
3337
  
 
3338
  if (!GTK_WIDGET_IS_SENSITIVE (widget))
 
3339
    {
 
3340
      priv->in_drag = 0;
 
3341
      calendar_stop_spinning (calendar);    
 
3342
    }
 
3343
 
 
3344
  for (i = 0; i < 4; i++)
 
3345
    if (GTK_WIDGET_IS_SENSITIVE (widget))
 
3346
      priv->arrow_state[i] = GTK_STATE_NORMAL;
 
3347
    else 
 
3348
      priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
 
3349
  
 
3350
  calendar_set_background (widget);
 
3351
}
 
3352
 
 
3353
static void
 
3354
glom_gtk_calendar_grab_notify (GtkWidget *widget,
 
3355
                          gboolean   was_grabbed)
 
3356
{
 
3357
  if (!was_grabbed)
 
3358
    calendar_stop_spinning (GLOM_GTK_CALENDAR (widget));
 
3359
}
 
3360
 
 
3361
static gboolean
 
3362
glom_gtk_calendar_focus_out (GtkWidget     *widget,
 
3363
                        GdkEventFocus *event)
 
3364
{
 
3365
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
3366
  GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
 
3367
 
 
3368
  calendar_queue_refresh (calendar);
 
3369
  calendar_stop_spinning (calendar);
 
3370
  
 
3371
  priv->in_drag = 0; 
 
3372
 
 
3373
  return FALSE;
 
3374
}
 
3375
 
 
3376
 
 
3377
/****************************************
 
3378
 *          Drag and Drop               *
 
3379
 ****************************************/
 
3380
 
 
3381
static void
 
3382
glom_gtk_calendar_drag_data_get (GtkWidget        *widget,
 
3383
                            GdkDragContext   *context,
 
3384
                            GtkSelectionData *selection_data,
 
3385
                            guint             info,
 
3386
                            guint             time)
 
3387
{
 
3388
  GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
 
3389
  GDate *date;
 
3390
  gchar str[128];
 
3391
  gsize len;
 
3392
 
 
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);
 
3396
  
 
3397
  g_free (date);
 
3398
}
 
3399
 
 
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.
 
3403
 */
 
3404
static void
 
3405
set_status_pending (GdkDragContext *context,
 
3406
                    GdkDragAction   suggested_action)
 
3407
{
 
3408
  g_object_set_data (G_OBJECT (context),
 
3409
                     I_("gtk-calendar-status-pending"),
 
3410
                     GINT_TO_POINTER (suggested_action));
 
3411
}
 
3412
 
 
3413
static GdkDragAction
 
3414
get_status_pending (GdkDragContext *context)
 
3415
{
 
3416
  return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
 
3417
                                             "gtk-calendar-status-pending"));
 
3418
}
 
3419
 
 
3420
static void
 
3421
glom_gtk_calendar_drag_leave (GtkWidget      *widget,
 
3422
                         GdkDragContext *context,
 
3423
                         guint           time)
 
3424
{
 
3425
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
3426
 
 
3427
  priv->drag_highlight = 0;
 
3428
  gtk_drag_unhighlight (widget);
 
3429
  
 
3430
}
 
3431
 
 
3432
static gboolean
 
3433
glom_gtk_calendar_drag_motion (GtkWidget      *widget,
 
3434
                          GdkDragContext *context,
 
3435
                          gint            x,
 
3436
                          gint            y,
 
3437
                          guint           time)
 
3438
{
 
3439
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (widget);
 
3440
  GdkAtom target;
 
3441
  
 
3442
  if (!priv->drag_highlight) 
 
3443
    {
 
3444
      priv->drag_highlight = 1;
 
3445
      gtk_drag_highlight (widget);
 
3446
    }
 
3447
  
 
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);
 
3451
  else
 
3452
    {
 
3453
      set_status_pending (context, context->suggested_action);
 
3454
      gtk_drag_get_data (widget, context, target, time);
 
3455
    }
 
3456
  
 
3457
  return TRUE;
 
3458
}
 
3459
 
 
3460
static gboolean
 
3461
glom_gtk_calendar_drag_drop (GtkWidget      *widget,
 
3462
                        GdkDragContext *context,
 
3463
                        gint            x,
 
3464
                        gint            y,
 
3465
                        guint           time)
 
3466
{
 
3467
  GdkAtom target;
 
3468
 
 
3469
  target = gtk_drag_dest_find_target (widget, context, NULL);  
 
3470
  if (target != GDK_NONE)
 
3471
    {
 
3472
      gtk_drag_get_data (widget, context, 
 
3473
                         target, 
 
3474
                         time);
 
3475
      return TRUE;
 
3476
    }
 
3477
 
 
3478
  return FALSE;
 
3479
}
 
3480
 
 
3481
static void
 
3482
glom_gtk_calendar_drag_data_received (GtkWidget        *widget,
 
3483
                                 GdkDragContext   *context,
 
3484
                                 gint              x,
 
3485
                                 gint              y,
 
3486
                                 GtkSelectionData *selection_data,
 
3487
                                 guint             info,
 
3488
                                 guint             time)
 
3489
{
 
3490
  GlomGtkCalendar *calendar = GLOM_GTK_CALENDAR (widget);
 
3491
  guint day, month, year;
 
3492
  gchar *str;
 
3493
  GDate *date;
 
3494
  GdkDragAction suggested_action;
 
3495
 
 
3496
  suggested_action = get_status_pending (context);
 
3497
 
 
3498
  if (suggested_action) 
 
3499
    {
 
3500
      set_status_pending (context, 0);
 
3501
     
 
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
 
3505
       * data.
 
3506
       */
 
3507
      str = (gchar*) gtk_selection_data_get_text (selection_data);
 
3508
 
 
3509
      if (str) 
 
3510
        {
 
3511
          date = g_date_new ();
 
3512
          g_date_set_parse (date, str);
 
3513
          if (!g_date_valid (date)) 
 
3514
              suggested_action = 0;
 
3515
          g_date_free (date);
 
3516
          g_free (str);
 
3517
        }
 
3518
      else
 
3519
        suggested_action = 0;
 
3520
 
 
3521
      gdk_drag_status (context, suggested_action, time);
 
3522
 
 
3523
      return;
 
3524
    }
 
3525
 
 
3526
  date = g_date_new ();
 
3527
  str = (gchar*) gtk_selection_data_get_text (selection_data);
 
3528
  if (str) 
 
3529
    {
 
3530
      g_date_set_parse (date, str);
 
3531
      g_free (str);
 
3532
    }
 
3533
  
 
3534
  if (!g_date_valid (date)) 
 
3535
    {
 
3536
      g_warning ("Received invalid date data\n");
 
3537
      g_date_free (date);       
 
3538
      gtk_drag_finish (context, FALSE, FALSE, time);
 
3539
      return;
 
3540
    }
 
3541
 
 
3542
  day = g_date_get_day (date);
 
3543
  month = g_date_get_month (date);
 
3544
  year = g_date_get_year (date);
 
3545
  g_date_free (date);   
 
3546
 
 
3547
  gtk_drag_finish (context, TRUE, FALSE, time);
 
3548
 
 
3549
  
 
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));  
 
3556
}
 
3557
 
 
3558
 
 
3559
/****************************************
 
3560
 *              Public API              *
 
3561
 ****************************************/
 
3562
 
 
3563
/**
 
3564
 * glom_gtk_calendar_new:
 
3565
 * 
 
3566
 * Creates a new calendar, with the current date being selected. 
 
3567
 * 
 
3568
 * Return value: a newly #GlomGtkCalendar widget
 
3569
 **/
 
3570
GtkWidget*
 
3571
glom_gtk_calendar_new (void)
 
3572
{
 
3573
  return g_object_new (GLOM_GTK_TYPE_CALENDAR, NULL);
 
3574
}
 
3575
 
 
3576
/**
 
3577
 * glom_gtk_calendar_display_options:
 
3578
 * @calendar: a #GlomGtkCalendar.
 
3579
 * @flags: the display options to set.
 
3580
 *
 
3581
 * Sets display options (whether to display the heading and the month headings).
 
3582
 * 
 
3583
 * Deprecated: 2.4: Use glom_gtk_calendar_set_display_options() instead
 
3584
 **/
 
3585
void
 
3586
glom_gtk_calendar_display_options (GlomGtkCalendar             *calendar,
 
3587
                              GlomGtkCalendarDisplayOptions flags)
 
3588
{
 
3589
  glom_gtk_calendar_set_display_options (calendar, flags);
 
3590
}
 
3591
 
 
3592
/**
 
3593
 * glom_gtk_calendar_get_display_options:
 
3594
 * @calendar: a #GlomGtkCalendar
 
3595
 * 
 
3596
 * Returns the current display options of @calendar. 
 
3597
 * 
 
3598
 * Return value: the display options.
 
3599
 *
 
3600
 * Since: 2.4
 
3601
 **/
 
3602
GlomGtkCalendarDisplayOptions 
 
3603
glom_gtk_calendar_get_display_options (GlomGtkCalendar         *calendar)
 
3604
{
 
3605
  g_return_val_if_fail (GLOM_GTK_IS_CALENDAR (calendar), 0);
 
3606
 
 
3607
  return calendar->display_flags;
 
3608
}
 
3609
 
 
3610
/**
 
3611
 * glom_gtk_calendar_set_display_options:
 
3612
 * @calendar: a #GlomGtkCalendar
 
3613
 * @flags: the display options to set
 
3614
 * 
 
3615
 * Sets display options (whether to display the heading and the month  
 
3616
 * headings).
 
3617
 *
 
3618
 * Since: 2.4
 
3619
 **/
 
3620
void
 
3621
glom_gtk_calendar_set_display_options (GlomGtkCalendar         *calendar,
 
3622
                                  GlomGtkCalendarDisplayOptions flags)
 
3623
{
 
3624
  GtkWidget *widget = GTK_WIDGET (calendar);
 
3625
  GlomGtkCalendarPrivate *priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
3626
  gint resize = 0;
 
3627
  gint i;
 
3628
  GlomGtkCalendarDisplayOptions old_flags;
 
3629
  
 
3630
  g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
 
3631
  
 
3632
  old_flags = calendar->display_flags;
 
3633
  
 
3634
  if (GTK_WIDGET_REALIZED (widget))
 
3635
    {
 
3636
      if ((flags ^ calendar->display_flags) & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
 
3637
        {
 
3638
          resize ++;
 
3639
          if (! (flags & GLOM_GTK_CALENDAR_NO_MONTH_CHANGE)
 
3640
              && (priv->header_win))
 
3641
            {
 
3642
              calendar->display_flags &= ~GLOM_GTK_CALENDAR_NO_MONTH_CHANGE;
 
3643
              calendar_realize_arrows (calendar);
 
3644
            }
 
3645
          else
 
3646
            {
 
3647
              for (i = 0; i < 4; i++)
 
3648
                {
 
3649
                  if (priv->arrow_win[i])
 
3650
                    {
 
3651
                      gdk_window_set_user_data (priv->arrow_win[i], 
 
3652
                                                NULL);
 
3653
                      gdk_window_destroy (priv->arrow_win[i]);
 
3654
                      priv->arrow_win[i] = NULL;
 
3655
                    }
 
3656
                }
 
3657
            }
 
3658
        }
 
3659
      
 
3660
      if ((flags ^ calendar->display_flags) & GLOM_GTK_CALENDAR_SHOW_HEADING)
 
3661
        {
 
3662
          resize++;
 
3663
          
 
3664
          if (flags & GLOM_GTK_CALENDAR_SHOW_HEADING)
 
3665
            {
 
3666
              calendar->display_flags |= GLOM_GTK_CALENDAR_SHOW_HEADING;
 
3667
              calendar_realize_header (calendar);
 
3668
            }
 
3669
          else
 
3670
            {
 
3671
              for (i = 0; i < 4; i++)
 
3672
                {
 
3673
                  if (priv->arrow_win[i])
 
3674
                    {
 
3675
                      gdk_window_set_user_data (priv->arrow_win[i], 
 
3676
                                                NULL);
 
3677
                      gdk_window_destroy (priv->arrow_win[i]);
 
3678
                      priv->arrow_win[i] = NULL;
 
3679
                    }
 
3680
                }
 
3681
              gdk_window_set_user_data (priv->header_win, NULL);
 
3682
              gdk_window_destroy (priv->header_win);
 
3683
              priv->header_win = NULL;
 
3684
            }
 
3685
        }
 
3686
      
 
3687
      
 
3688
      if ((flags ^ calendar->display_flags) & GLOM_GTK_CALENDAR_SHOW_DAY_NAMES)
 
3689
        {
 
3690
          resize++;
 
3691
          
 
3692
          if (flags & GLOM_GTK_CALENDAR_SHOW_DAY_NAMES)
 
3693
            {
 
3694
              calendar->display_flags |= GLOM_GTK_CALENDAR_SHOW_DAY_NAMES;
 
3695
              calendar_realize_day_names (calendar);
 
3696
            }
 
3697
          else
 
3698
            {
 
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;
 
3702
            }
 
3703
        }
 
3704
      
 
3705
      if ((flags ^ calendar->display_flags) & GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS)
 
3706
        {
 
3707
          resize++;
 
3708
          
 
3709
          if (flags & GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS)
 
3710
            {
 
3711
              calendar->display_flags |= GLOM_GTK_CALENDAR_SHOW_WEEK_NUMBERS;
 
3712
              calendar_realize_week_numbers (calendar);
 
3713
            }
 
3714
          else
 
3715
            {
 
3716
              gdk_window_set_user_data (priv->week_win, NULL);
 
3717
              gdk_window_destroy (priv->week_win);
 
3718
              priv->week_win = NULL;
 
3719
            }
 
3720
        }
 
3721
 
 
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");
 
3724
      
 
3725
      if ((flags ^ calendar->display_flags) & GLOM_GTK_CALENDAR_SHOW_DETAILS)
 
3726
        resize++;
 
3727
 
 
3728
      calendar->display_flags = flags;
 
3729
      if (resize)
 
3730
        gtk_widget_queue_resize (GTK_WIDGET (calendar));
 
3731
      
 
3732
    } 
 
3733
  else
 
3734
    calendar->display_flags = flags;
 
3735
  
 
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));
 
3746
}
 
3747
 
 
3748
/**
 
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.
 
3753
 * 
 
3754
 * Shifts the calendar to a different month.
 
3755
 * 
 
3756
 * Return value: %TRUE, always
 
3757
 **/
 
3758
gboolean
 
3759
glom_gtk_calendar_select_month (GlomGtkCalendar *calendar,
 
3760
                           guint        month,
 
3761
                           guint        year)
 
3762
{
 
3763
  g_return_val_if_fail (GLOM_GTK_IS_CALENDAR (calendar), FALSE);
 
3764
  g_return_val_if_fail (month <= 11, FALSE);
 
3765
  
 
3766
  calendar->month = month;
 
3767
  calendar->year  = year;
 
3768
  
 
3769
  calendar_compute_days (calendar);
 
3770
  calendar_queue_refresh (calendar);
 
3771
 
 
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));
 
3776
 
 
3777
  g_signal_emit (calendar,
 
3778
                 glom_gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
 
3779
                 0);
 
3780
  return TRUE;
 
3781
}
 
3782
 
 
3783
/**
 
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.
 
3788
 * 
 
3789
 * Selects a day from the current month.
 
3790
 **/
 
3791
void
 
3792
glom_gtk_calendar_select_day (GlomGtkCalendar *calendar,
 
3793
                         guint        day)
 
3794
{
 
3795
  g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
 
3796
  g_return_if_fail (day <= 31);
 
3797
  
 
3798
  /* Deselect the old day */
 
3799
  if (calendar->selected_day > 0)
 
3800
    {
 
3801
      gint selected_day;
 
3802
      
 
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);
 
3807
    }
 
3808
  
 
3809
  calendar->selected_day = day;
 
3810
  
 
3811
  /* Select the new day */
 
3812
  if (day != 0)
 
3813
    {
 
3814
      if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
 
3815
        calendar_invalidate_day_num (calendar, day);
 
3816
    }
 
3817
  
 
3818
  g_object_notify (G_OBJECT (calendar), "day");
 
3819
 
 
3820
  g_signal_emit (calendar,
 
3821
                 glom_gtk_calendar_signals[DAY_SELECTED_SIGNAL],
 
3822
                 0);
 
3823
}
 
3824
 
 
3825
/**
 
3826
 * glom_gtk_calendar_clear_marks:
 
3827
 * @calendar: a #GlomGtkCalendar
 
3828
 * 
 
3829
 * Remove all visual markers.
 
3830
 **/
 
3831
void
 
3832
glom_gtk_calendar_clear_marks (GlomGtkCalendar *calendar)
 
3833
{
 
3834
  guint day;
 
3835
  
 
3836
  g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
 
3837
  
 
3838
  for (day = 0; day < 31; day++)
 
3839
    {
 
3840
      calendar->marked_date[day] = FALSE;
 
3841
    }
 
3842
 
 
3843
  calendar->num_marked_dates = 0;
 
3844
  calendar_queue_refresh (calendar);
 
3845
}
 
3846
 
 
3847
/**
 
3848
 * glom_gtk_calendar_mark_day:
 
3849
 * @calendar: a #GlomGtkCalendar 
 
3850
 * @day: the day number to mark between 1 and 31.
 
3851
 * 
 
3852
 * Places a visual marker on a particular day.
 
3853
 * 
 
3854
 * Return value: %TRUE, always
 
3855
 **/
 
3856
gboolean
 
3857
glom_gtk_calendar_mark_day (GlomGtkCalendar *calendar,
 
3858
                       guint        day)
 
3859
{
 
3860
  g_return_val_if_fail (GLOM_GTK_IS_CALENDAR (calendar), FALSE);
 
3861
  
 
3862
  if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == FALSE)
 
3863
    {
 
3864
      calendar->marked_date[day - 1] = TRUE;
 
3865
      calendar->num_marked_dates++;
 
3866
      calendar_invalidate_day_num (calendar, day);
 
3867
    }
 
3868
  
 
3869
  return TRUE;
 
3870
}
 
3871
 
 
3872
/**
 
3873
 * glom_gtk_calendar_unmark_day:
 
3874
 * @calendar: a #GlomGtkCalendar.
 
3875
 * @day: the day number to unmark between 1 and 31.
 
3876
 * 
 
3877
 * Removes the visual marker from a particular day.
 
3878
 * 
 
3879
 * Return value: %TRUE, always
 
3880
 **/
 
3881
gboolean
 
3882
glom_gtk_calendar_unmark_day (GlomGtkCalendar *calendar,
 
3883
                         guint        day)
 
3884
{
 
3885
  g_return_val_if_fail (GLOM_GTK_IS_CALENDAR (calendar), FALSE);
 
3886
  
 
3887
  if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == TRUE)
 
3888
    {
 
3889
      calendar->marked_date[day - 1] = FALSE;
 
3890
      calendar->num_marked_dates--;
 
3891
      calendar_invalidate_day_num (calendar, day);
 
3892
    }
 
3893
  
 
3894
  return TRUE;
 
3895
}
 
3896
 
 
3897
/**
 
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
 
3903
 * 
 
3904
 * Obtains the selected date from a #GlomGtkCalendar.
 
3905
 **/
 
3906
void
 
3907
glom_gtk_calendar_get_date (GlomGtkCalendar *calendar,
 
3908
                       guint       *year,
 
3909
                       guint       *month,
 
3910
                       guint       *day)
 
3911
{
 
3912
  g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
 
3913
  
 
3914
  if (year)
 
3915
    *year = calendar->year;
 
3916
  
 
3917
  if (month)
 
3918
    *month = calendar->month;
 
3919
  
 
3920
  if (day)
 
3921
    *day = calendar->selected_day;
 
3922
}
 
3923
 
 
3924
/**
 
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.
 
3930
 *
 
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
 
3936
 * is not set.
 
3937
 *
 
3938
 * The size of the details area can be restricted by setting the
 
3939
 * #GlomGtkCalendar:detail-width-chars and #GlomGtkCalendar:detail-height-rows
 
3940
 * properties.
 
3941
 *
 
3942
 * Since: 2.16
 
3943
 */
 
3944
void
 
3945
glom_gtk_calendar_set_detail_func (GlomGtkCalendar           *calendar,
 
3946
                              GlomGtkCalendarDetailFunc  func,
 
3947
                              gpointer               data,
 
3948
                              GDestroyNotify         destroy)
 
3949
{
 
3950
  GlomGtkCalendarPrivate *priv;
 
3951
 
 
3952
  g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
 
3953
 
 
3954
  priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
3955
 
 
3956
  if (priv->detail_func_destroy)
 
3957
    priv->detail_func_destroy (priv->detail_func_user_data);
 
3958
 
 
3959
  priv->detail_func = func;
 
3960
  priv->detail_func_user_data = data;
 
3961
  priv->detail_func_destroy = destroy;
 
3962
 
 
3963
  gtk_widget_set_has_tooltip (GTK_WIDGET (calendar),
 
3964
                              NULL != priv->detail_func);
 
3965
  gtk_widget_queue_resize (GTK_WIDGET (calendar));
 
3966
}
 
3967
 
 
3968
/**
 
3969
 * glom_gtk_calendar_set_detail_width_chars:
 
3970
 * @calendar: a #GlomGtkCalendar.
 
3971
 * @chars: detail width in characters.
 
3972
 *
 
3973
 * Updates the width of detail cells.
 
3974
 * See #GlomGtkCalendar:detail-width-chars.
 
3975
 *
 
3976
 * Since: 2.16
 
3977
 */
 
3978
void
 
3979
glom_gtk_calendar_set_detail_width_chars (GlomGtkCalendar *calendar,
 
3980
                                     gint         chars)
 
3981
{
 
3982
  GlomGtkCalendarPrivate *priv;
 
3983
 
 
3984
  g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
 
3985
 
 
3986
  priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
3987
 
 
3988
  if (chars != priv->detail_width_chars)
 
3989
    {
 
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));
 
3993
    }
 
3994
}
 
3995
 
 
3996
/**
 
3997
 * glom_gtk_calendar_set_detail_height_rows:
 
3998
 * @calendar: a #GlomGtkCalendar.
 
3999
 * @rows: detail height in rows.
 
4000
 *
 
4001
 * Updates the height of detail cells.
 
4002
 * See #GlomGtkCalendar:detail-height-rows.
 
4003
 *
 
4004
 * Since: 2.16
 
4005
 */
 
4006
void
 
4007
glom_gtk_calendar_set_detail_height_rows (GlomGtkCalendar *calendar,
 
4008
                                     gint         rows)
 
4009
{
 
4010
  GlomGtkCalendarPrivate *priv;
 
4011
 
 
4012
  g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
 
4013
 
 
4014
  priv = GLOM_GTK_CALENDAR_GET_PRIVATE (calendar);
 
4015
 
 
4016
  if (rows != priv->detail_height_rows)
 
4017
    {
 
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));
 
4021
    }
 
4022
}
 
4023
 
 
4024
/**
 
4025
 * glom_gtk_calendar_get_detail_width_chars:
 
4026
 * @calendar: a #GlomGtkCalendar.
 
4027
 *
 
4028
 * Queries the width of detail cells, in characters.
 
4029
 * See #GlomGtkCalendar:detail-width-chars.
 
4030
 *
 
4031
 * Since: 2.16
 
4032
 *
 
4033
 * Return value: The width of detail cells, in characters.
 
4034
 */
 
4035
gint
 
4036
glom_gtk_calendar_get_detail_width_chars (GlomGtkCalendar *calendar)
 
4037
{
 
4038
  g_return_val_if_fail (GLOM_GTK_IS_CALENDAR (calendar), 0);
 
4039
  return GLOM_GTK_CALENDAR_GET_PRIVATE (calendar)->detail_width_chars;
 
4040
}
 
4041
 
 
4042
/**
 
4043
 * glom_gtk_calendar_get_detail_height_rows:
 
4044
 * @calendar: a #GlomGtkCalendar.
 
4045
 *
 
4046
 * Queries the height of detail cells, in rows.
 
4047
 * See #GlomGtkCalendar:detail-width-chars.
 
4048
 *
 
4049
 * Since: 2.16
 
4050
 *
 
4051
 * Return value: The height of detail cells, in rows.
 
4052
 */
 
4053
gint
 
4054
glom_gtk_calendar_get_detail_height_rows (GlomGtkCalendar *calendar)
 
4055
{
 
4056
  g_return_val_if_fail (GLOM_GTK_IS_CALENDAR (calendar), 0);
 
4057
  return GLOM_GTK_CALENDAR_GET_PRIVATE (calendar)->detail_height_rows;
 
4058
}
 
4059
 
 
4060
/**
 
4061
 * glom_gtk_calendar_freeze:
 
4062
 * @calendar: a #GlomGtkCalendar
 
4063
 * 
 
4064
 * Does nothing. Previously locked the display of the calendar until
 
4065
 * it was thawed with glom_gtk_calendar_thaw().
 
4066
 *
 
4067
 * Deprecated: 2.8: 
 
4068
 **/
 
4069
void
 
4070
glom_gtk_calendar_freeze (GlomGtkCalendar *calendar)
 
4071
{
 
4072
  g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
 
4073
}
 
4074
 
 
4075
/**
 
4076
 * glom_gtk_calendar_thaw:
 
4077
 * @calendar: a #GlomGtkCalendar
 
4078
 * 
 
4079
 * Does nothing. Previously defrosted a calendar; all the changes made
 
4080
 * since the last glom_gtk_calendar_freeze() were displayed.
 
4081
 *
 
4082
 * Deprecated: 2.8: 
 
4083
 **/
 
4084
void
 
4085
glom_gtk_calendar_thaw (GlomGtkCalendar *calendar)
 
4086
{
 
4087
  g_return_if_fail (GLOM_GTK_IS_CALENDAR (calendar));
 
4088
}
 
4089
 
 
4090
#endif //!GTK_CHECK_VERSION(2,16,0)