gtkcalendar.c 77.3 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
40
41
42
43
44
45
46
47
48
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include "gtkcalendar.h"
#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.  */

49
50
51
typedef	 unsigned   int	    N_int;
typedef	 unsigned   long    N_long;
typedef	 signed	    long    Z_long;
52
53
typedef enum { false = FALSE , true = TRUE } boolean;

54
55
#define and	    &&	    /* logical (boolean) operators: lower case */
#define or	    ||
56

57
static const N_int month_length[2][13] =
58
{
59
60
  { 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 }
61
62
};

63
static const N_int days_in_months[2][14] =
64
{
65
66
  { 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 }
67
68
69
70
71
};

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,
72
				N_int year2, N_int mm2, N_int dd2);
73
74
75
76
77
static N_int   weeks_in_year(N_int year);

static boolean 
leap(N_int year)
{
78
  return((((year % 4) == 0) and ((year % 100) != 0)) or ((year % 400) == 0));
79
80
81
82
83
}

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

static N_int weeks_in_year(N_int year)
{
98
  return(52 + ((day_of_week(year,1,1)==4) or (day_of_week(year,12,31)==4)));
99
100
101
102
103
}

static boolean 
check_date(N_int year, N_int mm, N_int dd)
{
104
105
106
107
  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);
108
109
110
111
112
}

static N_int 
week_number(N_int year, N_int mm, N_int dd)
{
113
114
115
116
117
  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) );
118
119
120
121
122
}

static Z_long 
year_to_days(N_int year)
{
123
  return( year * 365L + (year / 4) - (year / 100) + (year / 400) );
124
125
126
127
128
129
}


static Z_long 
calc_days(N_int year, N_int mm, N_int dd)
{
130
131
132
133
134
135
  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 );
136
137
138
139
140
}

static boolean 
week_of_year(N_int *week, N_int *year, N_int mm, N_int dd)
{
141
142
143
144
145
146
147
148
149
150
151
152
153
  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);
154
155
156
157
}

static Z_long 
dates_difference(N_int year1, N_int mm1, N_int dd1,
158
		 N_int year2, N_int mm2, N_int dd2)
159
{
160
  return( calc_days(year2, mm2, dd2) - calc_days(year1, mm1, dd1) );
161
162
}

163
/*** END OF lib_date routines ********************************************/
164

165
166
167
168
#define CALENDAR_MARGIN		 0
#define CALENDAR_YSEP		 4
#define CALENDAR_XSEP		 4
#define INNER_BORDER		 4
169
170
171

#define DAY_XPAD		 2
#define DAY_YPAD		 2
172
#define DAY_XSEP		 0 /* not really good for small calendar */
173
174
175
#define DAY_YSEP		 0 /* not really good for small calendar */

/* Color usage */
176
177
#define HEADER_FG_COLOR(widget)		 (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
#define HEADER_BG_COLOR(widget)		 (& (widget)->style->bg[GTK_WIDGET_STATE (widget)])
178
#define DAY_NAME_COLOR(widget)		 (& (widget)->style->bg[GTK_STATE_SELECTED])
179
180
#define NORMAL_DAY_COLOR(widget)	 (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
#define SELECTION_FOCUS_COLOR(widget)	 (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
181
#define SELECTION_NO_FOCUS_COLOR(widget) (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
182
183
184
185
186
187
#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 FOREGROUND_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)])
188
189
190
191
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
217
218

#define HEADER_FONT(widget) ((widget)->style->font)
#define LABEL_FONT(widget)   ((widget)->style->font)
#define DAY_FONT(widget)     ((widget)->style->font)

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
};

static gint gtk_calendar_signals[LAST_SIGNAL] = { 0 };

219
220
221
222
223
224
225
226
227
228
229
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];

230
231
232
  guint header_h;
  guint day_name_h;
  guint main_h;
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249

  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;
  
250
251
  guint freeze_count;

252
  /* flags */
253
254
255
256
  guint dirty_header : 1;
  guint dirty_day_names : 1;
  guint dirty_main : 1;
  guint dirty_week : 1;
257
258
259
260
};

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

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

263
264
static void gtk_calendar_class_init	(GtkCalendarClass *class);
static void gtk_calendar_init		(GtkCalendar *calendar);
265
static void gtk_calendar_destroy	(GtkObject *calendar);
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
static void gtk_calendar_realize	(GtkWidget *widget);
static void gtk_calendar_unrealize	(GtkWidget *widget);
static void gtk_calendar_draw_focus	(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);
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);
static void gtk_calendar_state_changed	(GtkWidget *widget,
288
					 GtkStateType previous_state);
289
static void gtk_calendar_style_set	(GtkWidget *widget,
290
					 GtkStyle  *previous_style);
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
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);


static void gtk_calendar_paint		(GtkWidget    *widget,
					 GdkRectangle *area);
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);
311
312
313
314

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

315
316
GtkType
gtk_calendar_get_type (void)
317
{
318
319
  static GtkType calendar_type = 0;
  
320
321
  if (!calendar_type)
    {
322
      static const GTypeInfo calendar_info =
323
324
      {
	sizeof (GtkCalendarClass),
325
326
327
328
329
330
331
332
	NULL,           /* base_init */
	NULL,           /* base_finalize */
	(GClassInitFunc) gtk_calendar_class_init,
	NULL,           /* class_finalize */
	NULL,           /* class_data */
	sizeof (GtkCalendar),
	16,             /* n_preallocs */
	(GInstanceInitFunc) gtk_calendar_init,
333
      };
334

335
      calendar_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkCalendar", &calendar_info, 0);
336
    }
337
  
338
339
340
341
342
343
344
345
  return calendar_type;
}

static void
gtk_calendar_class_init (GtkCalendarClass *class)
{
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
346
  
347
348
  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;
349
350
351
  
  parent_class = gtk_type_class (GTK_TYPE_WIDGET);
  
352
353
  object_class->destroy = gtk_calendar_destroy;

354
355
356
357
358
359
360
361
362
363
364
365
  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;
  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;
366
  
367
368
369
370
371
372
373
374
  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;

375
376
  gtk_calendar_signals[MONTH_CHANGED_SIGNAL] =
    gtk_signal_new ("month_changed",
377
		    GTK_RUN_FIRST, GTK_CLASS_TYPE (object_class),
378
		    GTK_SIGNAL_OFFSET (GtkCalendarClass, month_changed),
379
380
381
		    gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
  gtk_calendar_signals[DAY_SELECTED_SIGNAL] =
    gtk_signal_new ("day_selected",
382
		    GTK_RUN_FIRST, GTK_CLASS_TYPE (object_class),
383
		    GTK_SIGNAL_OFFSET (GtkCalendarClass, day_selected),
384
385
386
		    gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
  gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL] =
    gtk_signal_new ("day_selected_double_click",
387
		    GTK_RUN_FIRST, GTK_CLASS_TYPE (object_class),
388
		    GTK_SIGNAL_OFFSET (GtkCalendarClass, day_selected_double_click),
389
390
391
		    gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
  gtk_calendar_signals[PREV_MONTH_SIGNAL] =
    gtk_signal_new ("prev_month",
392
		    GTK_RUN_FIRST, GTK_CLASS_TYPE (object_class),
393
		    GTK_SIGNAL_OFFSET (GtkCalendarClass, prev_month),
394
395
396
		    gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
  gtk_calendar_signals[NEXT_MONTH_SIGNAL] =
    gtk_signal_new ("next_month",
397
		    GTK_RUN_FIRST, GTK_CLASS_TYPE (object_class),
398
		    GTK_SIGNAL_OFFSET (GtkCalendarClass, next_month),
399
400
401
		    gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
  gtk_calendar_signals[PREV_YEAR_SIGNAL] =
    gtk_signal_new ("prev_year",
402
		    GTK_RUN_FIRST, GTK_CLASS_TYPE (object_class),
403
		    GTK_SIGNAL_OFFSET (GtkCalendarClass, prev_year),
404
405
406
		    gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
  gtk_calendar_signals[NEXT_YEAR_SIGNAL] =
    gtk_signal_new ("next_year",
407
		    GTK_RUN_FIRST, GTK_CLASS_TYPE (object_class),
408
		    GTK_SIGNAL_OFFSET (GtkCalendarClass, next_year),
409
410
411
412
413
414
415
416
417
418
419
420
		    gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
}

static void
gtk_calendar_init (GtkCalendar *calendar)
{
  time_t secs;
  struct tm *tm;
  gint i;
  char buffer[255];
  time_t tmp_time;
  GtkWidget *widget;
421
  GtkCalendarPrivateData *private_data;
422
  
423
424
  widget = GTK_WIDGET (calendar);
  GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
425
  
426
  calendar->private_data = (gpointer) g_malloc (sizeof (GtkCalendarPrivateData));
427
428
  private_data = GTK_CALENDAR_PRIVATE_DATA (calendar);

429
430
431
432
433
  if (!default_abbreviated_dayname[0])
    for (i=0; i<7; i++)
      {
	tmp_time= (i+3)*86400;
	strftime ( buffer, sizeof (buffer), "%a", gmtime (&tmp_time));
434
	default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
435
      }
436
  
437
438
439
440
441
  if (!default_monthname[0])
    for (i=0; i<12; i++)
      {
	tmp_time=i*2764800;
	strftime ( buffer, sizeof (buffer), "%B", gmtime (&tmp_time));
442
	default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
443
      }
444
  
445
446
447
448
449
  /* Set defaults */
  secs = time (NULL);
  tm = localtime (&secs);
  calendar->month = tm->tm_mon;
  calendar->year  = 1900 + tm->tm_year;
450
  
451
452
  for (i=0;i<31;i++)
    calendar->marked_date[i] = FALSE;
453
  calendar->num_marked_dates = 0;
454
  calendar->selected_day = 1;
455
  
456
457
  calendar->display_flags = ( GTK_CALENDAR_SHOW_HEADING | 
			      GTK_CALENDAR_SHOW_DAY_NAMES );
458
  
459
460
  calendar->highlight_row = -1;
  calendar->highlight_col = -1;
461
  
462
463
464
465
  calendar->focus_row = -1;
  calendar->focus_col = -1;
  calendar->xor_gc = NULL;

466
467
468
469
470
471
472
473
474
475
476
  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;
477
478

  private_data->freeze_count = 0;
479
  
480
481
482
483
  private_data->dirty_header = 0;
  private_data->dirty_day_names = 0;
  private_data->dirty_week = 0;
  private_data->dirty_main = 0;
484
485
486
}

GtkWidget*
487
gtk_calendar_new (void)
488
{
489
  return GTK_WIDGET (gtk_type_new (GTK_TYPE_CALENDAR));
490
491
492
493
494
}

/* column_from_x: returns the column 0-6 that the
 * x pixel of the xwindow is in */
static gint
495
496
column_from_x (GtkCalendar *calendar,
	       gint	    event_x)
497
498
499
{
  gint c, column;
  gint x_left, x_right;
500
  
501
  column = -1;
502
  
503
504
505
  for (c = 0; c < 7; c++)
    {
      x_left = left_x_for_column (calendar, c);
506
      x_right = x_left + GTK_CALENDAR_PRIVATE_DATA (calendar)->day_width;
507
      
Owen Taylor's avatar
Owen Taylor committed
508
      if (event_x >= x_left && event_x < x_right)
509
510
511
512
513
	{
	  column = c;
	  break;
	}
    }
514
  
515
516
517
518
519
520
  return column;
}

static gint
row_height (GtkCalendar *calendar)
{
521
  return (GTK_CALENDAR_PRIVATE_DATA (calendar)->main_h - CALENDAR_MARGIN
522
523
524
525
526
527
528
529
	  - ((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
530
531
row_from_y (GtkCalendar *calendar,
	    gint	 event_y)
532
533
534
535
{
  gint r, row;
  gint height;
  gint y_top, y_bottom;
536
  
537
538
  height = row_height (calendar);
  row = -1;
539
  
540
541
542
543
  for (r = 0; r < 6; r++)
    {
      y_top = top_y_for_row (calendar, r);
      y_bottom = y_top + height;
544
      
Owen Taylor's avatar
Owen Taylor committed
545
      if (event_y >= y_top && event_y < y_bottom)
546
547
548
549
550
	{
	  row = r;
	  break;
	}
    }
551
  
552
553
554
555
556
557
  return row;
}

/* left_x_for_column: returns the x coordinate
 * for the left of the column */
static gint
558
559
left_x_for_column (GtkCalendar *calendar,
		   gint		column)
560
561
562
{
  gint width;
  gint x_left;
563
  
564
565
566
  if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
    column = 6 - column;

567
  width = GTK_CALENDAR_PRIVATE_DATA (calendar)->day_width;
568
569
570
571
  if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
    x_left =  DAY_XSEP + (width + DAY_XSEP) * column;
  else
    x_left = CALENDAR_MARGIN + (width + DAY_XSEP) * column;
572
  
573
574
575
576
577
578
  return x_left;
}

/* top_y_for_row: returns the y coordinate
 * for the top of the row */
static gint
579
580
top_y_for_row (GtkCalendar *calendar,
	       gint	    row)
581
{
582
  
583
584
585
  return (GTK_CALENDAR_PRIVATE_DATA (calendar)->main_h 
	  - (CALENDAR_MARGIN + (6 - row)
	     * row_height (calendar)));
586
587
588
589
590
}

/* 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
591
592
593
594
595
draw_arrow_right (GdkWindow *window,
		  GdkGC	    *gc,
		  gint	     x,
		  gint	     y,
		  gint	     size)
596
597
{
  gint i;
598
  
599
600
601
602
603
604
605
606
607
608
609
610
611
  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
612
613
614
615
616
draw_arrow_left (GdkWindow *window,
		 GdkGC	   *gc,
		 gint	    x,
		 gint	    y,
		 gint	    size)
617
618
{
  gint i;
619
  
620
621
622
623
624
625
626
627
628
629
630
631
632
633
  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;
634
  
635
636
  if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
    return;
637
  
638
639
640
641
642
643
644
  if (calendar->month == 0)
    {
      calendar->month = 11;
      calendar->year--;
    } 
  else 
    calendar->month--;
645
  
646
  month_len = month_length[leap (calendar->year)][calendar->month + 1];
647
  
648
649
  gtk_calendar_freeze (calendar);
  gtk_calendar_compute_days (calendar);
650
  
651
652
653
654
  gtk_signal_emit (GTK_OBJECT (calendar),
		   gtk_calendar_signals[PREV_MONTH_SIGNAL]);
  gtk_signal_emit (GTK_OBJECT (calendar),
		   gtk_calendar_signals[MONTH_CHANGED_SIGNAL]);
655
  
656
657
658
659
660
661
662
663
664
  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];
665
      gtk_calendar_select_day (calendar, calendar->selected_day);
666
    }
667
  
668
669
670
671
672
673
674
675
676
  gtk_calendar_paint (GTK_WIDGET (calendar), NULL);
  gtk_calendar_thaw (calendar);
}


static void
gtk_calendar_set_month_next (GtkCalendar *calendar)
{
  gint month_len;
677
  
678
679
  g_return_if_fail (calendar != NULL);
  g_return_if_fail (GTK_IS_WIDGET (calendar));
680
  
681
682
  if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
    return;
683
684
  
  
685
686
687
688
689
690
691
  if (calendar->month == 11)
    {
      calendar->month = 0;
      calendar->year++;
    } 
  else 
    calendar->month++;
692
  
693
694
695
696
697
698
  gtk_calendar_freeze (calendar);
  gtk_calendar_compute_days (calendar);
  gtk_signal_emit (GTK_OBJECT (calendar),
		   gtk_calendar_signals[NEXT_MONTH_SIGNAL]);
  gtk_signal_emit (GTK_OBJECT (calendar),
		   gtk_calendar_signals[MONTH_CHANGED_SIGNAL]);
699
  
700
  month_len = month_length[leap (calendar->year)][calendar->month + 1];
701
  
702
703
704
705
706
707
708
  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);
709
  
710
711
712
713
714
715
716
717
  gtk_calendar_paint (GTK_WIDGET(calendar), NULL);
  gtk_calendar_thaw (calendar);
}

static void
gtk_calendar_set_year_prev (GtkCalendar *calendar)
{
  gint month_len;
718
  
719
720
  g_return_if_fail (calendar != NULL);
  g_return_if_fail (GTK_IS_WIDGET (calendar));
721
  
722
723
724
725
726
727
728
  calendar->year--;
  gtk_calendar_freeze (calendar);
  gtk_calendar_compute_days (calendar);
  gtk_signal_emit (GTK_OBJECT (calendar),
		   gtk_calendar_signals[PREV_YEAR_SIGNAL]);
  gtk_signal_emit (GTK_OBJECT (calendar),
		   gtk_calendar_signals[MONTH_CHANGED_SIGNAL]);
729
  
730
  month_len = month_length[leap (calendar->year)][calendar->month + 1];
731
  
732
733
734
735
736
737
738
  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);
739
  
740
741
742
743
744
745
746
747
748
  gtk_calendar_paint (GTK_WIDGET (calendar), NULL);
  gtk_calendar_thaw (calendar);
}

static void
gtk_calendar_set_year_next (GtkCalendar *calendar)
{
  gint month_len;
  GtkWidget *widget;
749
  
750
751
  g_return_if_fail (calendar != NULL);
  g_return_if_fail (GTK_IS_WIDGET (calendar));
752
  
753
  widget = GTK_WIDGET (calendar);
754
  
755
  gtk_calendar_freeze (calendar);
756
  
757
758
759
760
761
762
  calendar->year++;
  gtk_calendar_compute_days (calendar);
  gtk_signal_emit (GTK_OBJECT (calendar),
		   gtk_calendar_signals[NEXT_YEAR_SIGNAL]);
  gtk_signal_emit (GTK_OBJECT (calendar),
		   gtk_calendar_signals[MONTH_CHANGED_SIGNAL]);
763
  
764
  month_len = month_length[leap (calendar->year)][calendar->month + 1];
765
  
766
767
768
769
770
771
772
  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);
773
  
774
775
776
777
778
  gtk_calendar_paint (GTK_WIDGET (calendar), NULL);
  gtk_calendar_thaw (calendar);
}

static void
779
gtk_calendar_main_button (GtkWidget	 *widget,
780
781
782
783
784
785
			  GdkEventButton *event)
{
  GtkCalendar *calendar;
  gint x, y;
  gint row, col;
  gint day_month;
786
  gint old_focus_row, old_focus_col;
787
  
788
  calendar = GTK_CALENDAR (widget);
789
  
790
791
  x = (gint) (event->x);
  y = (gint) (event->y);
792
  
793
794
  row = row_from_y (calendar, y);
  col = column_from_x (calendar, x);
Owen Taylor's avatar
Owen Taylor committed
795
796
797
798

  /* If row or column isn't found, just return. */
  if (row == -1 || col == -1)
    return;
799
  
800
  day_month = calendar->day_month[row][col];
801
  
802
803
804
805
806
807
808
809
810
  if (day_month == MONTH_CURRENT)
    {
      if (event->type == GDK_2BUTTON_PRESS)
	gtk_signal_emit (GTK_OBJECT (calendar),
			 gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL]);
      else
	{
	  if (!GTK_WIDGET_HAS_FOCUS (widget))
	    gtk_widget_grab_focus (widget);
811
812
813
814
815
	  old_focus_row = calendar->focus_row;
	  old_focus_col = calendar->focus_col;
	  calendar->focus_row = row;
	  calendar->focus_col = col;
	  gtk_calendar_paint_day (widget, old_focus_row, old_focus_col);
816
817
818
819
820
821
822
823
824
825
826
827
828
	  gtk_calendar_select_day (calendar, calendar->day[row][col]);
	}
    }
  else if (day_month == MONTH_PREV)
    gtk_calendar_set_month_prev (calendar);
  else if (day_month == MONTH_NEXT)
    gtk_calendar_set_month_next (calendar);
}

static void
gtk_calendar_realize_arrows (GtkWidget *widget)
{
  GtkCalendar *calendar;
829
  GtkCalendarPrivateData *private_data;
830
831
832
  GdkWindowAttr attributes;
  gint attributes_mask;
  gint i;
833
  
834
835
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CALENDAR (widget));
836
  
837
  calendar = GTK_CALENDAR (widget);
838
839
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);

840
841
842
843
844
845
846
847
848
849
850
851
852
  /* 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;
853
854
      attributes.width = private_data->arrow_width;
      attributes.height = private_data->header_h - 7;
855
856
857
858
859
860
861
862
      for (i = 0; i < 4; i++)
	{
	  switch (i)
	    {
	    case ARROW_MONTH_LEFT:
	      attributes.x = 3;
	      break;
	    case ARROW_MONTH_RIGHT:
863
864
	      attributes.x = (private_data->arrow_width 
			      + private_data->max_month_width);
865
866
	      break;
	    case ARROW_YEAR_LEFT:
867
868
869
	      attributes.x = (widget->allocation.width - 4
			      - (3 + 2*private_data->arrow_width 
				 + private_data->max_year_width));
870
871
	      break;
	    case ARROW_YEAR_RIGHT:
872
873
	      attributes.x = (widget->allocation.width - 4 
			      - 3 - private_data->arrow_width);
874
875
	      break;
	    }
876
877
878
879
880
	  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],
881
				     HEADER_BG_COLOR (GTK_WIDGET (calendar)));
882
883
	  gdk_window_show (private_data->arrow_win[i]);
	  gdk_window_set_user_data (private_data->arrow_win[i], widget);
884
885
886
	}
    }
  else
887
888
889
890
    {
      for (i = 0; i < 4; i++)
	private_data->arrow_win[i] = NULL;
    }
891
892
893
894
895
896
}

static void
gtk_calendar_realize_header (GtkWidget *widget)
{
  GtkCalendar *calendar;
897
  GtkCalendarPrivateData *private_data;
898
899
  GdkWindowAttr attributes;
  gint attributes_mask;
900
  
901
902
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CALENDAR (widget));
903
  
904
  calendar = GTK_CALENDAR (widget);
905
906
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);

907
908
909
910
911
912
913
914
915
  /* 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;
916
917
918
919
920
      attributes.x = 2;
      attributes.y = 2;
      attributes.width = widget->allocation.width - 4;
      attributes.height = private_data->header_h;
      private_data->header_win = gdk_window_new (widget->window,
921
					     &attributes, attributes_mask);
922
      
923
      gdk_window_set_background (private_data->header_win,
924
				 HEADER_BG_COLOR (GTK_WIDGET (calendar)));
925
926
      gdk_window_show (private_data->header_win);
      gdk_window_set_user_data (private_data->header_win, widget);
927
      
928
929
    }
  else
930
931
932
    {
      private_data->header_win = NULL;
    }
933
934
935
936
937
938
939
  gtk_calendar_realize_arrows (widget);
}

static void
gtk_calendar_realize_day_names (GtkWidget *widget)
{
  GtkCalendar *calendar;
940
  GtkCalendarPrivateData *private_data;
941
942
  GdkWindowAttr attributes;
  gint attributes_mask;
943
  
944
945
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CALENDAR (widget));
946
  
947
  calendar = GTK_CALENDAR (widget);
948
949
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);

950
  /* Day names	window --------------------------------- */
951
952
953
954
955
956
957
958
  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;
959
960
      attributes.x = (widget->style->xthickness + INNER_BORDER);
      attributes.y = private_data->header_h + (widget->style->ythickness 
961
962
					   + INNER_BORDER);
      attributes.width = (widget->allocation.width 
963
			  - (widget->style->xthickness + INNER_BORDER) 
964
965
966
967
968
969
970
971
972
			  * 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);
973
974
    }
  else
975
976
977
    {
      private_data->day_name_win = NULL;
    }
978
979
980
981
982
983
}

static void
gtk_calendar_realize_week_numbers (GtkWidget *widget)
{
  GtkCalendar *calendar;
984
  GtkCalendarPrivateData *private_data;
985
986
  GdkWindowAttr attributes;
  gint attributes_mask;
987
  
988
989
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CALENDAR (widget));
990
  
991
  calendar = GTK_CALENDAR (widget);
992
993
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);

994
995
996
997
998
999
1000
1001
  /* 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;
1002
      
1003
      attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1004
      attributes.x = + (widget->style->xthickness + INNER_BORDER);
1005
      attributes.y = (private_data->header_h + private_data->day_name_h 
1006
		      + (widget->style->ythickness + INNER_BORDER));
1007
1008
1009
1010
1011
1012
1013
1014
      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);
1015
1016
    } 
  else
1017
1018
1019
    {
      private_data->week_win = NULL;
    }
1020
1021
1022
1023
1024
1025
}

static void
gtk_calendar_realize (GtkWidget *widget)
{
  GtkCalendar *calendar;
1026
  GtkCalendarPrivateData *private_data;
1027
1028
  GdkWindowAttr attributes;
  gint attributes_mask;
1029
1030
  GdkGCValues values;

1031
1032
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CALENDAR (widget));
1033
  
1034
  calendar = GTK_CALENDAR (widget);
1035
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1036
  
1037
1038
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
  gtk_calendar_compute_days (calendar);
1039
  
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
  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);
1050
  
1051
1052
1053
  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);
1054
  
1055
  widget->style = gtk_style_attach (widget->style, widget->window);
1056
  
1057
1058
  /* Header window ------------------------------------- */
  gtk_calendar_realize_header (widget);
1059
  /* Day names	window --------------------------------- */
1060
1061
1062
  gtk_calendar_realize_day_names (widget);
  /* Week number window -------------------------------- */
  gtk_calendar_realize_week_numbers (widget);
1063
  /* Main Window --------------------------------------	 */
1064
1065
1066
  attributes.event_mask =  (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
			    | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
			    | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1067
  
1068
  if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1069
    attributes.x = private_data->week_width;
1070
1071
  else
    attributes.x = 0;
1072
  attributes.x += (widget->style->xthickness + INNER_BORDER);
1073
  attributes.y = (private_data->header_h + private_data->day_name_h 
1074
		  + (widget->style->ythickness + INNER_BORDER));
1075
  attributes.width = (widget->allocation.width - attributes.x 
1076
		      - (widget->style->xthickness + INNER_BORDER));
1077
1078
1079
1080
1081
1082
1083
  attributes.height = private_data->main_h;
  private_data->main_win = gdk_window_new (widget->window,
					   &attributes, attributes_mask);
  gdk_window_set_background (private_data->main_win, 
			     BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
  gdk_window_show (private_data->main_win);
  gdk_window_set_user_data (private_data->main_win, widget);
1084
1085
1086
  gdk_window_set_background (widget->window, BACKGROUND_COLOR (widget));
  gdk_window_show (widget->window);
  gdk_window_set_user_data (widget->window, widget);
1087
  
1088
1089
  /* Set widgets gc */
  calendar->gc = gdk_gc_new (widget->window);
1090
1091
1092
1093
1094
1095
1096

  values.foreground = widget->style->white;
  values.function = GDK_XOR;
  calendar->xor_gc = gdk_gc_new_with_values (widget->window,
					     &values,
					     GDK_GC_FOREGROUND |
					     GDK_GC_FUNCTION);
1097
1098
1099
1100
1101
1102
}

static void
gtk_calendar_unrealize (GtkWidget *widget)
{
  GtkCalendar *calendar;
1103
  GtkCalendarPrivateData *private_data;
1104
  gint i;
1105
  
1106
1107
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CALENDAR (widget));
1108
  
1109
  calendar = GTK_CALENDAR (widget);
1110
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1111
  
1112
  if (private_data->header_win)
1113
    {
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
      for (i = 0; i < 4; i++)
	{
	  if (private_data->arrow_win[i])
	    {
	      gdk_window_set_user_data (private_data->arrow_win[i], NULL);
	      gdk_window_destroy (private_data->arrow_win[i]);
	      private_data->arrow_win[i] = NULL;
	    }
	}
      gdk_window_set_user_data (private_data->header_win, NULL);
      gdk_window_destroy (private_data->header_win);
      private_data->header_win = NULL;
1126
    }
1127
  
1128
  if (private_data->week_win)
1129
    {
1130
1131
1132
      gdk_window_set_user_data (private_data->week_win, NULL);
      gdk_window_destroy (private_data->week_win);
      private_data->week_win = NULL;      
1133
    }
1134
  
1135
  if (private_data->main_win)
1136
    {
1137
1138
1139
      gdk_window_set_user_data (private_data->main_win, NULL);
      gdk_window_destroy (private_data->main_win);
      private_data->main_win = NULL;      
1140
    }
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
  if (private_data->day_name_win)
    <