gtkcalendar.c 76.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/* 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
 * modify it under the terms of the GNU Library General Public
 * 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 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <sys/time.h>
#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.  */

38 39 40
typedef	 unsigned   int	    N_int;
typedef	 unsigned   long    N_long;
typedef	 signed	    long    Z_long;
41 42
typedef enum { false = FALSE , true = TRUE } boolean;

43 44
#define and	    &&	    /* logical (boolean) operators: lower case */
#define or	    ||
45 46 47

static N_int month_length[2][13] =
{
48 49
  { 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 }
50 51 52 53
};

static N_int days_in_months[2][14] =
{
54 55
  { 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 }
56 57 58 59 60
};

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,
61
				N_int year2, N_int mm2, N_int dd2);
62 63 64 65 66
static N_int   weeks_in_year(N_int year);

static boolean 
leap(N_int year)
{
67
  return((((year % 4) == 0) and ((year % 100) != 0)) or ((year % 400) == 0));
68 69 70 71 72
}

static N_int 
day_of_week(N_int year, N_int mm, N_int dd)
{
73 74 75 76
  Z_long  days;
  
  days = calc_days(year, mm, dd);
  if (days > 0L)
77
    {
78 79 80
      days--;
      days %= 7L;
      days++;
81
    }
82
  return( (N_int) days );
83 84 85 86
}

static N_int weeks_in_year(N_int year)
{
87
  return(52 + ((day_of_week(year,1,1)==4) or (day_of_week(year,12,31)==4)));
88 89 90 91 92
}

static boolean 
check_date(N_int year, N_int mm, N_int dd)
{
93 94 95 96
  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);
97 98 99 100 101
}

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

static Z_long 
year_to_days(N_int year)
{
112
  return( year * 365L + (year / 4) - (year / 100) + (year / 400) );
113 114 115 116 117 118
}


static Z_long 
calc_days(N_int year, N_int mm, N_int dd)
{
119 120 121 122 123 124
  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 );
125 126 127 128 129
}

static boolean 
week_of_year(N_int *week, N_int *year, N_int mm, N_int dd)
{
130 131 132 133 134 135 136 137 138 139 140 141 142
  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);
143 144 145 146
}

static Z_long 
dates_difference(N_int year1, N_int mm1, N_int dd1,
147
		 N_int year2, N_int mm2, N_int dd2)
148
{
149
  return( calc_days(year2, mm2, dd2) - calc_days(year1, mm1, dd1) );
150 151 152 153
}

/** END OF lib_date routines ************************************************/

154 155 156 157
#define CALENDAR_MARGIN		 0
#define CALENDAR_YSEP		 4
#define CALENDAR_XSEP		 4
#define INNER_BORDER		 4
158 159 160

#define DAY_XPAD		 2
#define DAY_YPAD		 2
161
#define DAY_XSEP		 0 /* not really good for small calendar */
162 163 164
#define DAY_YSEP		 0 /* not really good for small calendar */

/* Color usage */
165 166
#define HEADER_FG_COLOR(widget)		 (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
#define HEADER_BG_COLOR(widget)		 (& (widget)->style->bg[GTK_WIDGET_STATE (widget)])
167
#define DAY_NAME_COLOR(widget)		 (& (widget)->style->bg[GTK_STATE_SELECTED])
168 169
#define NORMAL_DAY_COLOR(widget)	 (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
#define SELECTION_FOCUS_COLOR(widget)	 (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
170
#define SELECTION_NO_FOCUS_COLOR(widget) (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
171 172 173 174 175 176
#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)])
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207

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

208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
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];

  guint header_h, day_name_h, main_h;

  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;
  
  /* flags */
  unsigned int dirty_header:1;
  unsigned int dirty_day_names:1;
  unsigned int dirty_main:1;
  unsigned int dirty_week:1;
  unsigned int frozen;
};

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

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

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
static void gtk_calendar_class_init	(GtkCalendarClass *class);
static void gtk_calendar_init		(GtkCalendar *calendar);
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 gint gtk_calendar_focus_in	(GtkWidget *widget,
					 GdkEventFocus *event);
static gint gtk_calendar_focus_out	(GtkWidget *widget,
					 GdkEventFocus *event);
static void gtk_calendar_state_changed	(GtkWidget *widget,
277
					 GtkStateType previous_state);
278
static void gtk_calendar_style_set	(GtkWidget *widget,
279
					 GtkStyle  *previous_style);
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
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_draw		(GtkWidget    *widget,
					 GdkRectangle *area);
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);
302 303 304 305

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

306 307
GtkType
gtk_calendar_get_type (void)
308
{
309 310
  static GtkType calendar_type = 0;
  
311 312 313 314 315 316 317 318 319 320 321 322
  if (!calendar_type)
    {
      GtkTypeInfo calendar_info =
      {
	"GtkCalendar",
	sizeof (GtkCalendar),
	sizeof (GtkCalendarClass),
	(GtkClassInitFunc) gtk_calendar_class_init,
	(GtkObjectInitFunc) gtk_calendar_init,
	(GtkArgSetFunc) NULL,
	(GtkArgGetFunc) NULL,
      };
323 324
      
      calendar_type = gtk_type_unique (GTK_TYPE_WIDGET, &calendar_info);
325
    }
326
  
327 328 329 330 331 332 333 334
  return calendar_type;
}

static void
gtk_calendar_class_init (GtkCalendarClass *class)
{
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
335
  
336 337
  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;
338 339 340
  
  parent_class = gtk_type_class (GTK_TYPE_WIDGET);
  
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
  widget_class->realize = gtk_calendar_realize;
  widget_class->unrealize = gtk_calendar_unrealize;
  widget_class->expose_event = gtk_calendar_expose;
  widget_class->draw = gtk_calendar_draw;
  widget_class->draw_focus = gtk_calendar_draw_focus;
  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->focus_in_event = gtk_calendar_focus_in;
  widget_class->focus_out_event = gtk_calendar_focus_out;
  widget_class->style_set = gtk_calendar_style_set;
  widget_class->state_changed = gtk_calendar_state_changed;
357
  
358 359 360
  gtk_calendar_signals[MONTH_CHANGED_SIGNAL] =
    gtk_signal_new ("month_changed",
		    GTK_RUN_FIRST, object_class->type,
361
		    GTK_SIGNAL_OFFSET (GtkCalendarClass, month_changed),
362 363 364 365
		    gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
  gtk_calendar_signals[DAY_SELECTED_SIGNAL] =
    gtk_signal_new ("day_selected",
		    GTK_RUN_FIRST, object_class->type,
366
		    GTK_SIGNAL_OFFSET (GtkCalendarClass, day_selected),
367 368 369 370
		    gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
  gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL] =
    gtk_signal_new ("day_selected_double_click",
		    GTK_RUN_FIRST, object_class->type,
371
		    GTK_SIGNAL_OFFSET (GtkCalendarClass, day_selected_double_click),
372 373 374 375
		    gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
  gtk_calendar_signals[PREV_MONTH_SIGNAL] =
    gtk_signal_new ("prev_month",
		    GTK_RUN_FIRST, object_class->type,
376
		    GTK_SIGNAL_OFFSET (GtkCalendarClass, prev_month),
377 378 379 380
		    gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
  gtk_calendar_signals[NEXT_MONTH_SIGNAL] =
    gtk_signal_new ("next_month",
		    GTK_RUN_FIRST, object_class->type,
381
		    GTK_SIGNAL_OFFSET (GtkCalendarClass, next_month),
382 383 384 385
		    gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
  gtk_calendar_signals[PREV_YEAR_SIGNAL] =
    gtk_signal_new ("prev_year",
		    GTK_RUN_FIRST, object_class->type,
386
		    GTK_SIGNAL_OFFSET (GtkCalendarClass, prev_year),
387 388 389 390
		    gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
  gtk_calendar_signals[NEXT_YEAR_SIGNAL] =
    gtk_signal_new ("next_year",
		    GTK_RUN_FIRST, object_class->type,
391
		    GTK_SIGNAL_OFFSET (GtkCalendarClass, next_year),
392
		    gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
393
  
394
  gtk_object_class_add_signals (object_class, gtk_calendar_signals, LAST_SIGNAL);
395 396 397 398 399 400 401 402
  
  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;
403 404 405 406 407 408 409 410 411 412 413
}

static void
gtk_calendar_init (GtkCalendar *calendar)
{
  time_t secs;
  struct tm *tm;
  gint i;
  char buffer[255];
  time_t tmp_time;
  GtkWidget *widget;
414
  GtkCalendarPrivateData *private_data;
415
  
416 417
  widget = GTK_WIDGET (calendar);
  GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
418
  
419 420 421
  calendar->private_data = (gpointer) malloc (sizeof (GtkCalendarPrivateData));
  private_data = GTK_CALENDAR_PRIVATE_DATA (calendar);

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

458 459 460 461 462 463 464 465 466 467 468
  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;
469
  
470 471 472 473 474
  private_data->dirty_header = 0;
  private_data->dirty_day_names = 0;
  private_data->dirty_week = 0;
  private_data->dirty_main = 0;
  private_data->frozen = 0;
475 476 477
}

GtkWidget*
478
gtk_calendar_new (void)
479
{
480
  return GTK_WIDGET (gtk_type_new (GTK_TYPE_CALENDAR));
481 482 483 484 485
}

/* column_from_x: returns the column 0-6 that the
 * x pixel of the xwindow is in */
static gint
486 487
column_from_x (GtkCalendar *calendar,
	       gint	    event_x)
488 489 490
{
  gint c, column;
  gint x_left, x_right;
491
  
492
  column = -1;
493
  
494 495 496
  for (c = 0; c < 7; c++)
    {
      x_left = left_x_for_column (calendar, c);
497
      x_right = x_left + GTK_CALENDAR_PRIVATE_DATA (calendar)->day_width;
498
      
499 500 501 502 503 504
      if (event_x > x_left && event_x < x_right)
	{
	  column = c;
	  break;
	}
    }
505
  
506 507 508 509 510 511
  return column;
}

static gint
row_height (GtkCalendar *calendar)
{
512
  return (GTK_CALENDAR_PRIVATE_DATA (calendar)->main_h - CALENDAR_MARGIN
513 514 515 516 517 518 519 520
	  - ((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
521 522
row_from_y (GtkCalendar *calendar,
	    gint	 event_y)
523 524 525 526
{
  gint r, row;
  gint height;
  gint y_top, y_bottom;
527
  
528 529
  height = row_height (calendar);
  row = -1;
530
  
531 532 533 534
  for (r = 0; r < 6; r++)
    {
      y_top = top_y_for_row (calendar, r);
      y_bottom = y_top + height;
535
      
536 537 538 539 540 541
      if (event_y > y_top && event_y < y_bottom)
	{
	  row = r;
	  break;
	}
    }
542
  
543 544 545 546 547 548
  return row;
}

/* left_x_for_column: returns the x coordinate
 * for the left of the column */
static gint
549 550
left_x_for_column (GtkCalendar *calendar,
		   gint		column)
551 552 553
{
  gint width;
  gint x_left;
554
  
555
  width = GTK_CALENDAR_PRIVATE_DATA (calendar)->day_width;
556 557 558 559
  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;
560
  
561 562 563 564 565 566
  return x_left;
}

/* top_y_for_row: returns the y coordinate
 * for the top of the row */
static gint
567 568
top_y_for_row (GtkCalendar *calendar,
	       gint	    row)
569
{
570
  
571 572 573
  return (GTK_CALENDAR_PRIVATE_DATA (calendar)->main_h 
	  - (CALENDAR_MARGIN + (6 - row)
	     * row_height (calendar)));
574 575 576 577 578
}

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


static void
gtk_calendar_set_month_next (GtkCalendar *calendar)
{
  gint month_len;
667
  
668 669
  g_return_if_fail (calendar != NULL);
  g_return_if_fail (GTK_IS_WIDGET (calendar));
670
  
671 672
  if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
    return;
673 674
  
  
675 676 677 678 679 680 681
  if (calendar->month == 11)
    {
      calendar->month = 0;
      calendar->year++;
    } 
  else 
    calendar->month++;
682
  
683 684 685 686 687 688
  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]);
689
  
690
  month_len = month_length[leap (calendar->year)][calendar->month + 1];
691
  
692 693 694 695 696 697 698
  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);
699
  
700 701 702 703 704 705 706 707
  gtk_calendar_paint (GTK_WIDGET(calendar), NULL);
  gtk_calendar_thaw (calendar);
}

static void
gtk_calendar_set_year_prev (GtkCalendar *calendar)
{
  gint month_len;
708
  
709 710
  g_return_if_fail (calendar != NULL);
  g_return_if_fail (GTK_IS_WIDGET (calendar));
711
  
712 713 714 715 716 717 718
  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]);
719
  
720
  month_len = month_length[leap (calendar->year)][calendar->month + 1];
721
  
722 723 724 725 726 727 728
  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);
729
  
730 731 732 733 734 735 736 737 738
  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;
739
  
740 741
  g_return_if_fail (calendar != NULL);
  g_return_if_fail (GTK_IS_WIDGET (calendar));
742
  
743
  widget = GTK_WIDGET (calendar);
744
  
745
  gtk_calendar_freeze (calendar);
746
  
747 748 749 750 751 752
  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]);
753
  
754
  month_len = month_length[leap (calendar->year)][calendar->month + 1];
755
  
756 757 758 759 760 761 762
  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);
763
  
764 765 766 767 768
  gtk_calendar_paint (GTK_WIDGET (calendar), NULL);
  gtk_calendar_thaw (calendar);
}

static void
769
gtk_calendar_main_button (GtkWidget	 *widget,
770 771 772 773 774 775
			  GdkEventButton *event)
{
  GtkCalendar *calendar;
  gint x, y;
  gint row, col;
  gint day_month;
776
  gint old_focus_row, old_focus_col;
777
  
778
  calendar = GTK_CALENDAR (widget);
779
  
780 781
  x = (gint) (event->x);
  y = (gint) (event->y);
782
  
783 784
  row = row_from_y (calendar, y);
  col = column_from_x (calendar, x);
785
  
786
  day_month = calendar->day_month[row][col];
787
  
788 789 790 791 792 793 794 795 796
  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);
797 798 799 800 801
	  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);
802 803 804 805 806 807 808 809 810 811 812 813 814
	  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;
815
  GtkCalendarPrivateData *private_data;
816 817 818
  GdkWindowAttr attributes;
  gint attributes_mask;
  gint i;
819
  
820 821
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CALENDAR (widget));
822
  
823
  calendar = GTK_CALENDAR (widget);
824 825
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);

826 827 828 829 830 831 832 833 834 835 836 837 838
  /* 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;
839 840
      attributes.width = private_data->arrow_width;
      attributes.height = private_data->header_h - 7;
841 842 843 844 845 846 847 848
      for (i = 0; i < 4; i++)
	{
	  switch (i)
	    {
	    case ARROW_MONTH_LEFT:
	      attributes.x = 3;
	      break;
	    case ARROW_MONTH_RIGHT:
849 850
	      attributes.x = (private_data->arrow_width 
			      + private_data->max_month_width);
851 852
	      break;
	    case ARROW_YEAR_LEFT:
853 854 855
	      attributes.x = (widget->allocation.width - 4
			      - (3 + 2*private_data->arrow_width 
				 + private_data->max_year_width));
856 857
	      break;
	    case ARROW_YEAR_RIGHT:
858 859
	      attributes.x = (widget->allocation.width - 4 
			      - 3 - private_data->arrow_width);
860 861
	      break;
	    }
862 863 864 865 866
	  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],
867
				     HEADER_BG_COLOR (GTK_WIDGET (calendar)));
868 869
	  gdk_window_show (private_data->arrow_win[i]);
	  gdk_window_set_user_data (private_data->arrow_win[i], widget);
870 871 872
	}
    }
  else
873 874 875 876
    {
      for (i = 0; i < 4; i++)
	private_data->arrow_win[i] = NULL;
    }
877 878 879 880 881 882
}

static void
gtk_calendar_realize_header (GtkWidget *widget)
{
  GtkCalendar *calendar;
883
  GtkCalendarPrivateData *private_data;
884 885
  GdkWindowAttr attributes;
  gint attributes_mask;
886
  
887 888
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CALENDAR (widget));
889
  
890
  calendar = GTK_CALENDAR (widget);
891 892
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);

893 894 895 896 897 898 899 900 901
  /* 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;
902 903 904 905 906
      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,
907
					     &attributes, attributes_mask);
908
      
909
      gdk_window_set_background (private_data->header_win,
910
				 HEADER_BG_COLOR (GTK_WIDGET (calendar)));
911 912
      gdk_window_show (private_data->header_win);
      gdk_window_set_user_data (private_data->header_win, widget);
913
      
914 915
    }
  else
916 917 918
    {
      private_data->header_win = NULL;
    }
919 920 921 922 923 924 925
  gtk_calendar_realize_arrows (widget);
}

static void
gtk_calendar_realize_day_names (GtkWidget *widget)
{
  GtkCalendar *calendar;
926
  GtkCalendarPrivateData *private_data;
927 928
  GdkWindowAttr attributes;
  gint attributes_mask;
929
  
930 931
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CALENDAR (widget));
932
  
933
  calendar = GTK_CALENDAR (widget);
934 935
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);

936
  /* Day names	window --------------------------------- */
937 938 939 940 941 942 943 944 945
  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;
      attributes.x = (widget->style->klass->xthickness + INNER_BORDER);
946 947 948 949 950 951 952 953 954 955 956 957 958
      attributes.y = private_data->header_h + (widget->style->klass->ythickness 
					   + INNER_BORDER);
      attributes.width = (widget->allocation.width 
			  - (widget->style->klass->xthickness + INNER_BORDER) 
			  * 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);
959 960
    }
  else
961 962 963
    {
      private_data->day_name_win = NULL;
    }
964 965 966 967 968 969
}

static void
gtk_calendar_realize_week_numbers (GtkWidget *widget)
{
  GtkCalendar *calendar;
970
  GtkCalendarPrivateData *private_data;
971 972
  GdkWindowAttr attributes;
  gint attributes_mask;
973
  
974 975
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CALENDAR (widget));
976
  
977
  calendar = GTK_CALENDAR (widget);
978 979
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);

980 981 982 983 984 985 986 987
  /* 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;
988
      
989 990
      attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
      attributes.x = + (widget->style->klass->xthickness + INNER_BORDER);
991
      attributes.y = (private_data->header_h + private_data->day_name_h 
992
		      + (widget->style->klass->ythickness + INNER_BORDER));
993 994 995 996 997 998 999 1000
      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);
1001 1002
    } 
  else
1003 1004 1005
    {
      private_data->week_win = NULL;
    }
1006 1007 1008 1009 1010 1011
}

static void
gtk_calendar_realize (GtkWidget *widget)
{
  GtkCalendar *calendar;
1012
  GtkCalendarPrivateData *private_data;
1013 1014
  GdkWindowAttr attributes;
  gint attributes_mask;
1015 1016
  GdkGCValues values;

1017 1018
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CALENDAR (widget));
1019
  
1020
  calendar = GTK_CALENDAR (widget);
1021
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1022
  
1023 1024
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
  gtk_calendar_compute_days (calendar);
1025
  
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
  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);
1036
  
1037 1038 1039
  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);
1040
  
1041
  widget->style = gtk_style_attach (widget->style, widget->window);
1042
  
1043 1044
  /* Header window ------------------------------------- */
  gtk_calendar_realize_header (widget);
1045
  /* Day names	window --------------------------------- */
1046 1047 1048
  gtk_calendar_realize_day_names (widget);
  /* Week number window -------------------------------- */
  gtk_calendar_realize_week_numbers (widget);
1049
  /* Main Window --------------------------------------	 */
1050 1051 1052
  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);
1053
  
1054
  if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1055
    attributes.x = private_data->week_width;
1056 1057 1058
  else
    attributes.x = 0;
  attributes.x += (widget->style->klass->xthickness + INNER_BORDER);
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
  attributes.y = (private_data->header_h + private_data->day_name_h 
		  + (widget->style->klass->ythickness + INNER_BORDER));
  attributes.width = (widget->allocation.width - attributes.x 
		      - (widget->style->klass->xthickness + INNER_BORDER));
  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);
1070 1071 1072
  gdk_window_set_background (widget->window, BACKGROUND_COLOR (widget));
  gdk_window_show (widget->window);
  gdk_window_set_user_data (widget->window, widget);
1073
  
1074 1075
  /* Set widgets gc */
  calendar->gc = gdk_gc_new (widget->window);
1076 1077 1078 1079 1080 1081 1082

  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);
1083 1084 1085 1086 1087 1088
}

static void
gtk_calendar_unrealize (GtkWidget *widget)
{
  GtkCalendar *calendar;
1089
  GtkCalendarPrivateData *private_data;
1090
  gint i;
1091
  
1092 1093
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CALENDAR (widget));
1094
  
1095
  calendar = GTK_CALENDAR (widget);
1096
  private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1097
  
1098
  if (private_data->header_win)
1099
    {
1100 1101 1102 1103 1104 1105 1106 1107