home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2305 / Calendar.c
C/C++ Source or Header  |  1990-12-28  |  58KB  |  1,951 lines

  1. /*
  2.  * Author: Jason Baietto, jason@ssd.csd.harris.com
  3.  * xdiary Copyright 1990 Harris Corporation
  4.  *
  5.  * Permission to use, copy, modify, and distribute, this software and its
  6.  * documentation for any purpose is hereby granted without fee, provided that
  7.  * the above copyright notice appear in all copies and that both that
  8.  * copyright notice and this permission notice appear in supporting
  9.  * documentation, and that the name of the copyright holder be used in
  10.  * advertising or publicity pertaining to distribution of the software with
  11.  * specific, written prior permission, and that no fee is charged for further
  12.  * distribution of this software, or any modifications thereof.  The copyright
  13.  * holder makes no representations about the suitability of this software for
  14.  * any purpose.  It is provided "as is" without express or implied warranty.
  15.  *
  16.  * THE COPYRIGHT HOLDER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  17.  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND IN NO
  18.  * EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  19.  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM ITS USE,
  20.  * LOSS OF DATA, PROFITS, QPA OR GPA, WHETHER IN AN ACTION OF CONTRACT,
  21.  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
  22.  * THE USE OR PERFORMANCE OF THIS SOFTWARE.
  23.  */
  24.  
  25. /*==========================================================================*/
  26. /*                              Header Files:                               */
  27. /*==========================================================================*/
  28. #include <stdio.h>
  29. #include <X11/Xos.h>
  30. #include <X11/StringDefs.h>
  31. #include <X11/IntrinsicP.h>
  32. #include <X11/Xmu/Converters.h>
  33. #include "CalendarP.h"
  34.  
  35. #ifdef DEBUG
  36. #define BEGIN(str) (/*VARARGS0*/fprintf(stderr, "%s()\n", (str)))
  37. #define END(str) ;
  38. #else
  39. #define BEGIN(str) ;
  40. #define END(str) ;
  41. #endif
  42.  
  43.  
  44. /*==========================================================================*/
  45. /*                            Forward References:                           */
  46. /*==========================================================================*/
  47. static void initialize_line_GCs();
  48. static void initialize_font_GCs_and_info();
  49. static void compute_minimum_cell_size();
  50. static void compute_cell_geometry();
  51. static void free_line_GCs();
  52. static void free_font_GCs_and_info();
  53. static void compute_grid();
  54. static void draw_grid();
  55. static void draw_weekdays();
  56. static void draw_title();
  57. static void draw_digits();
  58. static void compute_title_string();
  59. static void toggle_highlight();
  60. static void rotate_weekdays();
  61. static void calendar_update();
  62. static void compute_month_data();
  63.  
  64.  
  65.  
  66.  
  67. /*==========================================================================*/
  68. /*                            Static Global Data:                           */
  69. /*==========================================================================*/
  70. static int days_in_month[] = 
  71.    /* jan feb mar apr may jun jul aug sep oct nov dec */
  72.    {  31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31  };
  73.  
  74.  
  75.  
  76.  
  77. /*==========================================================================*/
  78. /*                              Resource List:                              */
  79. /*==========================================================================*/
  80. #define offset(field) XtOffset(CalendarWidget, field)
  81. static XtResource calendar_resources[] = {
  82.    {
  83.       XtNcallback,
  84.       XtCCallback,
  85.       XtRCallback,
  86.       sizeof(XtPointer),
  87.       offset(calendar.callback),
  88.       XtRCallback,
  89.       NULL
  90.    },
  91.    {
  92.       XtNlineWidth,
  93.       XtCLineWidth,
  94.       XtRInt,
  95.       sizeof(int),
  96.       offset(calendar.line_width),
  97.       XtRImmediate,
  98.       (XtPointer)DEFAULT_LINE_WIDTH
  99.    },
  100.    {
  101.       XtNforeground,
  102.       XtCForeground,
  103.       XtRPixel,
  104.       sizeof(Pixel),
  105.       offset(calendar.foreground),
  106.       XtRString,
  107.       XtDefaultForeground
  108.    },
  109.    {
  110.       XtNbackground,
  111.       XtCBackground,
  112.       XtRPixel,
  113.       sizeof(Pixel),
  114.       offset(calendar.background),
  115.       XtRString,
  116.       XtDefaultBackground
  117.    },
  118.    {
  119.       XtNdigitFont,
  120.       XtCCalendarFont,
  121.       XtRFont,
  122.       sizeof(Font),
  123.       offset(calendar.digit_font),
  124.       XtRString,
  125.       CALENDAR_DEFAULT_FONT
  126.    },
  127.    {
  128.       XtNweekdayFont,
  129.       XtCCalendarFont,
  130.       XtRFont,
  131.       sizeof(Font),
  132.       offset(calendar.weekday_font),
  133.       XtRString,
  134.       CALENDAR_DEFAULT_FONT
  135.    },
  136.    {
  137.       XtNtitleFont,
  138.       XtCCalendarFont,
  139.       XtRFont,
  140.       sizeof(Font),
  141.       offset(calendar.title_font),
  142.       XtRString,
  143.       CALENDAR_DEFAULT_FONT
  144.    },
  145.    {
  146.       XtNinfoFont,
  147.       XtCCalendarFont,
  148.       XtRFont,
  149.       sizeof(Font),
  150.       offset(calendar.info_font),
  151.       XtRString,
  152.       CALENDAR_DEFAULT_FONT
  153.    },
  154.    {
  155.       XtNdigitGravity,
  156.       XtCDigitGravity,
  157.       XtRGravity,
  158.       sizeof(XtGravity),
  159.       offset(calendar.digit_gravity),
  160.       XtRImmediate,
  161.       (XtPointer)Center
  162.    },
  163.    {  XtNdigitNames,
  164.       XtCDigitNames,
  165.       XtRStringTable,
  166.       sizeof(StringTable),
  167.       offset(calendar.digit_names),
  168.       XtRString,
  169.       XtNDefaultDigitNames
  170.    },
  171.    {  XtNweekdayNames,
  172.       XtCWeekdayNames,
  173.       XtRStringTable,
  174.       sizeof(StringTable),
  175.       offset(calendar.weekday_names),
  176.       XtRString,
  177.       XtNDefaultWeekdayNames
  178.    },
  179.    {  XtNmonthNames,
  180.       XtCMonthNames,
  181.       XtRStringTable,
  182.       sizeof(StringTable),
  183.       offset(calendar.month_names),
  184.       XtRString,
  185.       XtNDefaultMonthNames
  186.    },
  187.    {  XtNhighlight,
  188.       XtCHighlight,
  189.       XtRBoolean,
  190.       sizeof(Boolean),
  191.       offset(calendar.highlight),
  192.       XtRImmediate,
  193.       (XtPointer)True
  194.    },
  195.    {  XtNshowYear,
  196.       XtCShowYear,
  197.       XtRBoolean,
  198.       sizeof(Boolean),
  199.       offset(calendar.show_year),
  200.       XtRImmediate,
  201.       (XtPointer)True
  202.    },
  203.    {  XtNstartingWeekday,
  204.       XtCStartingWeekday,
  205.       XtRDayName,
  206.       sizeof(XtDayName),
  207.       offset(calendar.starting_weekday),
  208.       XtRImmediate,
  209.       (XtPointer)Sunday
  210.    }
  211. };
  212.  
  213.  
  214.  
  215. /*==========================================================================*/
  216. /*                           Action Declarations:                           */
  217. /*==========================================================================*/
  218. static void select_cell_action();
  219. static void default_button_up_action();
  220.  
  221.  
  222.  
  223. /*==========================================================================*/
  224. /*                              Actions Table:                              */
  225. /*==========================================================================*/
  226. static XtActionsRec calendar_actions[] = {
  227.    { "select",  select_cell_action       },
  228.    { "notify",  default_button_up_action }
  229. };
  230.  
  231. static char calendar_default_translations[] = XtRcalendarDefaultTranslations;
  232.  
  233.  
  234. /*==========================================================================*/
  235. /*                           Method Declarations:                           */
  236. /*==========================================================================*/
  237. static void             calendar_initialize_method();
  238. static void             calendar_class_initialize_method();
  239. static void             calendar_expose_method();
  240. static void             calendar_destroy_method();
  241. static void             calendar_resize_method();
  242. static Boolean          calendar_set_values_method();
  243. static XtGeometryResult calendar_query_geometry_method();
  244.  
  245.  
  246.  
  247. /*==========================================================================*/
  248. /*                       Class Record Initialization:                       */
  249. /*==========================================================================*/
  250. CalendarClassRec calendarClassRec = {
  251.    {
  252.       /* CORE CLASS PART:      */
  253.       /* superclass            */ (WidgetClass) &coreClassRec,
  254.       /* class_name            */ "Calendar",
  255.       /* widget_size           */ sizeof(CalendarRec),
  256.       /* class_initialize      */ calendar_class_initialize_method,
  257.       /* class_part_initialize */ NULL,
  258.       /* class_inited          */ FALSE,
  259.       /* initialize            */ calendar_initialize_method,
  260.       /* initialize_hook       */ NULL,
  261.       /* realize               */ XtInheritRealize,
  262.       /* actions               */ calendar_actions,
  263.       /* num_actions           */ XtNumber(calendar_actions),
  264.       /* resources             */ calendar_resources,
  265.       /* num_resources         */ XtNumber(calendar_resources),
  266.       /* xrm_class             */ NULLQUARK,
  267.       /* compress_motion       */ TRUE,
  268.       /* compress_exposure     */ TRUE,
  269.       /* compress_enterleave   */ TRUE,
  270.       /* visible_interest      */ FALSE,
  271.       /* destroy               */ calendar_destroy_method,
  272.       /* resize                */ calendar_resize_method,
  273.       /* expose                */ calendar_expose_method,
  274.       /* set_values            */ calendar_set_values_method,
  275.       /* set_values_hook       */ NULL,
  276.       /* set_values_almost     */ XtInheritSetValuesAlmost,
  277.       /* get_values_hook       */ NULL,
  278.       /* accept_focus          */ NULL,
  279.       /* version               */ XtVersion,
  280.       /* callback_private      */ NULL,
  281.       /* tm_table              */ calendar_default_translations,
  282.       /* query_geometry        */ calendar_query_geometry_method,
  283.       /* display_accelerator   */ XtInheritDisplayAccelerator,
  284.       /* extension             */ NULL
  285.    },
  286.    {
  287.       /* CALENDAR CLASS PART:  */
  288.       /* dummy field           */ 0
  289.    }
  290. };
  291.  
  292. /* Define the calendar widget class in terms of the above record. */
  293. WidgetClass calendarWidgetClass = (WidgetClass) &calendarClassRec;
  294.  
  295.  
  296. static XtConvertArgRec screenConvertArg[] = {
  297.    {  
  298.       XtBaseOffset,
  299.       (XtPointer)XtOffset(Widget, core.screen),
  300.       sizeof(Screen *)
  301.    }
  302. };
  303.  
  304.  
  305.  
  306. /*==========================================================================*/
  307. /*                           Method Definitions:                            */
  308. /*==========================================================================*/
  309. static void calendar_class_initialize_method()
  310. {
  311.    /* Register the string to gravity resource converter. */
  312.    XtAddConverter(
  313.       XtRString,                   /* source type */
  314.       XtRGravity,                  /* target type */
  315.       GravityConverter,            /* converter routine */
  316.       NULL,                        /* args for converter */
  317.       0                            /* num args for converter */
  318.    );
  319.  
  320.    /* Register the string to gravity resource converter. */
  321.    XtAddConverter(
  322.       XtRString,                   /* source type */
  323.       XtRDayName,                  /* target type */
  324.       DayNameConverter,            /* converter routine */
  325.       NULL,                        /* args for converter */
  326.       0                            /* num args for converter */
  327.    );
  328.  
  329.    /* Register the string to string table resource converter. */
  330.    XtAddConverter(
  331.       XtRString,                   /* source type */
  332.       XtRStringTable,              /* target type */
  333.       StringTableConverter,        /* converter routine */
  334.       NULL,                        /* args for converter */
  335.       0                            /* num args for converter */
  336.    );
  337.  
  338.    /* Register the string to bitmap resource converter. */
  339.    XtAddConverter(
  340.       XtRString,                   /* source type */
  341.       XtRBitmap,                   /* target type */
  342.       XmuCvtStringToBitmap,        /* converter routine */
  343.       screenConvertArg,            /* args for converter */
  344.       XtNumber(screenConvertArg)   /* num args for converter */
  345.    );
  346. }
  347.  
  348.  
  349. static void calendar_initialize_method(request, new)
  350. CalendarWidget request;
  351. CalendarWidget new;
  352. {
  353.    XrmValue from;
  354.    XrmValue to;
  355.    int i;
  356.    float initial_cell_width  = (float) request->core.width / (float) COLS;
  357.    float initial_cell_height = (float) request->core.height / (float) ROWS;
  358.  
  359.    BEGIN("calendar_initialize_method");
  360.  
  361.    /* Now entering the foggy zone. */
  362.    new->calendar.state = foggy;
  363.  
  364.    /* First check resources: */
  365.  
  366.    /* Did we get 7 weekday names? */
  367.    if (StringTableNumber(request->calendar.weekday_names) != 7) {
  368.       /*VARARGS*/
  369.       fprintf(stderr, "CalendarWidget: You must specify 7 weekday names.\n");
  370.  
  371.       /* Explicitly invoke converter on default value */
  372.       from.addr = XtNDefaultWeekdayNames;
  373.       from.size = sizeof(StringTable);
  374.       XtConvert(new, XtRString, &from, XtRStringTable, &to);
  375.       request->calendar.weekday_names = *(StringTable *)to.addr;
  376.    }
  377.    
  378.    /* Make a widget specific copy so we can twiddle with it if necessary. */
  379.    new->calendar.weekday_names 
  380.       = StringTableCopy(request->calendar.weekday_names);
  381.  
  382.    /* Do we need to rotate the weekdays? */
  383.    if (new->calendar.starting_weekday != SUNDAY) {
  384.       rotate_weekdays(new);
  385.    }
  386.  
  387.    /* Did we get 12 month names? */
  388.    if (StringTableNumber(request->calendar.month_names) != 12) {
  389.       /*VARARGS*/
  390.       fprintf(stderr, "CalendarWidget: You must specify 12 month names.\n");
  391.  
  392.       /* Explicitly invoke converter on default value */
  393.       from.addr = XtNDefaultMonthNames;
  394.       from.size = sizeof(StringTable);
  395.       XtConvert(new, XtRString, &from, XtRStringTable, &to);
  396.       new->calendar.month_names = *(StringTable *)to.addr;
  397.       
  398.    }
  399.  
  400.    /* Did we get 31 digit names? */
  401.    if (StringTableNumber(request->calendar.digit_names) != 31) {
  402.       /*VARARGS*/
  403.       fprintf(stderr, "CalendarWidget: You must specify 31 digit names.\n");
  404.  
  405.       /* Explicitly invoke converter on default value */
  406.       from.addr = XtNDefaultDigitNames;
  407.       from.size = sizeof(StringTable);
  408.       XtConvert(new, XtRString, &from, XtRStringTable, &to);
  409.       new->calendar.digit_names = *(StringTable *)to.addr;
  410.    }
  411.  
  412.    range_check(
  413.       XtNlineWidth,
  414.       new->calendar.line_width,
  415.       MIN_LINE_WIDTH,
  416.       MAX_LINE_WIDTH
  417.    );
  418.  
  419.    range_check(
  420.       XtNdigitGravity,
  421.       new->calendar.digit_gravity,
  422.       NorthWest,
  423.       SouthEast
  424.    );
  425.  
  426.    /* Next compute initial state: */
  427.  
  428.    /* Compute the lengths of the month, weekday and digit names once and keep */
  429.    /* around so we won't have to recompute them every time they're drawn. */
  430.    for (i=0; i < DAYS_IN_WEEK; i++) {
  431.       new->calendar.weekday_name_lengths[i] = strlen(new->calendar.weekday_names[i]);
  432.    }
  433.    for (i=0; i < MONTHS_IN_YEAR; i++) {
  434.       new->calendar.month_name_lengths[i] = strlen(new->calendar.month_names[i]);
  435.    }
  436.    for (i=0; i < MAX_DAYS_IN_MONTH; i++) {
  437.       new->calendar.digit_name_lengths[i] = strlen(new->calendar.digit_names[i]);
  438.    }
  439.  
  440.    initialize_line_GCs(new);
  441.    initialize_font_GCs_and_info(new);
  442.    compute_minimum_cell_size(new);
  443.  
  444.    new->calendar.real_cell_width
  445.       = MAX(new->calendar.min_cell_width + 1, initial_cell_width);
  446.    new->calendar.real_cell_height
  447.       = MAX(new->calendar.min_cell_height + 1, initial_cell_height);
  448.  
  449.    new->calendar.old_window_width = -1;
  450.    new->calendar.old_window_height = -1;
  451.  
  452.    if (new->core.width == 0) {
  453.       new->core.width = WINDOW_WIDTH(new->calendar.real_cell_width);
  454.    }
  455.    if (new->core.height == 0) {
  456.       new->core.height = WINDOW_HEIGHT(new->calendar.real_cell_height);
  457.    }
  458.  
  459.    compute_grid          (new);
  460.    compute_cell_geometry (new);
  461.  
  462.    /* Calendar defaults to current date. */
  463.    new->calendar.date = GetTodaysDate();
  464.  
  465.    /* Allocate a fixed amount of space for the title string. */
  466.    new->calendar.title_string = (char *)XtMalloc(MAX_TITLE_LEN*sizeof(char));
  467.  
  468.    /* Force a re-calculation of the month data for the current year. */
  469.    new->calendar.current_year = 0;
  470.    compute_month_data(new);
  471.  
  472.    new->calendar.highlight_days_in_february 
  473.       = new->calendar.days_in_february;
  474.  
  475.    /* Calendar highlights current day by default, if highligh is true. */
  476.    new->calendar.highlight_date = new->calendar.date;
  477.  
  478.    compute_title_string(new);
  479.  
  480.    END("calendar_initialize_method");
  481. }
  482.  
  483.  
  484.  
  485. /*ARGSUSED*/
  486. static void calendar_expose_method(widget, event)
  487. CalendarWidget widget;
  488. XExposeEvent *event;
  489. {
  490.    BEGIN("calendar_expose_method");
  491.  
  492.    /* Okay, it's safe to draw now. */
  493.    widget->calendar.state = solid;
  494.  
  495.    draw_weekdays    (widget);
  496.    draw_title       (widget);
  497.    draw_digits      (widget);
  498.  
  499.    if (SYNC(widget)) {
  500.       toggle_highlight (widget, ON);
  501.    }
  502.  
  503.    draw_grid        (widget);
  504.    
  505.    END("calendar_expose_method");
  506. }
  507.  
  508.  
  509.  
  510. static Boolean calendar_set_values_method(current, request, new)
  511. CalendarWidget current;
  512. CalendarWidget request;
  513. CalendarWidget new;
  514. {
  515.    Boolean redraw = False;
  516.    Boolean resize = False;
  517.    Boolean new_weekday_names = False;
  518.    Boolean new_starting_weekday = False;
  519.    Display * display;
  520.    XGCValues values;
  521.    XtGCMask mask;
  522.    int i; 
  523.    int new_width;
  524.    int new_height;
  525.  
  526.    BEGIN("calendar_set_values_method");
  527.  
  528.    display = XtDisplay(current);
  529.    
  530.    /* Line Width: */
  531.    if  (request->calendar.line_width != current->calendar.line_width) {
  532.       new->calendar.line_width = bound(
  533.          request->calendar.line_width,
  534.          MIN_LINE_WIDTH,
  535.          MAX_LINE_WIDTH
  536.       );
  537.    }
  538.    if (new->calendar.line_width != current->calendar.line_width) {
  539.       mask = GCLineWidth;
  540.       values.line_width = new->calendar.line_width;
  541.       XChangeGC(
  542.          display,
  543.          new->calendar.draw_gc,
  544.          mask,
  545.          &values
  546.       );
  547.       redraw = True;
  548.    }
  549.  
  550.    /* Digit Gravity: */
  551.    if (request->calendar.digit_gravity != current->calendar.digit_gravity) {
  552.       new->calendar.digit_gravity = (XtGravity) bound(
  553.          (int) request->calendar.digit_gravity,
  554.          (int) NorthWest,
  555.          (int) SouthEast
  556.       );
  557.    }
  558.    if (new->calendar.digit_gravity != current->calendar.digit_gravity) {
  559.       redraw = True;
  560.    }
  561.    
  562.    /* Weekday Names: */
  563.    if (request->calendar.weekday_names != current->calendar.weekday_names) {
  564.       if (StringTableNumber(request->calendar.weekday_names) != DAYS_IN_WEEK) {
  565.          /*VARARGS*/
  566.          fprintf(stderr, "CalendarWidget: must specify 7 weekday names\n");
  567.       } else {
  568.          /* Copy the string table so we can rotate it if necessary. */
  569.          new->calendar.weekday_names = 
  570.             StringTableCopy(request->calendar.weekday_names);
  571.          for (i=0; i < DAYS_IN_WEEK; i++) {
  572.             new->calendar.weekday_name_lengths[i] 
  573.                = strlen(request->calendar.weekday_names[i]);
  574.          }
  575.          new_weekday_names = True;
  576.          redraw = True;
  577.          resize = True;
  578.       }
  579.    }
  580.  
  581.    /* Month Names: */
  582.    if (request->calendar.month_names != current->calendar.month_names) {
  583.       if (StringTableNumber(request->calendar.month_names) != MONTHS_IN_YEAR) {
  584.          /*VARARGS*/
  585.          fprintf(stderr, "CalendarWidget: must specify 12 month names\n");
  586.       } else {
  587.          new->calendar.month_names = request->calendar.month_names;
  588.          for (i=0; i < MONTHS_IN_YEAR; i++) {
  589.             new->calendar.month_name_lengths[i] 
  590.                = strlen(request->calendar.month_names[i]);
  591.          }
  592.          compute_title_string(new);
  593.          redraw = True;
  594.          resize = True;
  595.       }
  596.    }
  597.  
  598.    /* Digit Names: */
  599.    if (request->calendar.digit_names != current->calendar.digit_names) {
  600.       if (StringTableNumber(request->calendar.digit_names) != MAX_DAYS_IN_MONTH) {
  601.          /*VARARGS*/
  602.          fprintf(stderr, "CalendarWidget: must specify 31 digit names\n");
  603.       } else {
  604.          new->calendar.digit_names = request->calendar.digit_names;
  605.          for (i=0; i < MAX_DAYS_IN_MONTH; i++) {
  606.             new->calendar.digit_name_lengths[i] 
  607.                = strlen(request->calendar.digit_names[i]);
  608.          }
  609.          redraw = True;
  610.          resize = True;
  611.       }
  612.    }
  613.  
  614.    /* Starting Weekday. */
  615.    new_starting_weekday = (request->calendar.starting_weekday 
  616.                            != current->calendar.starting_weekday);
  617.    if (new_starting_weekday 
  618.        && (request->calendar.starting_weekday < SUNDAY
  619.        || request->calendar.starting_weekday > SATURDAY)) {
  620.       /* Bogus value specified. */
  621.       /*VARARGS*/
  622.       fprintf(stderr, "CalendarWidget: invalid starting weekday, use [0..6]\n");
  623.       new_starting_weekday = FALSE;
  624.    }
  625.    if (new_starting_weekday && new_weekday_names) {
  626.       new->calendar.starting_weekday = request->calendar.starting_weekday;
  627.       rotate_weekdays(new);
  628.    } else if (new_weekday_names) {
  629.       rotate_weekdays(new);
  630.    } else if (new_starting_weekday) {
  631.       /* Rotate weekdays, taking into account current rotation. */
  632.       new->calendar.starting_weekday =
  633.          ( (7 - current->calendar.starting_weekday) 
  634.            + request->calendar.starting_weekday ) %7;
  635.       rotate_weekdays(new);
  636.       new->calendar.starting_weekday = request->calendar.starting_weekday;
  637.       redraw = True;
  638.    }
  639.  
  640.    /* Highlight. */
  641.    if (request->calendar.highlight != current->calendar.highlight) {
  642.       new->calendar.highlight = request->calendar.highlight;
  643.    }
  644.  
  645.    if (resize) {
  646.       compute_minimum_cell_size(new);
  647.       new_width =  WINDOW_WIDTH(new->calendar.min_cell_width);
  648.       new_height = WINDOW_HEIGHT(new->calendar.min_cell_height);
  649.       XtMakeResizeRequest(new, new_width, new_height, NULL, NULL);
  650.    }
  651.  
  652.    END("calendar_set_values_method");
  653.  
  654.    /* Return true if value change requires expose event to redraw. */
  655.    return (redraw);
  656. }
  657.  
  658.  
  659.  
  660.  
  661. static void calendar_resize_method(widget)
  662. CalendarWidget widget;
  663. {
  664.    int window_width = widget->core.width;
  665.    int window_height = widget->core.height;
  666.    float avail_cell_width  = (float) window_width  / (float) COLS;
  667.    float avail_cell_height = (float) window_height / (float) ROWS;
  668.  
  669.    BEGIN("calendar_resize_method");
  670.  
  671.    /* Now entering the foggy zone. */
  672.    widget->calendar.state = foggy;
  673.  
  674.    widget->calendar.real_cell_width = low_bound(
  675.       avail_cell_width, 
  676.       widget->calendar.min_cell_width
  677.    );
  678.  
  679.    widget->calendar.real_cell_height = low_bound(
  680.       avail_cell_height, 
  681.       widget->calendar.min_cell_height
  682.    );
  683.  
  684.    compute_grid          (widget);
  685.    compute_cell_geometry (widget);
  686.  
  687.    /* Don't need to redraw the calendar, an expose event follows. */
  688.    END("calendar_resize_method");
  689. }
  690.  
  691.  
  692.  
  693.  
  694. static XtGeometryResult calendar_query_geometry_method(widget, proposed, preferred)
  695. CalendarWidget widget;
  696. XtWidgetGeometry *proposed;
  697. XtWidgetGeometry *preferred;
  698. {
  699.    /* 
  700.     * There are three scenarios:
  701.     *
  702.     *   1. The geometry proposed by our parent is the same as our preferred
  703.     *      geometry.  Return XtGeometryYes to say, "Yes, I'm happy with your
  704.     *      proposed geometry so please resize me to it."
  705.     *
  706.     *   2. My preferred geometry is the same as our current geometry.  Return
  707.     *      XtGeometryNo to say, "I'm at my preferred geometry so please don't
  708.     *      resize me."
  709.     *
  710.     *   3. The geometry proposed by our parent is not our preferred geometry,
  711.     *      nor am I at my preferred geometry.  Return XtGeometryAlmost to say
  712.     *      "I'm sending you my preferred geometry.  Can't you try again to get
  713.     *      me the size I want?"
  714.     *
  715.     * Note that my parent is free to totally ignore this information, just like
  716.     * most parents ignore what their kids tell them.
  717.     */
  718.  
  719.    BEGIN("calendar_query_geometry_method");
  720.  
  721.    preferred->request_mode = CWWidth | CWHeight;
  722.    preferred->width        = WINDOW_WIDTH(widget->calendar.min_cell_width)   + 7;
  723.    preferred->height       = WINDOW_HEIGHT(widget->calendar.min_cell_height) + 8;
  724.  
  725.    if ( ((proposed->request_mode & (CWWidth | CWHeight)) == (CWWidth | CWHeight))
  726.          && ((proposed->width == preferred->width) 
  727.          && (proposed->height == preferred->height)) ) {
  728.       /* Yes, I like your proposal. */
  729.       return XtGeometryYes;
  730.    } else {
  731.       if ( (preferred->width  == widget->core.width) &&
  732.            (preferred->height == widget->core.height) ) {
  733.          /* Don't resize me!  I'm already at my preferred geometry. */
  734.          return XtGeometryNo;
  735.       } else {
  736.          /* Please try again. */
  737.          return XtGeometryAlmost;
  738.       }
  739.    }
  740.  
  741.    END("calendar_query_geometry_method");
  742. }
  743.  
  744.  
  745.  
  746. static void calendar_destroy_method(w)
  747. Widget w;
  748. {
  749.    free_line_GCs(w);
  750.    free_font_GCs_and_info(w);
  751. }
  752.  
  753.  
  754.  
  755. /*==========================================================================*/
  756. /*                           Action Definitions:                            */
  757. /*==========================================================================*/
  758. static void select_cell_action(widget, event)
  759. CalendarWidget widget;
  760. XButtonEvent *event;
  761. {
  762.    int window_width   = widget->core.width;
  763.    int window_height  = widget->core.height;
  764.    float cell_width   = widget->calendar.real_cell_width;
  765.    float cell_height  = widget->calendar.real_cell_height;
  766.    int current_x_cell = widget->calendar.current_x_cell;
  767.    int current_y_cell = widget->calendar.current_y_cell;
  768.    int x              = event->x;
  769.    int y              = event->y;
  770.    int new_y_cell     = y / cell_height;
  771.    int new_x_cell     = x / cell_width;
  772.    int cellnum;
  773.  
  774.    if (widget->calendar.state == foggy) {
  775.       return;
  776.    }
  777.  
  778.    /* Ignore events that occur outside the window.  These are motion */
  779.    /* events that started inside the window but were dragged outside. */
  780.    if ( (x<1) || (x>window_width-1) || (y<1) || (y>window_height-1) ) {
  781.       return;
  782.    }
  783.  
  784.    /* Ignore repeat events for the same cell. */
  785.    if (new_x_cell == current_x_cell && new_y_cell == current_y_cell) {
  786.       return;
  787.    }
  788.  
  789.    /* Ignore events in the month/year box. */
  790.    if (new_y_cell<2) {
  791.       return;
  792.    }
  793.  
  794.    /* Ignore events outide the cells that we care about for current month. */
  795.    cellnum = CELLXYtoCELLNUM(new_x_cell, new_y_cell);
  796.    if (cellnum < widget->calendar.month_start_cellnum ||
  797.        cellnum > widget->calendar.month_end_cellnum) {
  798.       return;
  799.    }
  800.  
  801.    widget->calendar.cell_selected = True;
  802.  
  803.    /* Reset the old selected cell. */
  804.    if (SYNC(widget)) {
  805.       toggle_highlight(widget, OFF);
  806.    }
  807.  
  808.    /* Highlight the newly selected cell. */
  809.    widget->calendar.highlight_date.year = widget->calendar.date.year;
  810.    widget->calendar.highlight_date.month = widget->calendar.date.month;
  811.    widget->calendar.highlight_date.day = CELLNUMtoDAYNUM(cellnum);
  812.    toggle_highlight(widget, ON);
  813.  
  814.    return;
  815. }
  816.  
  817.  
  818.  
  819. /*ARGSUSED*/
  820. static void default_button_up_action(widget, event)
  821. CalendarWidget widget;
  822. XButtonEvent *event;
  823. {
  824.    if (!widget->calendar.cell_selected) {
  825.       return;
  826.    }
  827.    widget->calendar.cell_selected = False;
  828.  
  829.    /* The button-down or button-motion event inverted the cell, so all     */
  830.    /* we have to do is call any callbacks that the application registered. */
  831.    XtCallCallbacks(widget, XtNcallback, &widget->calendar.highlight_date);
  832. }
  833.  
  834.  
  835.  
  836. /*==========================================================================*/
  837. /*                         Method Support Routines:                         */
  838. /*==========================================================================*/
  839. static void initialize_line_GCs(widget)
  840. CalendarWidget widget;
  841. {
  842.    XGCValues values;
  843.    XtGCMask mask;
  844.  
  845.    mask = GCForeground | GCBackground | GCLineWidth;
  846.    values.foreground  = widget->calendar.foreground;
  847.    values.background  = widget->calendar.background;
  848.    values.line_width  = widget->calendar.line_width;
  849.    widget->calendar.draw_gc = XtGetGC(widget, mask, &values);
  850.  
  851.    mask = GCForeground | GCBackground;
  852.    values.foreground  = widget->calendar.background;
  853.    values.background  = widget->calendar.foreground;
  854.    widget->calendar.undraw_gc = XtGetGC(widget, mask, &values);
  855.  
  856.    mask = GCForeground | GCBackground | GCFunction;
  857.    values.foreground  = widget->calendar.foreground;
  858.    values.background  = widget->calendar.background;
  859.    values.function    = GXinvert;
  860.    widget->calendar.invert_gc = XtGetGC(widget, mask, &values);
  861. }
  862.  
  863.  
  864.  
  865.  
  866. static void initialize_font_GCs_and_info(widget)
  867. CalendarWidget widget;
  868. {
  869.    XGCValues values;
  870.    XtGCMask mask;
  871.  
  872. /* Digit font */
  873.    mask = GCForeground | GCBackground | GCFont;
  874.    values.foreground  = widget->calendar.foreground;
  875.    values.background  = widget->calendar.background;
  876.    values.font        = widget->calendar.digit_font;
  877.    widget->calendar.digit_draw_gc = XtGetGC(widget, mask, &values);
  878.  
  879.    mask = GCForeground | GCBackground | GCFont;
  880.    values.foreground  = widget->calendar.background;
  881.    values.background  = widget->calendar.foreground;
  882.    values.font        = widget->calendar.digit_font;
  883.    widget->calendar.digit_undraw_gc = XtGetGC(widget, mask, &values);
  884.  
  885.    widget->calendar.digit_fsp = XQueryFont(
  886.       XtDisplay(widget),
  887.       XGContextFromGC(widget->calendar.digit_draw_gc)
  888.    );
  889.  
  890. /* Weekday font */
  891.    mask = GCForeground | GCBackground | GCFont;
  892.    values.foreground  = widget->calendar.foreground;
  893.    values.background  = widget->calendar.background;
  894.    values.font        = widget->calendar.weekday_font;
  895.    widget->calendar.weekday_draw_gc = XtGetGC(widget, mask, &values);
  896.  
  897.    mask = GCForeground | GCBackground | GCFont;
  898.    values.foreground  = widget->calendar.background;
  899.    values.background  = widget->calendar.foreground;
  900.    values.font        = widget->calendar.weekday_font;
  901.    widget->calendar.weekday_undraw_gc = XtGetGC(widget, mask, &values);
  902.  
  903.    widget->calendar.weekday_fsp = XQueryFont(
  904.       XtDisplay(widget),
  905.       XGContextFromGC(widget->calendar.weekday_draw_gc)
  906.    );
  907.  
  908. /* Title font */
  909.    mask = GCForeground | GCBackground | GCFont;
  910.    values.foreground  = widget->calendar.foreground;
  911.    values.background  = widget->calendar.background;
  912.    values.font        = widget->calendar.title_font;
  913.    widget->calendar.title_draw_gc = XtGetGC(widget, mask, &values);
  914.  
  915.    mask = GCForeground | GCBackground | GCFont;
  916.    values.foreground  = widget->calendar.background;
  917.    values.background  = widget->calendar.foreground;
  918.    values.font        = widget->calendar.title_font;
  919.    widget->calendar.title_undraw_gc = XtGetGC(widget, mask, &values);
  920.  
  921.    widget->calendar.title_fsp = XQueryFont(
  922.       XtDisplay(widget),
  923.       XGContextFromGC(widget->calendar.title_draw_gc)
  924.    );
  925.  
  926. /* Info font */
  927.    mask = GCForeground | GCBackground | GCFont;
  928.    values.foreground  = widget->calendar.foreground;
  929.    values.background  = widget->calendar.background;
  930.    values.font        = widget->calendar.info_font;
  931.    widget->calendar.info_draw_gc = XtGetGC(widget, mask, &values);
  932.  
  933.    mask = GCForeground | GCBackground | GCFont;
  934.    values.foreground  = widget->calendar.background;
  935.    values.background  = widget->calendar.foreground;
  936.    values.font        = widget->calendar.info_font;
  937.    widget->calendar.info_undraw_gc = XtGetGC(widget, mask, &values);
  938.  
  939.    widget->calendar.info_fsp = XQueryFont(
  940.       XtDisplay(widget),
  941.       XGContextFromGC(widget->calendar.info_draw_gc)
  942.    );
  943.  
  944. }
  945.  
  946.  
  947.  
  948. /* Compute the minimum allowable height and width of a cell based on */
  949. /* the specified fonts.  This is done during initialization and when */
  950. /* XtSetValues changes a font or string table.                       */
  951. static void compute_minimum_cell_size(new)
  952. CalendarWidget new;
  953. {
  954.    int max_day_digit_width   = 0;
  955.    int max_day_digit_height  = 0;
  956.    int max_day_name_width    = 0;
  957.    int max_day_name_height   = 0;
  958.    int max_month_name_width  = 0;
  959.    int max_year_width        = 0;
  960.    int max_title_width;
  961.    int max_title_height;
  962.    XFontStruct  * font;
  963.    char digit_string[4];
  964.    char * string;
  965.    int length;
  966.    int i;
  967.    int temp;
  968.  
  969.    /* WIDTH: */
  970.  
  971.    /* Compute the max width needed for all digits in the digit font. */
  972.    font = new->calendar.digit_fsp;
  973.    for (i=0; i < MAX_DAYS_IN_MONTH; i++) {
  974.       temp = 
  975.          XTextWidth(
  976.             font, 
  977.             new->calendar.digit_names[i],
  978.             new->calendar.digit_name_lengths[i]
  979.          );
  980.       max_day_digit_width = MAX(max_day_digit_width, temp);
  981.    }
  982.    /* Add in a space to avoid cramming. */
  983.    max_day_digit_width += XTextWidth(font, " ", 1);
  984.  
  985.    /* Compute the max width needed for all weekday names in the names font. */
  986.    font = new->calendar.weekday_fsp;
  987.    for (i=0; i < DAYS_IN_WEEK; i++) {
  988.       string = new->calendar.weekday_names[i];
  989.       length = new->calendar.weekday_name_lengths[i];
  990.       temp   = XTextWidth(font, string, length);
  991.       max_day_name_width = MAX(max_day_name_width, temp);
  992.    }
  993.    max_day_name_width += XTextWidth(font, " ", 1);
  994.  
  995.    /* Compute the max width needed for all month names in title font.  */
  996.    font = new->calendar.title_fsp;
  997.    for (i=0; i < MONTHS_IN_YEAR; i++) {
  998.       string = new->calendar.month_names[i];
  999.       length = new->calendar.month_name_lengths[i];
  1000.       temp   = XTextWidth(font, string, length);
  1001.       max_month_name_width = MAX(max_month_name_width, temp);
  1002.    }
  1003.  
  1004.    /* Compute the max width needed to hold any 5 digit year in title font. */
  1005.    digit_string[1] = NULL;
  1006.    for (i=0; i < 10; i++) {
  1007.       digit_string[0] = (char) ('0' + i);
  1008.       temp = XTextWidth(font, digit_string, 1);
  1009.       max_year_width = MAX(max_year_width, temp);
  1010.    }
  1011.    max_year_width *= 5;
  1012.  
  1013.    max_title_width
  1014.       = max_month_name_width 
  1015.       + max_year_width
  1016.       + XTextWidth(font, " ", 1) ;
  1017.  
  1018.    /* Divide this by the number of rows.  Add one for spacing. */
  1019.    max_title_width /= (ROWS + 1);
  1020.  
  1021.    /* Set the minimum cell width to the max width required above. */
  1022.    new->calendar.min_cell_width =
  1023.       (float) MAX3(max_day_digit_width,
  1024.                    max_day_name_width,
  1025.                    max_title_width);
  1026.  
  1027.    /* HEIGHT: */
  1028.    
  1029.    font = new->calendar.digit_fsp;
  1030.    max_day_digit_height = font->ascent + font->descent;
  1031.   
  1032.    font = new->calendar.weekday_fsp;
  1033.    max_day_name_height = font->ascent + font->descent;
  1034.  
  1035.    font = new->calendar.title_fsp;
  1036.    max_title_height = font->ascent + font->descent;
  1037.  
  1038.    /* Set the minimum cell height to the max height required above plus */
  1039.    /* whatever we need to for the line width and borders. */
  1040.    new->calendar.min_cell_height =
  1041.       (float) MAX3(max_day_digit_height, max_day_name_height, max_title_height)
  1042.       + 2 * INVERT_BORDER 
  1043.       + 2 * new->calendar.line_width
  1044.       + 2;
  1045. }
  1046.  
  1047.  
  1048.  
  1049. static void free_line_GCs(widget)
  1050. CalendarWidget widget;
  1051. {
  1052.    Display *display; 
  1053.  
  1054.    display = XtDisplay(widget);
  1055.  
  1056.    XFreeGC(display, widget->calendar.draw_gc);
  1057.    XFreeGC(display, widget->calendar.undraw_gc);
  1058.    XFreeGC(display, widget->calendar.invert_gc);
  1059. }
  1060.  
  1061.  
  1062.  
  1063.  
  1064. static void free_font_GCs_and_info(widget)
  1065. CalendarWidget widget;
  1066. {
  1067.    Display *display; 
  1068.  
  1069.    display = XtDisplay(widget);
  1070.  
  1071.    XFreeGC(display, widget->calendar.digit_draw_gc);
  1072.    XFreeGC(display, widget->calendar.digit_undraw_gc);
  1073.    XFreeFont(display, widget->calendar.digit_fsp);
  1074.  
  1075.    XFreeGC(display, widget->calendar.weekday_draw_gc);
  1076.    XFreeGC(display, widget->calendar.weekday_undraw_gc);
  1077.    XFreeFont(display, widget->calendar.weekday_fsp);
  1078.  
  1079.    XFreeGC(display, widget->calendar.title_draw_gc);
  1080.    XFreeGC(display, widget->calendar.title_undraw_gc);
  1081.    XFreeFont(display, widget->calendar.title_fsp);
  1082.  
  1083.    XFreeGC(display, widget->calendar.info_draw_gc);
  1084.    XFreeGC(display, widget->calendar.info_undraw_gc);
  1085.    XFreeFont(display, widget->calendar.info_fsp);
  1086. }
  1087.  
  1088.  
  1089. static void rotate_weekdays(widget)
  1090. CalendarWidget widget;
  1091. {
  1092.    int weekday;
  1093.    int i;
  1094.    char * save_names[DAYS_IN_WEEK];
  1095.    int save_lengths[DAYS_IN_WEEK];
  1096.  
  1097.    weekday = widget->calendar.starting_weekday;
  1098.    if (!weekday) {
  1099.       return;
  1100.    }
  1101.  
  1102.    /* Save a copy of the table. */
  1103.    for (i=0; i < DAYS_IN_WEEK; i++) {
  1104.       save_names[i] = widget->calendar.weekday_names[i];
  1105.       save_lengths[i] = widget->calendar.weekday_name_lengths[i];
  1106.    }
  1107.  
  1108.    /* Rotate the original. */
  1109.    for (i=0; i < DAYS_IN_WEEK; i++) {
  1110.       widget->calendar.weekday_names[i] = save_names[weekday];
  1111.       widget->calendar.weekday_name_lengths[i] = save_lengths[weekday];
  1112.       weekday++;
  1113.       if (weekday == DAYS_IN_WEEK) {
  1114.          weekday = 0;
  1115.       }
  1116.    }
  1117. }
  1118.  
  1119.  
  1120.  
  1121. /*
  1122.    For simplicity and convenience, the window is divided into a grid of 7 by 8
  1123.    rectangles.  However, vertical lines are not drawn in the top 2 rows of boxes
  1124.    because that's where the month/year text and day-of-week names will go.
  1125. */
  1126. static void compute_grid(widget)
  1127. CalendarWidget widget;
  1128. {
  1129.    int window_width      = widget->core.width;
  1130.    int window_height     = widget->core.height;
  1131.    float cell_width      = widget->calendar.real_cell_width;
  1132.    float cell_height     = widget->calendar.real_cell_height;
  1133.    float min_cell_width  = widget->calendar.min_cell_width;
  1134.    float min_cell_height = widget->calendar.min_cell_height;
  1135.    int bottom_showing    = (window_height >= ROWS * min_cell_height);
  1136.    int right_showing     = (window_width >= COLS * min_cell_width);
  1137.    int line_width        = widget->calendar.line_width;
  1138.    int fudge             = line_width % 2;
  1139.    int seg;
  1140.    int i;
  1141.  
  1142.    /* Compute horizontal segments. */
  1143.    seg = 0;
  1144.    for (i=2; i < HORIZ_SEGMENTS+2; i++, seg++) {
  1145.       /* Starting point. */
  1146.       widget->calendar.segments[seg].x1 = (short) (0);
  1147.       widget->calendar.segments[seg].y1 = (short) (cell_height * i);
  1148.  
  1149.       /* Ending point. */
  1150.       widget->calendar.segments[seg].x2 = (short) (window_width);
  1151.       widget->calendar.segments[seg].y2 = (short) (cell_height * i);
  1152.    }
  1153.  
  1154.    /* Compute vertical segments. */
  1155.    for (i=1; i < VERTI_SEGMENTS+1; i++, seg++) {
  1156.       /* Starting point. */
  1157.       widget->calendar.segments[seg].x1 = (short) (cell_width * i);
  1158.       widget->calendar.segments[seg].y1 = (short) (cell_height * 2);
  1159.  
  1160.       /* Ending point. */
  1161.       widget->calendar.segments[seg].x2 = (short) (cell_width * i);
  1162.       widget->calendar.segments[seg].y2 = (short) (window_height);
  1163.    }
  1164.  
  1165.    /* Draw a border around the whole grid.  I could do this with an  */
  1166.    /* XRectangle, but since I'm calling XDrawSegments, it's one less */
  1167.    /* function call to do it this way (could still be slower though) */
  1168.  
  1169.    /* draw solid top */
  1170.    widget->calendar.segments[seg].x1 = (short) (0);
  1171.    widget->calendar.segments[seg].y1 = (short) (0);
  1172.    widget->calendar.segments[seg].x2 = (short) (window_width);
  1173.    widget->calendar.segments[seg].y2 = (short) (0);
  1174.  
  1175.    /* draw solid left */
  1176.    seg++;
  1177.    widget->calendar.segments[seg].x1 = (short) (0);
  1178.    widget->calendar.segments[seg].y1 = (short) (0);
  1179.    widget->calendar.segments[seg].x2 = (short) (0);
  1180.    widget->calendar.segments[seg].y2 = (short) (window_height);
  1181.  
  1182.    /* draw solid bottom */
  1183.    if (bottom_showing) {
  1184.       seg++;
  1185.       widget->calendar.segments[seg].x1 = (short) (0);
  1186.       widget->calendar.segments[seg].y1 = (short) (window_height - fudge);
  1187.       widget->calendar.segments[seg].x2 = (short) (window_width);
  1188.       widget->calendar.segments[seg].y2 = (short) (window_height - fudge);
  1189.    }
  1190.  
  1191.    /* draw solid right */
  1192.    if (right_showing) {
  1193.       seg++;
  1194.       widget->calendar.segments[seg].x1 = (short) (window_width - fudge);
  1195.       widget->calendar.segments[seg].y1 = (short) (0);
  1196.       widget->calendar.segments[seg].x2 = (short) (window_width - fudge);
  1197.       widget->calendar.segments[seg].y2 = (short) (window_height);
  1198.    }
  1199.  
  1200.    widget->calendar.number_segments = seg+1;
  1201. }
  1202.  
  1203.  
  1204.  
  1205.    
  1206. static void draw_grid(widget)
  1207. CalendarWidget widget;
  1208. {
  1209.    if (widget->calendar.state == foggy) {
  1210.       return;
  1211.    }
  1212.  
  1213.    /* Draw the grid. */
  1214.    XDrawSegments(
  1215.       XtDisplay(widget),
  1216.       XtWindow(widget),
  1217.       widget->calendar.draw_gc,
  1218.       widget->calendar.segments,
  1219.       widget->calendar.number_segments
  1220.    );
  1221. }
  1222.  
  1223.  
  1224.  
  1225.  
  1226. static void compute_cell_geometry(widget)
  1227. CalendarWidget widget;
  1228. {
  1229.    int window_width      = widget->core.width;
  1230.    int window_height     = widget->core.height;
  1231.    int old_window_width  = widget->calendar.old_window_width;
  1232.    int old_window_height = widget->calendar.old_window_height;
  1233.    float cell_width      = widget->calendar.real_cell_width;
  1234.    float cell_height     = widget->calendar.real_cell_height;
  1235.    int line_width        = widget->calendar.line_width;
  1236.    int line_factor       = line_width / 2;
  1237.    int fudge             = line_width % 2;
  1238.    int temp_x;
  1239.    int temp_y;
  1240.    int x,y;
  1241.  
  1242.    /* If the window size has changed, re-compute the cell_geometry array.  */
  1243.    /* This takes a little while (lots of float arithmetic) but that's okay */
  1244.    /* because it only happens on resize, and we should punish the user.    */
  1245.  
  1246.    if (old_window_width != window_width || old_window_height != window_height) {
  1247.  
  1248.       /* Compute geometries for cells not along top, right or bottom edge. */
  1249.       for (x=0; x < COLS-1; x++) {
  1250.          for (y=1; y < ROWS-1; y++) {
  1251.             GEOMETRY(widget, x, y).x = temp_x
  1252.                = (short) (x * cell_width + line_factor + fudge + INVERT_BORDER);
  1253.             GEOMETRY(widget, x, y).y = temp_y
  1254.                = (short) (y * cell_height + line_factor + fudge + INVERT_BORDER);
  1255.             GEOMETRY(widget, x, y).width
  1256.                = (unsigned short) ((x+1) * cell_width - line_factor - INVERT_BORDER - temp_x);
  1257.             GEOMETRY(widget, x, y).height
  1258.                = (unsigned short) ((y+1) * cell_height - line_factor - INVERT_BORDER - temp_y);
  1259.          }
  1260.       }
  1261.  
  1262.       /* Compute geometries for cells along right edge. */
  1263.       x = COLS-1;
  1264.       for (y=0; y < ROWS-1; y++) {
  1265.          GEOMETRY(widget, x, y).x = temp_x
  1266.             = (short) (x * cell_width + line_factor + fudge + INVERT_BORDER);
  1267.          GEOMETRY(widget, x, y).y = temp_y
  1268.             = (short) (y * cell_height + line_factor + fudge + INVERT_BORDER);
  1269.          GEOMETRY(widget, x, y).width
  1270.             = (unsigned short) (COLS * cell_width - line_factor - INVERT_BORDER - temp_x - fudge);
  1271.          GEOMETRY(widget, x, y).height
  1272.             = (unsigned short) ((y+1) * cell_height - line_factor - INVERT_BORDER - temp_y);
  1273.       }
  1274.  
  1275.       /* Compute geometries for cells along bottom edge. */
  1276.       /* For a valid calendar, at most the first two cells in the bottom */
  1277.       /* row will be used. */
  1278.       y = ROWS-1;
  1279.       for (x=0; x < 3; x++) {
  1280.          GEOMETRY(widget, x, y).x = temp_x
  1281.             = (short) (x * cell_width + line_factor + fudge + INVERT_BORDER);
  1282.          GEOMETRY(widget, x, y).y = temp_y
  1283.             = (short) (y * cell_height + line_factor + fudge + INVERT_BORDER);
  1284.          GEOMETRY(widget, x, y).width
  1285.             = (unsigned short) ((x+1) * cell_width - line_factor - INVERT_BORDER - temp_x);
  1286.          GEOMETRY(widget, x, y).height
  1287.             = (unsigned short) (ROWS * cell_height - line_factor - INVERT_BORDER - temp_y - fudge);
  1288.       }
  1289.  
  1290.       /* Finally, compute the available rectangle for centering the title. */
  1291.       widget->calendar.title_geometry.x = temp_x 
  1292.          = (short) (line_factor + fudge + INVERT_BORDER);
  1293.       widget->calendar.title_geometry.y = temp_y 
  1294.          = (short) (line_factor + fudge + INVERT_BORDER);
  1295.       widget->calendar.title_geometry.width
  1296.          = (unsigned short) (COLS * cell_width - line_factor - INVERT_BORDER - temp_x - fudge);
  1297.       widget->calendar.title_geometry.height
  1298.          = (unsigned short) (cell_height - line_factor - INVERT_BORDER - temp_y);
  1299.  
  1300.       /* Factor in the day names font size for a better appearance. */
  1301.       widget->calendar.title_geometry.height
  1302.          = (unsigned short) (widget->calendar.title_geometry.height
  1303.                              + cell_height
  1304.                              - widget->calendar.weekday_fsp->ascent 
  1305.                              - widget->calendar.weekday_fsp->descent 
  1306.                              - 1);
  1307.  
  1308.       widget->calendar.old_window_height = window_height;
  1309.       widget->calendar.old_window_width  = window_width;
  1310.    }
  1311. }
  1312.  
  1313.  
  1314.  
  1315.  
  1316. static void draw_weekdays(widget)
  1317. CalendarWidget widget;
  1318. {
  1319.    if (widget->calendar.state == foggy) {
  1320.       return;
  1321.    }
  1322.  
  1323.    DrawStringsInRects(
  1324.       XtDisplay(widget), 
  1325.       XtWindow(widget),
  1326.       widget->calendar.weekday_draw_gc,
  1327.       widget->calendar.weekday_fsp, 
  1328.       widget->calendar.weekday_names, 
  1329.       widget->calendar.weekday_name_lengths,
  1330.       &GEOMETRY(widget, 0, 1),
  1331.       DAYS_IN_WEEK,
  1332.       South,
  1333.       1,0
  1334.    );
  1335. }
  1336.  
  1337.  
  1338.  
  1339. static void draw_digits(widget)
  1340. CalendarWidget widget;
  1341. {
  1342.    int x,y;
  1343.    int start = widget->calendar.month_start_cellnum;
  1344.    int end   = widget->calendar.month_end_cellnum;
  1345.  
  1346.    if (widget->calendar.state == foggy) {
  1347.       return;
  1348.    }
  1349.  
  1350.    CELLNUMtoCELLXY(start,x,y);
  1351.  
  1352.    DrawStringsInRects(
  1353.       XtDisplay(widget), 
  1354.       XtWindow(widget),
  1355.       widget->calendar.digit_draw_gc,
  1356.       widget->calendar.digit_fsp, 
  1357.       widget->calendar.digit_names,
  1358.       widget->calendar.digit_name_lengths,
  1359.       &GEOMETRY(widget, x, y),
  1360.       end - start + 1,
  1361.       widget->calendar.digit_gravity,
  1362.       1,0
  1363.    );
  1364. }
  1365.  
  1366.  
  1367.  
  1368. static void draw_title(widget)
  1369. CalendarWidget widget;
  1370. {
  1371.    if (widget->calendar.state == foggy) {
  1372.       return;
  1373.    }
  1374.  
  1375.    DrawStringInRect(
  1376.       XtDisplay(widget), 
  1377.       XtWindow(widget),
  1378.       widget->calendar.title_draw_gc,
  1379.       widget->calendar.title_fsp, 
  1380.       widget->calendar.title_string,
  1381.       widget->calendar.title_string_length,
  1382.       &widget->calendar.title_geometry,
  1383.       Center,
  1384.       1,0
  1385.    );
  1386. }
  1387.  
  1388.  
  1389.  
  1390.  
  1391.  
  1392. /* Routine to fill in the widget->calendar.month_starting_weekdays[] and  */
  1393. /* the widget->calendar.days_in_february field, given a certain date.     */
  1394. /* The data is only computed when the widget's current_year has changed.  */
  1395.  
  1396. static void compute_month_data(widget)
  1397. CalendarWidget widget;
  1398. {
  1399.    int month = widget->calendar.date.month - 1;
  1400.    int year  = widget->calendar.date.year;
  1401.    int leap_days;
  1402.    int total_days;
  1403.    int weekday;
  1404.    int i;
  1405.  
  1406.    if (widget->calendar.current_year != year) {
  1407.       /* Compute data for the whole year while we're at it. */
  1408.       leap_days = 1;            /* Year 0 was a leap year. */
  1409.       leap_days += year/4;      /* Add all years divisible by 4. */
  1410.       leap_days -= year/100;    /* Subtract all century years. */
  1411.       leap_days += year/400;    /* Add back century years divisible by 400. */
  1412.  
  1413.       if (A_LEAP_YEAR(year)) {
  1414.          leap_days--;
  1415.          widget->calendar.days_in_february = 29;
  1416.       } else {
  1417.          widget->calendar.days_in_february = 28;
  1418.       }
  1419.  
  1420.       /* Compute days elapsed to beginning of the year. */
  1421.       total_days = (year*365) + leap_days;
  1422.  
  1423.       /* Compute the starting weekday for january in this year. */
  1424.       weekday = DAYStoWEEKDAY(total_days);
  1425.       weekday = ROTATE(widget, weekday);
  1426.       widget->calendar.month_starting_weekdays[JANUARY] = weekday;
  1427.  
  1428.       /* Compute the starting weekday for february in this year. */
  1429.       total_days += days_in_month[JANUARY];
  1430.       weekday = DAYStoWEEKDAY(total_days);
  1431.       weekday = ROTATE(widget, weekday);
  1432.       widget->calendar.month_starting_weekdays[FEBRUARY] = weekday;
  1433.  
  1434.       /* Compute the starting weekday for the rest of the months. */
  1435.       total_days += widget->calendar.days_in_february;
  1436.       for (i=MARCH; i <= DECEMBER; i++) {
  1437.          weekday = DAYStoWEEKDAY(total_days);
  1438.          weekday = ROTATE(widget, weekday);
  1439.          widget->calendar.month_starting_weekdays[i] = weekday;
  1440.          total_days += days_in_month[i];
  1441.       }
  1442.       widget->calendar.current_year = year;
  1443.  
  1444.       /* Compute the new year string. */
  1445.       if (widget->calendar.show_year) {
  1446.          sprintf(widget->calendar.year_string, "%d" ,widget->calendar.date.year);
  1447.          widget->calendar.year_string_length = strlen(widget->calendar.year_string);
  1448.       } else {
  1449.          strcpy(widget->calendar.year_string, "");
  1450.          widget->calendar.year_string_length = 0;
  1451.       }
  1452.    }
  1453.  
  1454.    widget->calendar.month_start_cellnum 
  1455.       = widget->calendar.month_starting_weekdays[month];
  1456.  
  1457.    widget->calendar.month_end_cellnum
  1458.       = widget->calendar.month_start_cellnum 
  1459.       + DAYS_IN_MONTH(widget) - 1;
  1460. }
  1461.  
  1462.  
  1463.  
  1464.  
  1465.  
  1466.  
  1467. static void compute_title_string(widget)
  1468. CalendarWidget widget;
  1469. {
  1470.    int month = widget->calendar.date.month - 1;
  1471.  
  1472.    /* Compute the title string length. */
  1473.    widget->calendar.title_string_length = widget->calendar.month_name_lengths[month];
  1474.  
  1475.    /* Fill in the title string. */
  1476.    strcpy(widget->calendar.title_string, widget->calendar.month_names[month]);
  1477.  
  1478.    if (widget->calendar.show_year) {
  1479.       strcat(widget->calendar.title_string, " ");
  1480.       widget->calendar.title_string_length += 1;
  1481.  
  1482.       strcat(widget->calendar.title_string, widget->calendar.year_string);
  1483.       widget->calendar.title_string_length += widget->calendar.year_string_length;
  1484.    }
  1485. }
  1486.  
  1487.  
  1488.  
  1489.  
  1490.  
  1491.  
  1492. static void clear_calendar(widget)
  1493. CalendarWidget widget;
  1494. {
  1495.    int month_start_cellnum = widget->calendar.month_start_cellnum;
  1496.    int month_end_cellnum   = widget->calendar.month_end_cellnum;
  1497.    Display * display;
  1498.    Window window;
  1499.    int x,y;
  1500.  
  1501.    if (widget->calendar.state == foggy) {
  1502.       return;
  1503.    }
  1504.    BEGIN("clear_calendar");
  1505.  
  1506.    display = XtDisplay(widget);
  1507.    window  = XtWindow(widget);
  1508.  
  1509.    CELLNUMtoCELLXY(month_start_cellnum, x, y);
  1510.    
  1511.    /* Erase the digits. */
  1512.    XFillRectangles(
  1513.       display,
  1514.       window,
  1515.       widget->calendar.undraw_gc,
  1516.       &GEOMETRY(widget,x,y),
  1517.       month_end_cellnum - month_start_cellnum + 1
  1518.    );
  1519.  
  1520.    /* Erase the title box.  */
  1521.    XFillRectangles(
  1522.       display,
  1523.       window,
  1524.       widget->calendar.undraw_gc,
  1525.       &widget->calendar.title_geometry,
  1526.       1
  1527.    );
  1528.  
  1529.    END("clear_calendar");
  1530. }
  1531.  
  1532.  
  1533.  
  1534.  
  1535. static void calendar_update(widget)
  1536. CalendarWidget widget;
  1537. {
  1538.  
  1539.    BEGIN("calendar_update");
  1540.  
  1541.    clear_calendar       (widget);
  1542.    compute_month_data   (widget);
  1543.    compute_title_string (widget);
  1544.    draw_title           (widget);
  1545.    draw_digits          (widget);
  1546.  
  1547.    if (SYNC(widget)) {
  1548.       toggle_highlight(widget, ON);
  1549.    } else {
  1550.       widget->calendar.current_x_cell = 0;
  1551.       widget->calendar.current_y_cell = 0;
  1552.    }
  1553.  
  1554.    END("calendar_update");
  1555. }
  1556.  
  1557.  
  1558.  
  1559. static void toggle_highlight(widget, state)
  1560. CalendarWidget widget;
  1561. int state;
  1562. {
  1563.    int current_x_cell;
  1564.    int current_y_cell;
  1565.    int day;
  1566.    Display * display;
  1567.    Window window;
  1568.    GC draw_gc;
  1569.    GC undraw_gc;
  1570.  
  1571.    if (widget->calendar.state == foggy) {
  1572.       return;
  1573.    }
  1574.    BEGIN("toggle_highlight");
  1575.  
  1576.    WIDGETtoCELLXY(
  1577.       widget,
  1578.       current_x_cell,
  1579.       current_y_cell
  1580.    );
  1581.  
  1582.    day = widget->calendar.highlight_date.day - 1;
  1583.  
  1584.    if (current_y_cell && widget->calendar.highlight) {
  1585.  
  1586.       display = XtDisplay(widget);
  1587.       window  = XtWindow(widget);
  1588.  
  1589.       if (state == ON) {
  1590.          draw_gc = widget->calendar.draw_gc;
  1591.          undraw_gc = widget->calendar.digit_undraw_gc;
  1592.       } else {
  1593.          draw_gc = widget->calendar.undraw_gc;
  1594.          undraw_gc = widget->calendar.digit_draw_gc;
  1595.       }
  1596.  
  1597.       XFillRectangles(
  1598.          display,
  1599.          window,
  1600.          draw_gc,
  1601.          &GEOMETRY(widget, current_x_cell, current_y_cell),
  1602.          1
  1603.       );
  1604.  
  1605.       DrawStringInRect(
  1606.          display, 
  1607.          window,
  1608.          undraw_gc,
  1609.          widget->calendar.digit_fsp, 
  1610.          widget->calendar.digit_names[day], 
  1611.          widget->calendar.digit_name_lengths[day],
  1612.          &GEOMETRY(widget, current_x_cell, current_y_cell),
  1613.          widget->calendar.digit_gravity,
  1614.          1,0
  1615.       );
  1616.  
  1617.       /* Eventually this will need to draw the day info as well. */
  1618.    }
  1619.  
  1620.    if (state == OFF) {
  1621.       widget->calendar.current_x_cell = 0;
  1622.       widget->calendar.current_y_cell = 0;
  1623.    } else {
  1624.       widget->calendar.current_x_cell = current_x_cell;
  1625.       widget->calendar.current_y_cell = current_y_cell;
  1626.    }
  1627.  
  1628.    END("toggle_highlight");
  1629. }
  1630.  
  1631.  
  1632.  
  1633.  
  1634.  
  1635. /*==========================================================================*/
  1636. /*                             Public Functions:                            */
  1637. /*==========================================================================*/
  1638. void CalendarIncMonth(widget)
  1639. CalendarWidget widget;
  1640. {
  1641.    if (widget->calendar.date.month == DECEMBER+1) {
  1642.       widget->calendar.date.month = JANUARY+1;
  1643.       widget->calendar.date.year++;
  1644.    } else {
  1645.       widget->calendar.date.month++;
  1646.    }
  1647.    calendar_update(widget);
  1648. }
  1649.  
  1650.  
  1651.  
  1652. void CalendarDecMonth(widget)
  1653. CalendarWidget widget;
  1654. {
  1655.    if (widget->calendar.date.month == JANUARY+1) {
  1656.       widget->calendar.date.month = DECEMBER+1;
  1657.       widget->calendar.date.year--;
  1658.    } else {
  1659.       widget->calendar.date.month--;
  1660.    }
  1661.    calendar_update(widget);
  1662. }
  1663.  
  1664.  
  1665. void CalendarIncYear(widget)
  1666. CalendarWidget widget;
  1667. {
  1668.    widget->calendar.date.year++;
  1669.    calendar_update(widget);
  1670. }
  1671.  
  1672.  
  1673.  
  1674. void CalendarDecYear(widget)
  1675. CalendarWidget widget;
  1676. {
  1677.    widget->calendar.date.year--;
  1678.    calendar_update(widget);
  1679. }
  1680.  
  1681.  
  1682.  
  1683.  
  1684. void CalendarIncDay(widget, show)
  1685. CalendarWidget widget;
  1686. Boolean show;
  1687. {
  1688.    if (!widget->calendar.highlight_date.day) {
  1689.       return;
  1690.    }
  1691.    BEGIN("CalendarIncDay");
  1692.  
  1693.    if (SYNC(widget)) {
  1694.       toggle_highlight(widget, OFF);
  1695.    }
  1696.  
  1697.    if (widget->calendar.highlight_date.day == DAYS_IN_HIGHLIGHT_MONTH(widget)) {
  1698.       /* We've rolled over a month. */ 
  1699.       widget->calendar.highlight_date.day = 1;
  1700.  
  1701.       if (widget->calendar.highlight_date.month == DECEMBER+1) {
  1702.          widget->calendar.highlight_date.month = JANUARY+1;
  1703.          widget->calendar.highlight_date.year++;
  1704.  
  1705.          /* We've rolled over a year, get new days in feb. */
  1706.          widget->calendar.highlight_days_in_february 
  1707.             = 28 + A_LEAP_YEAR(widget->calendar.highlight_date.year);
  1708.  
  1709.       } else {
  1710.          widget->calendar.highlight_date.month++;
  1711.       }
  1712.  
  1713.       if (show) {
  1714.          CalendarShowMonth(widget, widget->calendar.highlight_date);
  1715.       }
  1716.  
  1717.    } else {
  1718.       widget->calendar.highlight_date.day++;
  1719.    }
  1720.  
  1721.    if (SYNC(widget)) {
  1722.       toggle_highlight(widget, ON);
  1723.    }
  1724.  
  1725.    END("CalendarIncDay");
  1726. }
  1727.  
  1728.  
  1729.  
  1730.  
  1731.  
  1732. void CalendarDecDay(widget, show)
  1733. CalendarWidget widget;
  1734. Boolean show;
  1735. {
  1736.    if (!widget->calendar.highlight_date.day) {
  1737.       return;
  1738.    }
  1739.    BEGIN("CalendarDecDay");
  1740.  
  1741.    if (SYNC(widget)) {
  1742.       toggle_highlight(widget, OFF);
  1743.    }
  1744.  
  1745.    if (widget->calendar.highlight_date.day == 1) {
  1746.       /* We've rolled over a month. */
  1747.  
  1748.       if (widget->calendar.highlight_date.month == JANUARY+1) {
  1749.          widget->calendar.highlight_date.month = DECEMBER+1;
  1750.          widget->calendar.highlight_date.year--;
  1751.          widget->calendar.highlight_date.day = 31;
  1752.  
  1753.          /* We've rolled over a year, get new days in feb. */
  1754.          widget->calendar.highlight_days_in_february 
  1755.             = 28 + A_LEAP_YEAR(widget->calendar.highlight_date.year);
  1756.  
  1757.       } else {
  1758.          widget->calendar.highlight_date.month--;
  1759.          widget->calendar.highlight_date.day = DAYS_IN_HIGHLIGHT_MONTH(widget);
  1760.       }
  1761.  
  1762.       if (show) {
  1763.          CalendarShowMonth(widget, widget->calendar.highlight_date);
  1764.       }
  1765.  
  1766.    } else {
  1767.       widget->calendar.highlight_date.day--;
  1768.    }
  1769.  
  1770.    if (SYNC(widget)) {
  1771.       toggle_highlight(widget, ON);
  1772.    }
  1773.  
  1774.    END("CalendarDecDay");
  1775. }
  1776.  
  1777.  
  1778.  
  1779.  
  1780.  
  1781. void CalendarSetDate(widget, date)
  1782. CalendarWidget widget;
  1783. Date date;
  1784. {
  1785.    if (!is_valid_date(&date)) {
  1786.       return;
  1787.    }
  1788.    BEGIN("CalendarSetDate");
  1789.  
  1790.    if (SYNC(widget)) {
  1791.       /* Reset any already highlighted date. */
  1792.       toggle_highlight(widget, OFF);
  1793.    }
  1794.  
  1795.    if (widget->calendar.date.month == date.month &&
  1796.        widget->calendar.date.year  == date.year) {
  1797.       /* The date specified is on the calendar currently being shown. */
  1798.  
  1799.       /* Highlight the specified date. */
  1800.       widget->calendar.highlight_date = date;
  1801.       toggle_highlight(widget, ON);
  1802.    } else {
  1803.       /* The date specified is not on the current calendar. */
  1804.  
  1805.       if (date.year != widget->calendar.highlight_date.year) {
  1806.          widget->calendar.highlight_days_in_february
  1807.             = 28 + A_LEAP_YEAR(date.year);
  1808.       }
  1809.       widget->calendar.highlight_date = date;
  1810.    }
  1811.  
  1812.    END("CalendarSetDate");
  1813. }
  1814.  
  1815.  
  1816.  
  1817.  
  1818. void CalendarShowMonth(widget, date)
  1819. CalendarWidget widget;
  1820. Date date;
  1821. {
  1822.    date.day = 1;
  1823.    if (!is_valid_date(&date)) {
  1824.       return;
  1825.    }
  1826.    BEGIN("CalendarShowMonth");
  1827.  
  1828.    if (widget->calendar.date.month == date.month
  1829.        && widget->calendar.date.year == date.year) {
  1830.       /* The corresponding calendar is currently being displayed. */
  1831.    } else {
  1832.       /* The corresponding calendar is not currently being displayed. */
  1833.       widget->calendar.date.month = date.month;
  1834.       widget->calendar.date.year  = date.year;
  1835.       calendar_update(widget);
  1836.    }
  1837.  
  1838.    END("CalendarShowMonth");
  1839. }
  1840.  
  1841.  
  1842.  
  1843. void CalendarGetDate(widget, date_ptr)
  1844. CalendarWidget widget;
  1845. Date * date_ptr;
  1846. {
  1847.    *date_ptr = widget->calendar.highlight_date;
  1848. }
  1849.  
  1850.  
  1851.  
  1852. Date DateConverter(widget, string)
  1853. CalendarWidget widget;
  1854. char * string;
  1855. {
  1856.    /* Call the real converter.  This just allows us to hide details. */
  1857.    return (convert_string_to_date(widget->calendar.month_names, string));
  1858. }
  1859.  
  1860.  
  1861.  
  1862.  
  1863. /* Returns a Date structure corresponding to the current date. */
  1864. /* Most applications are going to need it so I'll just provide it. */
  1865. Date GetTodaysDate()
  1866. {
  1867.    Date date;
  1868.    time_t current_time = time(NULL);
  1869.    struct tm * current_tm = localtime(¤t_time);
  1870.    date.month = current_tm->tm_mon + 1;
  1871.    date.day   = current_tm->tm_mday;
  1872.    date.year  = current_tm->tm_year + 1900;
  1873.    return date;
  1874. }
  1875.  
  1876.  
  1877.  
  1878.  
  1879. /* Return a string like "Thursday October 15, 1990" */
  1880. char * CalendarPrettyDate(widget)
  1881. CalendarWidget widget;
  1882. {
  1883.    static char buffer[MAX_PRETTY_DATE_STRING_LENGTH];
  1884.    Date highlight_date;
  1885.    Date showing_date;
  1886.    int weekday;
  1887.    int month;
  1888.  
  1889.    highlight_date = widget->calendar.highlight_date;
  1890.    showing_date = widget->calendar.date;
  1891.    
  1892.    if (showing_date.year == highlight_date.year) {
  1893.       month = highlight_date.month - 1;
  1894.       weekday = widget->calendar.month_starting_weekdays[month];
  1895.       weekday = (weekday + highlight_date.day - 1) % 7;
  1896.    } else {
  1897.       /* No info available, so resort to brute force. */
  1898.       weekday = compute_weekday(highlight_date);
  1899.       weekday = ROTATE(widget, weekday);
  1900.    }
  1901.  
  1902.    /*VARARGS*/
  1903.    sprintf(
  1904.       buffer,
  1905.       "%s %s %d, %d",
  1906.       widget->calendar.weekday_names[weekday],
  1907.       widget->calendar.month_names[highlight_date.month-1],
  1908.       highlight_date.day,
  1909.       highlight_date.year
  1910.    );
  1911.  
  1912.    return buffer;
  1913. }
  1914.  
  1915.  
  1916.  
  1917. static int compute_weekday(date)
  1918. Date date;
  1919. {
  1920.    int leap_days;
  1921.    int total_days;
  1922.    int month;
  1923.    int days_in_february;
  1924.    
  1925.    leap_days = 1;                 /* Year 0 was a leap year. */
  1926.    leap_days += date.year/4;      /* Add all years divisible by 4. */
  1927.    leap_days -= date.year/100;    /* Subtract all century years. */
  1928.    leap_days += date.year/400;    /* Add back century years divisible by 400. */
  1929.    
  1930.    if (A_LEAP_YEAR(date.year)) {
  1931.       leap_days--;
  1932.       days_in_february = 29;
  1933.    } else {
  1934.       days_in_february = 28;
  1935.    }
  1936.  
  1937.    total_days = (date.year*365) + leap_days;
  1938.  
  1939.    for (month=0; month < date.month - 1; month ++) {
  1940.       if (month == FEBRUARY) {
  1941.          total_days += days_in_february;
  1942.       } else {
  1943.          total_days += days_in_month[month];
  1944.       }
  1945.    }
  1946.  
  1947.    total_days += date.day - 1;
  1948.  
  1949.    return DAYStoWEEKDAY(total_days);
  1950. }
  1951.