gtkcalendar.c 85.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * GTK Calendar Widget
 * Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson and Mattias Grnlund
 * 
 * lib_date routines
 * Copyright (c) 1995, 1996, 1997, 1998 by Steffen Beyer
 *
 * This library is free software; you can redistribute it and/or
11
 * modify it under the terms of the GNU Lesser General Public
12
13
14
15
16
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
18
 * Lesser General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public
21
22
23
24
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

25
/*
Owen Taylor's avatar
Owen Taylor committed
26
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
27
28
29
30
31
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

32
33
34
#include "config.h"

#ifdef HAVE_SYS_TIME_H
35
#include <sys/time.h>
36
#endif
37
38
39
#include <string.h>
#include <stdlib.h>
#include <time.h>
40
#include <glib/gprintf.h>
Manish Singh's avatar
Manish Singh committed
41

42
#include "gtkcalendar.h"
Matthias Clasen's avatar
Matthias Clasen committed
43
#include "gtkintl.h"
44
45
#include "gtkmain.h"
#include "gtkmarshalers.h"
46
47
48
49
50
51
52
#include "gdk/gdkkeysyms.h"

/***************************************************************************/
/* The following date routines are taken from the lib_date package.  Keep
 * them seperate in case we want to update them if a newer lib_date comes
 * out with fixes.  */

53
54
55
typedef	 unsigned   int	    N_int;
typedef	 unsigned   long    N_long;
typedef	 signed	    long    Z_long;
56
57
typedef enum { false = FALSE , true = TRUE } boolean;

58
59
#define and	    &&	    /* logical (boolean) operators: lower case */
#define or	    ||
60

61
static const N_int month_length[2][13] =
62
{
63
64
  { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
  { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
65
66
};

67
static const N_int days_in_months[2][14] =
68
{
69
70
  { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
  { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
71
72
73
74
75
};

static Z_long  calc_days(N_int year, N_int mm, N_int dd);
static N_int   day_of_week(N_int year, N_int mm, N_int dd);
static Z_long  dates_difference(N_int year1, N_int mm1, N_int dd1,
76
				N_int year2, N_int mm2, N_int dd2);
77
78
79
80
81
static N_int   weeks_in_year(N_int year);

static boolean 
leap(N_int year)
{
82
  return((((year % 4) == 0) and ((year % 100) != 0)) or ((year % 400) == 0));
83
84
85
86
87
}

static N_int 
day_of_week(N_int year, N_int mm, N_int dd)
{
88
89
90
91
  Z_long  days;
  
  days = calc_days(year, mm, dd);
  if (days > 0L)
92
    {
93
94
95
      days--;
      days %= 7L;
      days++;
96
    }
97
  return( (N_int) days );
98
99
100
101
}

static N_int weeks_in_year(N_int year)
{
102
  return(52 + ((day_of_week(year,1,1)==4) or (day_of_week(year,12,31)==4)));
103
104
105
106
107
}

static boolean 
check_date(N_int year, N_int mm, N_int dd)
{
108
109
110
111
  if (year < 1) return(false);
  if ((mm < 1) or (mm > 12)) return(false);
  if ((dd < 1) or (dd > month_length[leap(year)][mm])) return(false);
  return(true);
112
113
114
115
116
}

static N_int 
week_number(N_int year, N_int mm, N_int dd)
{
117
118
119
120
121
  N_int first;
  
  first = day_of_week(year,1,1) - 1;
  return( (N_int) ( (dates_difference(year,1,1, year,mm,dd) + first) / 7L ) +
	  (first < 4) );
122
123
124
125
126
}

static Z_long 
year_to_days(N_int year)
{
127
  return( year * 365L + (year / 4) - (year / 100) + (year / 400) );
128
129
130
131
132
133
}


static Z_long 
calc_days(N_int year, N_int mm, N_int dd)
{
134
135
136
137
138
139
  boolean lp;
  
  if (year < 1) return(0L);
  if ((mm < 1) or (mm > 12)) return(0L);
  if ((dd < 1) or (dd > month_length[(lp = leap(year))][mm])) return(0L);
  return( year_to_days(--year) + days_in_months[lp][mm] + dd );
140
141
142
143
144
}

static boolean 
week_of_year(N_int *week, N_int *year, N_int mm, N_int dd)
{
145
146
147
148
149
150
151
152
153
154
155
156
157
  if (check_date(*year,mm,dd))
    {
      *week = week_number(*year,mm,dd);
      if (*week == 0) 
	*week = weeks_in_year(--(*year));
      else if (*week > weeks_in_year(*year))
	{
	  *week = 1;
	  (*year)++;
	}
      return(true);
    }
  return(false);
158
159
160
161
}

static Z_long 
dates_difference(N_int year1, N_int mm1, N_int dd1,
162
		 N_int year2, N_int mm2, N_int dd2)
163
{
164
  return( calc_days(year2, mm2, dd2) - calc_days(year1, mm1, dd1) );
165
166
}

167
/*** END OF lib_date routines ********************************************/
168

169
/* Spacing around day/week headers and main area, inside those windows */
170
#define CALENDAR_MARGIN		 0
171
172
173
/* Spacing around day/week headers and main area, outside those windows */
#define INNER_BORDER		 4
/* Separation between day headers and main area */
174
#define CALENDAR_YSEP		 4
175
/* Separation between week headers and main area */
176
#define CALENDAR_XSEP		 4
177

178
#define DAY_XSEP		 0 /* not really good for small calendar */
179
180
181
#define DAY_YSEP		 0 /* not really good for small calendar */

/* Color usage */
182
183
#define HEADER_FG_COLOR(widget)		 (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
#define HEADER_BG_COLOR(widget)		 (& (widget)->style->bg[GTK_WIDGET_STATE (widget)])
184
185
#define SELECTED_BG_COLOR(widget)	 (& (widget)->style->base[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
#define SELECTED_FG_COLOR(widget)	 (& (widget)->style->text[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
186
187
188
189
190
191
#define NORMAL_DAY_COLOR(widget)	 (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
#define PREV_MONTH_COLOR(widget)	 (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
#define NEXT_MONTH_COLOR(widget)	 (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
#define MARKED_COLOR(widget)		 (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
#define BACKGROUND_COLOR(widget)	 (& (widget)->style->base[GTK_WIDGET_STATE (widget)])
#define HIGHLIGHT_BACK_COLOR(widget)	 (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216

enum {
  ARROW_YEAR_LEFT,
  ARROW_YEAR_RIGHT,
  ARROW_MONTH_LEFT,
  ARROW_MONTH_RIGHT
};

enum {
  MONTH_PREV,
  MONTH_CURRENT,
  MONTH_NEXT
};

enum {
  MONTH_CHANGED_SIGNAL,
  DAY_SELECTED_SIGNAL,
  DAY_SELECTED_DOUBLE_CLICK_SIGNAL,
  PREV_MONTH_SIGNAL,
  NEXT_MONTH_SIGNAL,
  PREV_YEAR_SIGNAL,
  NEXT_YEAR_SIGNAL,
  LAST_SIGNAL
};

Matthias Clasen's avatar
Matthias Clasen committed
217
218
219
220
221
222
223
224
225
226
227
228
229
230
enum
{
  PROP_0,
  PROP_YEAR,
  PROP_MONTH,
  PROP_DAY,
  PROP_SHOW_HEADING,
  PROP_SHOW_DAY_NAMES,
  PROP_NO_MONTH_CHANGE,
  PROP_SHOW_WEEK_NUMBERS,
  PROP_WEEK_START_MONDAY,
  PROP_LAST
};

231
232
static gint gtk_calendar_signals[LAST_SIGNAL] = { 0 };

233
234
235
236
237
238
239
240
241
242
243
static GtkWidgetClass *parent_class = NULL;

typedef struct _GtkCalendarPrivateData GtkCalendarPrivateData;
struct _GtkCalendarPrivateData
{
  GdkWindow *header_win;
  GdkWindow *day_name_win;
  GdkWindow *main_win;
  GdkWindow *week_win;
  GdkWindow *arrow_win[4];

244
245
246
  guint header_h;
  guint day_name_h;
  guint main_h;
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263

  guint	     arrow_state[4];
  guint	     arrow_width;
  guint	     max_month_width;
  guint	     max_year_width;
  
  guint day_width;
  guint week_width;

  guint min_day_width;
  guint max_day_char_width;
  guint max_day_char_ascent;
  guint max_day_char_descent;
  guint max_label_char_ascent;
  guint max_label_char_descent;
  guint max_week_char_width;
  
264
265
  guint freeze_count;

266
  /* flags */
267
268
269
270
  guint dirty_header : 1;
  guint dirty_day_names : 1;
  guint dirty_main : 1;
  guint dirty_week : 1;
271
272
273
274
275

  guint need_timer  : 1;

  guint32 timer;
  gint click_child;
276
277
278
279
};

#define GTK_CALENDAR_PRIVATE_DATA(widget)  (((GtkCalendarPrivateData*)(GTK_CALENDAR (widget)->private_data)))

280
281
typedef void (*GtkCalendarSignalDate) (GtkObject *object, guint arg1, guint arg2, guint arg3, gpointer data);

282
283
static void gtk_calendar_class_init	(GtkCalendarClass *class);
static void gtk_calendar_init		(GtkCalendar *calendar);
284
static void gtk_calendar_finalize	(GObject *calendar);
285
static void gtk_calendar_destroy	(GtkObject *calendar);
Matthias Clasen's avatar
Matthias Clasen committed
286
287
288
289
290
291
292
293
static void gtk_calendar_set_property   (GObject      *object,
				         guint         prop_id,
				         const GValue *value,
				         GParamSpec   *pspec);
static void gtk_calendar_get_property   (GObject      *object,
					 guint         prop_id,
					 GValue       *value,
					 GParamSpec   *pspec);
294
295
296
297
298
299
300
301
302
303
static void gtk_calendar_realize	(GtkWidget *widget);
static void gtk_calendar_unrealize	(GtkWidget *widget);
static void gtk_calendar_size_request	(GtkWidget *widget,
					 GtkRequisition *requisition);
static void gtk_calendar_size_allocate	(GtkWidget *widget,
					 GtkAllocation *allocation);
static gint gtk_calendar_expose		(GtkWidget *widget,
					 GdkEventExpose *event);
static gint gtk_calendar_button_press	(GtkWidget *widget,
					 GdkEventButton *event);
304
305
static gint gtk_calendar_button_release	(GtkWidget *widget,
					 GdkEventButton *event);
306
307
308
309
310
311
312
313
314
315
static void gtk_calendar_main_button	(GtkWidget *widget,
					 GdkEventButton *event);
static gint gtk_calendar_motion_notify	(GtkWidget *widget,
					 GdkEventMotion *event);
static gint gtk_calendar_enter_notify	(GtkWidget *widget,
					 GdkEventCrossing *event);
static gint gtk_calendar_leave_notify	(GtkWidget *widget,
					 GdkEventCrossing *event);
static gint gtk_calendar_key_press	(GtkWidget	   *widget,
					 GdkEventKey	   *event);
316
317
static void gtk_calendar_grab_notify    (GtkWidget          *widget,
			 	         gboolean            was_grabbed);
318
static void gtk_calendar_state_changed	(GtkWidget *widget,
319
					 GtkStateType previous_state);
320
static void gtk_calendar_style_set	(GtkWidget *widget,
321
					 GtkStyle  *previous_style);
322
323
324
325
326
static void gtk_calendar_paint_header	    (GtkWidget *widget);
static void gtk_calendar_paint_day_names    (GtkWidget *widget);
static void gtk_calendar_paint_week_numbers (GtkWidget *widget);
static void gtk_calendar_paint_main	    (GtkWidget *widget);

327
328
static void gtk_calendar_select_and_focus_day (GtkCalendar *calendar,
					       guint        day);
329
330
331
332
333
334
335
336
337
338
339
340
341

static void gtk_calendar_paint_arrow	(GtkWidget    *widget,
					 guint	       arrow);
static void gtk_calendar_paint_day_num	(GtkWidget    *widget,
					 gint	       day);
static void gtk_calendar_paint_day	(GtkWidget    *widget,
					 gint	       row,
					 gint	       col);
static void gtk_calendar_compute_days	(GtkCalendar  *calendar);
static gint left_x_for_column		(GtkCalendar  *calendar,
					 gint	       column);
static gint top_y_for_row		(GtkCalendar  *calendar,
					 gint	       row);
342
343
344
345

static char    *default_abbreviated_dayname[7];
static char    *default_monthname[12];

Manish Singh's avatar
Manish Singh committed
346
GType
347
gtk_calendar_get_type (void)
348
{
Manish Singh's avatar
Manish Singh committed
349
  static GType calendar_type = 0;
350
  
351
352
  if (!calendar_type)
    {
353
      static const GTypeInfo calendar_info =
354
355
      {
	sizeof (GtkCalendarClass),
Manish Singh's avatar
Manish Singh committed
356
357
	NULL,		/* base_init */
	NULL,		/* base_finalize */
358
	(GClassInitFunc) gtk_calendar_class_init,
Manish Singh's avatar
Manish Singh committed
359
360
	NULL,		/* class_finalize */
	NULL,		/* class_data */
361
	sizeof (GtkCalendar),
Manish Singh's avatar
Manish Singh committed
362
	0,		/* n_preallocs */
363
	(GInstanceInitFunc) gtk_calendar_init,
364
      };
365

Manish Singh's avatar
Manish Singh committed
366
367
      calendar_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkCalendar",
					      &calendar_info, 0);
368
    }
369
  
370
371
372
373
374
375
  return calendar_type;
}

static void
gtk_calendar_class_init (GtkCalendarClass *class)
{
376
  GObjectClass   *gobject_class;
377
  GtkObjectClass   *object_class;
378
  GtkWidgetClass *widget_class;
379
380

  gobject_class = (GObjectClass*)  class;
381
  object_class = (GtkObjectClass*)  class;
382
  widget_class = (GtkWidgetClass*) class;
383
  
Manish Singh's avatar
Manish Singh committed
384
  parent_class = g_type_class_peek_parent (class);
385
  
Matthias Clasen's avatar
Matthias Clasen committed
386
387
  gobject_class->set_property = gtk_calendar_set_property;
  gobject_class->get_property = gtk_calendar_get_property;
388
  gobject_class->finalize = gtk_calendar_finalize;
389

390
391
  object_class->destroy = gtk_calendar_destroy;

392
393
394
395
396
397
  widget_class->realize = gtk_calendar_realize;
  widget_class->unrealize = gtk_calendar_unrealize;
  widget_class->expose_event = gtk_calendar_expose;
  widget_class->size_request = gtk_calendar_size_request;
  widget_class->size_allocate = gtk_calendar_size_allocate;
  widget_class->button_press_event = gtk_calendar_button_press;
398
  widget_class->button_release_event = gtk_calendar_button_release;
399
400
401
402
403
404
  widget_class->motion_notify_event = gtk_calendar_motion_notify;
  widget_class->enter_notify_event = gtk_calendar_enter_notify;
  widget_class->leave_notify_event = gtk_calendar_leave_notify;
  widget_class->key_press_event = gtk_calendar_key_press;
  widget_class->style_set = gtk_calendar_style_set;
  widget_class->state_changed = gtk_calendar_state_changed;
405
  widget_class->grab_notify = gtk_calendar_grab_notify;
406
  
407
408
409
410
411
412
413
414
  class->month_changed = NULL;
  class->day_selected = NULL;
  class->day_selected_double_click = NULL;
  class->prev_month = NULL;
  class->next_month = NULL;
  class->prev_year = NULL;
  class->next_year = NULL;

Matthias Clasen's avatar
Matthias Clasen committed
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
  g_object_class_install_property (gobject_class,
                                   PROP_YEAR,
                                   g_param_spec_int ("year",
						     _("Year"),
						     _("The selected year"),
						     0, G_MAXINT, 0,
						     G_PARAM_READWRITE));
  g_object_class_install_property (gobject_class,
                                   PROP_MONTH,
                                   g_param_spec_int ("month",
						     _("Month"),
						     _("The selected month (as a number between 0 and 11)"),
						     0, 11, 0,
						     G_PARAM_READWRITE));
  g_object_class_install_property (gobject_class,
                                   PROP_DAY,
                                   g_param_spec_int ("day",
						     _("Day"),
						     _("The selected day (as a number between 1 and 31, or 0 to unselect the currently selected day)"),
						     0, 31, 0,
						     G_PARAM_READWRITE));
  g_object_class_install_property (gobject_class,
                                   PROP_SHOW_HEADING,
                                   g_param_spec_boolean ("show_heading",
							 _("Show Heading"),
							 _("If TRUE, a heading is displayed"),
							 TRUE,
							 G_PARAM_READWRITE));
  g_object_class_install_property (gobject_class,
                                   PROP_SHOW_DAY_NAMES,
                                   g_param_spec_boolean ("show_day_names",
							 _("Show Day Names"),
							 _("If TRUE, day names are displayed"),
							 TRUE,
							 G_PARAM_READWRITE));
  g_object_class_install_property (gobject_class,
                                   PROP_NO_MONTH_CHANGE,
                                   g_param_spec_boolean ("no_month_change",
							 _("No Month Change"),
							 _("If TRUE, the selected month can not be changed"),
							 FALSE,
							 G_PARAM_READWRITE));
  g_object_class_install_property (gobject_class,
                                   PROP_SHOW_WEEK_NUMBERS,
                                   g_param_spec_boolean ("show_week_numbers",
							 _("Show Week Numbers"),
							 _("If TRUE, week numbers are displayed"),
							 FALSE,
							 G_PARAM_READWRITE));
  g_object_class_install_property (gobject_class,
                                   PROP_WEEK_START_MONDAY,
                                   g_param_spec_boolean ("week_start_monday",
							 _("Week Start Monday"),
							 _("If TRUE, Monday is displayed as the first day of the week"),
							 FALSE,
							 G_PARAM_READWRITE));


473
  gtk_calendar_signals[MONTH_CHANGED_SIGNAL] =
Manish Singh's avatar
Manish Singh committed
474
475
476
477
478
479
480
    g_signal_new ("month_changed",
		  G_OBJECT_CLASS_TYPE (gobject_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkCalendarClass, month_changed),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
481
  gtk_calendar_signals[DAY_SELECTED_SIGNAL] =
Manish Singh's avatar
Manish Singh committed
482
483
484
485
486
487
488
    g_signal_new ("day_selected",
		  G_OBJECT_CLASS_TYPE (gobject_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkCalendarClass, day_selected),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
489
  gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL] =
Manish Singh's avatar
Manish Singh committed
490
491
492
493
494
495
496
    g_signal_new ("day_selected_double_click",
		  G_OBJECT_CLASS_TYPE (gobject_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkCalendarClass, day_selected_double_click),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
497
  gtk_calendar_signals[PREV_MONTH_SIGNAL] =
Manish Singh's avatar
Manish Singh committed
498
499
500
501
502
503
504
    g_signal_new ("prev_month",
		  G_OBJECT_CLASS_TYPE (gobject_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkCalendarClass, prev_month),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
505
  gtk_calendar_signals[NEXT_MONTH_SIGNAL] =
Manish Singh's avatar
Manish Singh committed
506
507
508
509
510
511
512
    g_signal_new ("next_month",
		  G_OBJECT_CLASS_TYPE (gobject_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkCalendarClass, next_month),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
513
  gtk_calendar_signals[PREV_YEAR_SIGNAL] =
Manish Singh's avatar
Manish Singh committed
514
515
516
517
518
519
520
    g_signal_new ("prev_year",
		  G_OBJECT_CLASS_TYPE (gobject_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkCalendarClass, prev_year),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
521
  gtk_calendar_signals[NEXT_YEAR_SIGNAL] =
Manish Singh's avatar
Manish Singh committed
522
523
524
525
526
527
528
    g_signal_new ("next_year",
		  G_OBJECT_CLASS_TYPE (gobject_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkCalendarClass, next_year),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
529
530
531
532
533
534
535
536
537
538
539
}

static void
gtk_calendar_init (GtkCalendar *calendar)
{
  time_t secs;
  struct tm *tm;
  gint i;
  char buffer[255];
  time_t tmp_time;
  GtkWidget *widget;
540
  GtkCalendarPrivateData *private_data;
541
  
542
543
  widget = GTK_WIDGET (calendar);
  GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
544
  
Manish Singh's avatar
Manish Singh committed
545
  calendar->private_data = g_malloc (sizeof (GtkCalendarPrivateData));
546
547
  private_data = GTK_CALENDAR_PRIVATE_DATA (calendar);

548
549
550
551
552
  if (!default_abbreviated_dayname[0])
    for (i=0; i<7; i++)
      {
	tmp_time= (i+3)*86400;
	strftime ( buffer, sizeof (buffer), "%a", gmtime (&tmp_time));
553
	default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
554
      }
555
  
556
557
558
559
560
  if (!default_monthname[0])
    for (i=0; i<12; i++)
      {
	tmp_time=i*2764800;
	strftime ( buffer, sizeof (buffer), "%B", gmtime (&tmp_time));
561
	default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
562
      }
563
  
564
565
566
567
568
  /* Set defaults */
  secs = time (NULL);
  tm = localtime (&secs);
  calendar->month = tm->tm_mon;
  calendar->year  = 1900 + tm->tm_year;
569
  
570
571
  for (i=0;i<31;i++)
    calendar->marked_date[i] = FALSE;
572
  calendar->num_marked_dates = 0;
573
  calendar->selected_day = tm->tm_mday;
574
  
575
576
  calendar->display_flags = ( GTK_CALENDAR_SHOW_HEADING | 
			      GTK_CALENDAR_SHOW_DAY_NAMES );
577
  
578
579
  calendar->highlight_row = -1;
  calendar->highlight_col = -1;
580
  
581
582
583
584
  calendar->focus_row = -1;
  calendar->focus_col = -1;
  calendar->xor_gc = NULL;

585
586
587
588
589
590
591
592
593
594
595
  private_data->max_year_width = 0;
  private_data->max_month_width = 0;
  private_data->max_day_char_width = 0;
  private_data->max_week_char_width = 0;

  private_data->max_day_char_ascent = 0;
  private_data->max_day_char_descent = 0;
  private_data->max_label_char_ascent = 0;
  private_data->max_label_char_descent = 0;

  private_data->arrow_width = 10;
596
597

  private_data->freeze_count = 0;
598
  
599
600
601
602
  private_data->dirty_header = 0;
  private_data->dirty_day_names = 0;
  private_data->dirty_week = 0;
  private_data->dirty_main = 0;
603
604
605
606

  private_data->need_timer = 0;
  private_data->timer = 0;
  private_data->click_child = -1;
607
608
609
}

GtkWidget*
610
gtk_calendar_new (void)
611
{
Manish Singh's avatar
Manish Singh committed
612
  return g_object_new (GTK_TYPE_CALENDAR, NULL);
613
614
615
616
617
}

/* column_from_x: returns the column 0-6 that the
 * x pixel of the xwindow is in */
static gint
618
619
column_from_x (GtkCalendar *calendar,
	       gint	    event_x)
620
621
622
{
  gint c, column;
  gint x_left, x_right;
623
  
624
  column = -1;
625
  
626
627
628
  for (c = 0; c < 7; c++)
    {
      x_left = left_x_for_column (calendar, c);
629
      x_right = x_left + GTK_CALENDAR_PRIVATE_DATA (calendar)->day_width;
630
      
Owen Taylor's avatar
Owen Taylor committed
631
      if (event_x >= x_left && event_x < x_right)
632
633
634
635
636
	{
	  column = c;
	  break;
	}
    }
637
  
638
639
640
641
642
643
  return column;
}

static gint
row_height (GtkCalendar *calendar)
{
644
  return (GTK_CALENDAR_PRIVATE_DATA (calendar)->main_h - CALENDAR_MARGIN
645
646
647
648
649
650
651
652
	  - ((calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
	     ? CALENDAR_YSEP : CALENDAR_MARGIN)) / 6;
}


/* row_from_y: returns the row 0-5 that the
 * y pixel of the xwindow is in */
static gint
653
654
row_from_y (GtkCalendar *calendar,
	    gint	 event_y)
655
656
657
658
{
  gint r, row;
  gint height;
  gint y_top, y_bottom;
659
  
660
661
  height = row_height (calendar);
  row = -1;
662
  
663
664
665
666
  for (r = 0; r < 6; r++)
    {
      y_top = top_y_for_row (calendar, r);
      y_bottom = y_top + height;
667
      
Owen Taylor's avatar
Owen Taylor committed
668
      if (event_y >= y_top && event_y < y_bottom)
669
670
671
672
673
	{
	  row = r;
	  break;
	}
    }
674
  
675
  return row;
676
}/* left_x_for_column: returns the x coordinate
677
678
 * for the left of the column */
static gint
679
680
left_x_for_column (GtkCalendar *calendar,
		   gint		column)
681
682
683
{
  gint width;
  gint x_left;
684
  
685
686
687
  if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
    column = 6 - column;

688
  width = GTK_CALENDAR_PRIVATE_DATA (calendar)->day_width;
689
  if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
690
    x_left = CALENDAR_XSEP + (width + DAY_XSEP) * column;
691
692
  else
    x_left = CALENDAR_MARGIN + (width + DAY_XSEP) * column;
693
  
694
695
696
697
698
699
  return x_left;
}

/* top_y_for_row: returns the y coordinate
 * for the top of the row */
static gint
700
701
top_y_for_row (GtkCalendar *calendar,
	       gint	    row)
702
{
703
  
704
705
706
  return (GTK_CALENDAR_PRIVATE_DATA (calendar)->main_h 
	  - (CALENDAR_MARGIN + (6 - row)
	     * row_height (calendar)));
707
708
709
710
711
}

/* This function should be done by the toolkit, but we don't like the
 * GTK arrows because they don't look good on this widget */
static void
712
713
714
715
716
draw_arrow_right (GdkWindow *window,
		  GdkGC	    *gc,
		  gint	     x,
		  gint	     y,
		  gint	     size)
717
718
{
  gint i;
719
  
720
721
722
723
724
725
726
727
728
729
730
731
732
  for (i = 0; i <= size / 2; i++)
    {
      gdk_draw_line (window, gc,
		     x + i,
		     y + i,
		     x + i,
		     y + size - i);
    }
}

/* This function should be done by the toolkit, but we don't like the
 * GTK arrows because they don't look good on this widget */
static void
733
734
735
736
737
draw_arrow_left (GdkWindow *window,
		 GdkGC	   *gc,
		 gint	    x,
		 gint	    y,
		 gint	    size)
738
739
{
  gint i;
740
  
741
742
743
744
745
746
747
748
749
750
751
752
753
754
  for (i = 0; i <= size / 2; i++)
    {
      gdk_draw_line (window, gc,
		     x + size/2 - i,
		     y + i,
		     x + size/2 - i,
		     y + size - i);
    }
}

static void
gtk_calendar_set_month_prev (GtkCalendar *calendar)
{
  gint month_len;
755
  
756
757
  if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
    return;
758
  
759
760
761
762
763
764
765
  if (calendar->month == 0)
    {
      calendar->month = 11;
      calendar->year--;
    } 
  else 
    calendar->month--;
766
  
767
  month_len = month_length[leap (calendar->year)][calendar->month + 1];
768
  
769
770
  gtk_calendar_freeze (calendar);
  gtk_calendar_compute_days (calendar);
771
  
Manish Singh's avatar
Manish Singh committed
772
773
774
775
776
777
  g_signal_emit (calendar,
		 gtk_calendar_signals[PREV_MONTH_SIGNAL],
		 0);
  g_signal_emit (calendar,
		 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
		 0);
778
  
779
780
781
782
783
784
785
786
787
  if (month_len < calendar->selected_day)
    {
      calendar->selected_day = 0;
      gtk_calendar_select_day (calendar, month_len);
    }
  else
    {
      if (calendar->selected_day < 0)
	calendar->selected_day = calendar->selected_day + 1 + month_length[leap (calendar->year)][calendar->month + 1];
788
      gtk_calendar_select_day (calendar, calendar->selected_day);
789
    }
790
791

  gtk_widget_queue_draw (GTK_WIDGET (calendar));
792
793
794
795
796
797
798
799
  gtk_calendar_thaw (calendar);
}


static void
gtk_calendar_set_month_next (GtkCalendar *calendar)
{
  gint month_len;
800
  
801
  g_return_if_fail (GTK_IS_WIDGET (calendar));
802
  
803
804
  if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
    return;
805
806
  
  
807
808
809
810
811
812
813
  if (calendar->month == 11)
    {
      calendar->month = 0;
      calendar->year++;
    } 
  else 
    calendar->month++;
814
  
815
816
  gtk_calendar_freeze (calendar);
  gtk_calendar_compute_days (calendar);
Manish Singh's avatar
Manish Singh committed
817
818
819
820
821
822
  g_signal_emit (calendar,
		 gtk_calendar_signals[NEXT_MONTH_SIGNAL],
		 0);
  g_signal_emit (calendar,
		 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
		 0);
823
  
824
  month_len = month_length[leap (calendar->year)][calendar->month + 1];
825
  
826
827
828
829
830
831
832
  if (month_len < calendar->selected_day)
    {
      calendar->selected_day = 0;
      gtk_calendar_select_day (calendar, month_len);
    }
  else
    gtk_calendar_select_day (calendar, calendar->selected_day);
833
834

  gtk_widget_queue_draw (GTK_WIDGET (calendar));
835
836
837
838
839
840
841
  gtk_calendar_thaw (calendar);
}

static void
gtk_calendar_set_year_prev (GtkCalendar *calendar)
{
  gint month_len;
842
  
843
  g_return_if_fail (GTK_IS_WIDGET (calendar));
844
  
845
846
847
  calendar->year--;
  gtk_calendar_freeze (calendar);
  gtk_calendar_compute_days (calendar);
Manish Singh's avatar
Manish Singh committed
848
849
850
851
852
853
  g_signal_emit (calendar,
		 gtk_calendar_signals[PREV_YEAR_SIGNAL],
		 0);
  g_signal_emit (calendar,
		 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
		 0);
854
  
855
  month_len = month_length[leap (calendar->year)][calendar->month + 1];
856
  
857
858
859
860
861
862
863
  if (month_len < calendar->selected_day)
    {
      calendar->selected_day = 0;
      gtk_calendar_select_day (calendar, month_len);
    }
  else
    gtk_calendar_select_day (calendar, calendar->selected_day);
864
  
865
  gtk_widget_queue_draw (GTK_WIDGET (calendar));
866
867
868
869
870
871
872
873
  gtk_calendar_thaw (calendar);
}

static void
gtk_calendar_set_year_next (GtkCalendar *calendar)
{
  gint month_len;
  GtkWidget *widget;
874
  
875
  g_return_if_fail (GTK_IS_WIDGET (calendar));
876
  
877
  widget = GTK_WIDGET (calendar);
878
  
879
  gtk_calendar_freeze (calendar);
880
  
881
882
  calendar->year++;
  gtk_calendar_compute_days (calendar);
Manish Singh's avatar
Manish Singh committed
883
884
885
886
887
888
  g_signal_emit (calendar,
		 gtk_calendar_signals[NEXT_YEAR_SIGNAL],
		 0);
  g_signal_emit (calendar,
		 gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
		 0);
889
  
890
  month_len = month_length[leap (calendar->year)][calendar->month + 1];
891
  
892
893
894
895
896
897
898
  if (month_len < calendar->selected_day)
    {
      calendar->selected_day = 0;
      gtk_calendar_select_day (calendar, month_len);
    }
  else
    gtk_calendar_select_day (calendar, calendar->selected_day);
899
  
900
  gtk_widget_queue_draw (GTK_WIDGET (calendar));
901
902
903
904
  gtk_calendar_thaw (calendar);
}

static void
905
gtk_calendar_main_button (GtkWidget	 *widget,
906
907
908
909
910
911
			  GdkEventButton *event)
{
  GtkCalendar *calendar;
  gint x, y;
  gint row, col;
  gint day_month;
912
  gint day;
913
  
914
  calendar = GTK_CALENDAR (widget);
915
  
916
917
  x = (gint) (event->x);
  y = (gint) (event->y);
918
  
919
920
  row = row_from_y (calendar, y);
  col = column_from_x (calendar, x);
Owen Taylor's avatar
Owen Taylor committed
921
922
923
924

  /* If row or column isn't found, just return. */
  if (row == -1 || col == -1)
    return;
925
  
926
  day_month = calendar->day_month[row][col];
927
928

  if (event->type == GDK_BUTTON_PRESS)
929
    {
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
      day = calendar->day[row][col];
      
      if (day_month == MONTH_PREV)
	gtk_calendar_set_month_prev (calendar);
      else if (day_month == MONTH_NEXT)
	gtk_calendar_set_month_next (calendar);
      
      if (!GTK_WIDGET_HAS_FOCUS (widget))
	gtk_widget_grab_focus (widget);
	  
      gtk_calendar_select_and_focus_day (calendar, day);

    }
  else if (event->type == GDK_2BUTTON_PRESS)
    {
      if (day_month == MONTH_CURRENT)
Manish Singh's avatar
Manish Singh committed
946
947
948
	g_signal_emit (calendar,
		       gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL],
		       0);
949
950
951
952
953
954
955
    }
}

static void
gtk_calendar_realize_arrows (GtkWidget *widget)
{
  GtkCalendar *calendar;
956
  GtkCalendarPrivateData *private_data;
957
958
959
  GdkWindowAttr attributes;
  gint attributes_mask;
  gint i;
960
  
961
  g_return_if_fail (GTK_IS_CALENDAR (widget));
962
  
963
  calendar = GTK_CALENDAR (widget);
964
965
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);

966
967
968
969
970
971
972
973
974
975
976
977
978
  /* Arrow windows ------------------------------------- */
  if (! (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
      && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
    {
      attributes.wclass = GDK_INPUT_OUTPUT;
      attributes.window_type = GDK_WINDOW_CHILD;
      attributes.visual = gtk_widget_get_visual (widget);
      attributes.colormap = gtk_widget_get_colormap (widget);
      attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
			       | GDK_BUTTON_PRESS_MASK	| GDK_BUTTON_RELEASE_MASK
			       | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
      attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
      attributes.y = 3;
979
980
      attributes.width = private_data->arrow_width;
      attributes.height = private_data->header_h - 7;
981
982
983
984
985
986
987
988
      for (i = 0; i < 4; i++)
	{
	  switch (i)
	    {
	    case ARROW_MONTH_LEFT:
	      attributes.x = 3;
	      break;
	    case ARROW_MONTH_RIGHT:
989
990
	      attributes.x = (private_data->arrow_width 
			      + private_data->max_month_width);
991
992
	      break;
	    case ARROW_YEAR_LEFT:
993
	      attributes.x = (widget->allocation.width - 2 * widget->style->xthickness
994
995
			      - (3 + 2*private_data->arrow_width 
				 + private_data->max_year_width));
996
997
	      break;
	    case ARROW_YEAR_RIGHT:
998
	      attributes.x = (widget->allocation.width - 2 * widget->style->xthickness 
999
			      - 3 - private_data->arrow_width);
1000
1001
	      break;
	    }
1002
1003
1004
1005
1006
	  private_data->arrow_win[i] = gdk_window_new (private_data->header_win,
						       &attributes, 
						       attributes_mask);
	  private_data->arrow_state[i] = GTK_STATE_NORMAL;
	  gdk_window_set_background (private_data->arrow_win[i],
1007
				     HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1008
1009
	  gdk_window_show (private_data->arrow_win[i]);
	  gdk_window_set_user_data (private_data->arrow_win[i], widget);
1010
1011
1012
	}
    }
  else
1013
1014
1015
1016
    {
      for (i = 0; i < 4; i++)
	private_data->arrow_win[i] = NULL;
    }
1017
1018
1019
1020
1021
1022
}

static void
gtk_calendar_realize_header (GtkWidget *widget)
{
  GtkCalendar *calendar;
1023
  GtkCalendarPrivateData *private_data;
1024
1025
  GdkWindowAttr attributes;
  gint attributes_mask;
1026
  
1027
  g_return_if_fail (GTK_IS_CALENDAR (widget));
1028
  
1029
  calendar = GTK_CALENDAR (widget);
1030
1031
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);

1032
1033
1034
1035
1036
1037
1038
1039
1040
  /* Header window ------------------------------------- */
  if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
    {
      attributes.wclass = GDK_INPUT_OUTPUT;
      attributes.window_type = GDK_WINDOW_CHILD;
      attributes.visual = gtk_widget_get_visual (widget);
      attributes.colormap = gtk_widget_get_colormap (widget);
      attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
      attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1041
1042
      attributes.x = widget->style->xthickness;
      attributes.y = widget->style->ythickness;
1043
1044
      attributes.width = widget->allocation.width - 2 * attributes.x;
      attributes.height = private_data->header_h - 2 * attributes.y;
1045
      private_data->header_win = gdk_window_new (widget->window,
1046
					     &attributes, attributes_mask);
1047
      
1048
      gdk_window_set_background (private_data->header_win,
1049
				 HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1050
1051
      gdk_window_show (private_data->header_win);
      gdk_window_set_user_data (private_data->header_win, widget);
1052
      
1053
1054
    }
  else
1055
1056
1057
    {
      private_data->header_win = NULL;
    }
1058
1059
1060
1061
1062
1063
1064
  gtk_calendar_realize_arrows (widget);
}

static void
gtk_calendar_realize_day_names (GtkWidget *widget)
{
  GtkCalendar *calendar;
1065
  GtkCalendarPrivateData *private_data;
1066
1067
  GdkWindowAttr attributes;
  gint attributes_mask;
1068
  
1069
  g_return_if_fail (GTK_IS_CALENDAR (widget));
1070
  
1071
  calendar = GTK_CALENDAR (widget);
1072
1073
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);

1074
  /* Day names	window --------------------------------- */
1075
1076
1077
1078
1079
1080
1081
1082
  if ( calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
    {
      attributes.wclass = GDK_INPUT_OUTPUT;
      attributes.window_type = GDK_WINDOW_CHILD;
      attributes.visual = gtk_widget_get_visual (widget);
      attributes.colormap = gtk_widget_get_colormap (widget);
      attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
      attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1083
1084
      attributes.x = (widget->style->xthickness + INNER_BORDER);
      attributes.y = private_data->header_h + (widget->style->ythickness 
1085
1086
					   + INNER_BORDER);
      attributes.width = (widget->allocation.width 
1087
			  - (widget->style->xthickness + INNER_BORDER) 
1088
1089
1090
1091
1092
1093
1094
1095
1096
			  * 2);
      attributes.height = private_data->day_name_h;
      private_data->day_name_win = gdk_window_new (widget->window,
						   &attributes, 
						   attributes_mask);
      gdk_window_set_background (private_data->day_name_win, 
				 BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
      gdk_window_show (private_data->day_name_win);
      gdk_window_set_user_data (private_data->day_name_win, widget);
1097
1098
    }
  else
1099
1100
1101
    {
      private_data->day_name_win = NULL;
    }
1102
1103
1104
1105
1106
1107
}

static void
gtk_calendar_realize_week_numbers (GtkWidget *widget)
{
  GtkCalendar *calendar;
1108
  GtkCalendarPrivateData *private_data;
1109
1110
  GdkWindowAttr attributes;
  gint attributes_mask;
1111
  
1112
  g_return_if_fail (GTK_IS_CALENDAR (widget));
1113
  
1114
  calendar = GTK_CALENDAR (widget);
1115
1116
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);

1117
1118
1119
1120
1121
1122
1123
1124
  /* Week number window -------------------------------- */
  if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
    {
      attributes.wclass = GDK_INPUT_OUTPUT;
      attributes.window_type = GDK_WINDOW_CHILD;
      attributes.visual = gtk_widget_get_visual (widget);
      attributes.colormap = gtk_widget_get_colormap (widget);
      attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1125
      
1126
      attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1127
      attributes.x = widget->style->xthickness + INNER_BORDER;
1128
      attributes.y = (private_data->header_h + private_data->day_name_h 
1129
		      + (widget->style->ythickness + INNER_BORDER));
1130
1131
1132
1133
1134
1135
1136
1137
      attributes.width = private_data->week_width;
      attributes.height = private_data->main_h;
      private_data->week_win = gdk_window_new (widget->window,
					       &attributes, attributes_mask);
      gdk_window_set_background (private_data->week_win,  
				 BACKGROUND_COLOR (GTK_WIDGET (calendar)));
      gdk_window_show (private_data->week_win);
      gdk_window_set_user_data (private_data->week_win, widget);
1138
1139
    } 
  else
1140
1141
1142
    {
      private_data->week_win = NULL;
    }
1143
1144
1145
1146
1147
1148
}

static void
gtk_calendar_realize (GtkWidget *widget)
{
  GtkCalendar *calendar;
1149
  GtkCalendarPrivateData *private_data;
1150
1151
  GdkWindowAttr attributes;
  gint attributes_mask;
1152
1153
  GdkGCValues values;

1154
  calendar = GTK_CALENDAR (widget);
1155
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1156
  
1157
1158
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
  gtk_calendar_compute_days (calendar);
1159
  
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
  attributes.x = widget->allocation.x;
  attributes.y = widget->allocation.y;
  attributes.width = widget->allocation.width;
  attributes.height = widget->allocation.height;
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.event_mask =  (gtk_widget_get_events (widget) 
			    | GDK_EXPOSURE_MASK |GDK_KEY_PRESS_MASK);
  attributes.visual = gtk_widget_get_visual (widget);
  attributes.colormap = gtk_widget_get_colormap (widget);
1170
  
1171
1172
1173
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
  widget->window = gdk_window_new (widget->parent->window,
				   &attributes, attributes_mask);
1174