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
Wrap
C/C++ Source or Header
|
1990-12-28
|
58KB
|
1,951 lines
/*
* Author: Jason Baietto, jason@ssd.csd.harris.com
* xdiary Copyright 1990 Harris Corporation
*
* Permission to use, copy, modify, and distribute, this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holder be used in
* advertising or publicity pertaining to distribution of the software with
* specific, written prior permission, and that no fee is charged for further
* distribution of this software, or any modifications thereof. The copyright
* holder makes no representations about the suitability of this software for
* any purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND IN NO
* EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM ITS USE,
* LOSS OF DATA, PROFITS, QPA OR GPA, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
* THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*==========================================================================*/
/* Header Files: */
/*==========================================================================*/
#include <stdio.h>
#include <X11/Xos.h>
#include <X11/StringDefs.h>
#include <X11/IntrinsicP.h>
#include <X11/Xmu/Converters.h>
#include "CalendarP.h"
#ifdef DEBUG
#define BEGIN(str) (/*VARARGS0*/fprintf(stderr, "%s()\n", (str)))
#define END(str) ;
#else
#define BEGIN(str) ;
#define END(str) ;
#endif
/*==========================================================================*/
/* Forward References: */
/*==========================================================================*/
static void initialize_line_GCs();
static void initialize_font_GCs_and_info();
static void compute_minimum_cell_size();
static void compute_cell_geometry();
static void free_line_GCs();
static void free_font_GCs_and_info();
static void compute_grid();
static void draw_grid();
static void draw_weekdays();
static void draw_title();
static void draw_digits();
static void compute_title_string();
static void toggle_highlight();
static void rotate_weekdays();
static void calendar_update();
static void compute_month_data();
/*==========================================================================*/
/* Static Global Data: */
/*==========================================================================*/
static int days_in_month[] =
/* jan feb mar apr may jun jul aug sep oct nov dec */
{ 31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
/*==========================================================================*/
/* Resource List: */
/*==========================================================================*/
#define offset(field) XtOffset(CalendarWidget, field)
static XtResource calendar_resources[] = {
{
XtNcallback,
XtCCallback,
XtRCallback,
sizeof(XtPointer),
offset(calendar.callback),
XtRCallback,
NULL
},
{
XtNlineWidth,
XtCLineWidth,
XtRInt,
sizeof(int),
offset(calendar.line_width),
XtRImmediate,
(XtPointer)DEFAULT_LINE_WIDTH
},
{
XtNforeground,
XtCForeground,
XtRPixel,
sizeof(Pixel),
offset(calendar.foreground),
XtRString,
XtDefaultForeground
},
{
XtNbackground,
XtCBackground,
XtRPixel,
sizeof(Pixel),
offset(calendar.background),
XtRString,
XtDefaultBackground
},
{
XtNdigitFont,
XtCCalendarFont,
XtRFont,
sizeof(Font),
offset(calendar.digit_font),
XtRString,
CALENDAR_DEFAULT_FONT
},
{
XtNweekdayFont,
XtCCalendarFont,
XtRFont,
sizeof(Font),
offset(calendar.weekday_font),
XtRString,
CALENDAR_DEFAULT_FONT
},
{
XtNtitleFont,
XtCCalendarFont,
XtRFont,
sizeof(Font),
offset(calendar.title_font),
XtRString,
CALENDAR_DEFAULT_FONT
},
{
XtNinfoFont,
XtCCalendarFont,
XtRFont,
sizeof(Font),
offset(calendar.info_font),
XtRString,
CALENDAR_DEFAULT_FONT
},
{
XtNdigitGravity,
XtCDigitGravity,
XtRGravity,
sizeof(XtGravity),
offset(calendar.digit_gravity),
XtRImmediate,
(XtPointer)Center
},
{ XtNdigitNames,
XtCDigitNames,
XtRStringTable,
sizeof(StringTable),
offset(calendar.digit_names),
XtRString,
XtNDefaultDigitNames
},
{ XtNweekdayNames,
XtCWeekdayNames,
XtRStringTable,
sizeof(StringTable),
offset(calendar.weekday_names),
XtRString,
XtNDefaultWeekdayNames
},
{ XtNmonthNames,
XtCMonthNames,
XtRStringTable,
sizeof(StringTable),
offset(calendar.month_names),
XtRString,
XtNDefaultMonthNames
},
{ XtNhighlight,
XtCHighlight,
XtRBoolean,
sizeof(Boolean),
offset(calendar.highlight),
XtRImmediate,
(XtPointer)True
},
{ XtNshowYear,
XtCShowYear,
XtRBoolean,
sizeof(Boolean),
offset(calendar.show_year),
XtRImmediate,
(XtPointer)True
},
{ XtNstartingWeekday,
XtCStartingWeekday,
XtRDayName,
sizeof(XtDayName),
offset(calendar.starting_weekday),
XtRImmediate,
(XtPointer)Sunday
}
};
/*==========================================================================*/
/* Action Declarations: */
/*==========================================================================*/
static void select_cell_action();
static void default_button_up_action();
/*==========================================================================*/
/* Actions Table: */
/*==========================================================================*/
static XtActionsRec calendar_actions[] = {
{ "select", select_cell_action },
{ "notify", default_button_up_action }
};
static char calendar_default_translations[] = XtRcalendarDefaultTranslations;
/*==========================================================================*/
/* Method Declarations: */
/*==========================================================================*/
static void calendar_initialize_method();
static void calendar_class_initialize_method();
static void calendar_expose_method();
static void calendar_destroy_method();
static void calendar_resize_method();
static Boolean calendar_set_values_method();
static XtGeometryResult calendar_query_geometry_method();
/*==========================================================================*/
/* Class Record Initialization: */
/*==========================================================================*/
CalendarClassRec calendarClassRec = {
{
/* CORE CLASS PART: */
/* superclass */ (WidgetClass) &coreClassRec,
/* class_name */ "Calendar",
/* widget_size */ sizeof(CalendarRec),
/* class_initialize */ calendar_class_initialize_method,
/* class_part_initialize */ NULL,
/* class_inited */ FALSE,
/* initialize */ calendar_initialize_method,
/* initialize_hook */ NULL,
/* realize */ XtInheritRealize,
/* actions */ calendar_actions,
/* num_actions */ XtNumber(calendar_actions),
/* resources */ calendar_resources,
/* num_resources */ XtNumber(calendar_resources),
/* xrm_class */ NULLQUARK,
/* compress_motion */ TRUE,
/* compress_exposure */ TRUE,
/* compress_enterleave */ TRUE,
/* visible_interest */ FALSE,
/* destroy */ calendar_destroy_method,
/* resize */ calendar_resize_method,
/* expose */ calendar_expose_method,
/* set_values */ calendar_set_values_method,
/* set_values_hook */ NULL,
/* set_values_almost */ XtInheritSetValuesAlmost,
/* get_values_hook */ NULL,
/* accept_focus */ NULL,
/* version */ XtVersion,
/* callback_private */ NULL,
/* tm_table */ calendar_default_translations,
/* query_geometry */ calendar_query_geometry_method,
/* display_accelerator */ XtInheritDisplayAccelerator,
/* extension */ NULL
},
{
/* CALENDAR CLASS PART: */
/* dummy field */ 0
}
};
/* Define the calendar widget class in terms of the above record. */
WidgetClass calendarWidgetClass = (WidgetClass) &calendarClassRec;
static XtConvertArgRec screenConvertArg[] = {
{
XtBaseOffset,
(XtPointer)XtOffset(Widget, core.screen),
sizeof(Screen *)
}
};
/*==========================================================================*/
/* Method Definitions: */
/*==========================================================================*/
static void calendar_class_initialize_method()
{
/* Register the string to gravity resource converter. */
XtAddConverter(
XtRString, /* source type */
XtRGravity, /* target type */
GravityConverter, /* converter routine */
NULL, /* args for converter */
0 /* num args for converter */
);
/* Register the string to gravity resource converter. */
XtAddConverter(
XtRString, /* source type */
XtRDayName, /* target type */
DayNameConverter, /* converter routine */
NULL, /* args for converter */
0 /* num args for converter */
);
/* Register the string to string table resource converter. */
XtAddConverter(
XtRString, /* source type */
XtRStringTable, /* target type */
StringTableConverter, /* converter routine */
NULL, /* args for converter */
0 /* num args for converter */
);
/* Register the string to bitmap resource converter. */
XtAddConverter(
XtRString, /* source type */
XtRBitmap, /* target type */
XmuCvtStringToBitmap, /* converter routine */
screenConvertArg, /* args for converter */
XtNumber(screenConvertArg) /* num args for converter */
);
}
static void calendar_initialize_method(request, new)
CalendarWidget request;
CalendarWidget new;
{
XrmValue from;
XrmValue to;
int i;
float initial_cell_width = (float) request->core.width / (float) COLS;
float initial_cell_height = (float) request->core.height / (float) ROWS;
BEGIN("calendar_initialize_method");
/* Now entering the foggy zone. */
new->calendar.state = foggy;
/* First check resources: */
/* Did we get 7 weekday names? */
if (StringTableNumber(request->calendar.weekday_names) != 7) {
/*VARARGS*/
fprintf(stderr, "CalendarWidget: You must specify 7 weekday names.\n");
/* Explicitly invoke converter on default value */
from.addr = XtNDefaultWeekdayNames;
from.size = sizeof(StringTable);
XtConvert(new, XtRString, &from, XtRStringTable, &to);
request->calendar.weekday_names = *(StringTable *)to.addr;
}
/* Make a widget specific copy so we can twiddle with it if necessary. */
new->calendar.weekday_names
= StringTableCopy(request->calendar.weekday_names);
/* Do we need to rotate the weekdays? */
if (new->calendar.starting_weekday != SUNDAY) {
rotate_weekdays(new);
}
/* Did we get 12 month names? */
if (StringTableNumber(request->calendar.month_names) != 12) {
/*VARARGS*/
fprintf(stderr, "CalendarWidget: You must specify 12 month names.\n");
/* Explicitly invoke converter on default value */
from.addr = XtNDefaultMonthNames;
from.size = sizeof(StringTable);
XtConvert(new, XtRString, &from, XtRStringTable, &to);
new->calendar.month_names = *(StringTable *)to.addr;
}
/* Did we get 31 digit names? */
if (StringTableNumber(request->calendar.digit_names) != 31) {
/*VARARGS*/
fprintf(stderr, "CalendarWidget: You must specify 31 digit names.\n");
/* Explicitly invoke converter on default value */
from.addr = XtNDefaultDigitNames;
from.size = sizeof(StringTable);
XtConvert(new, XtRString, &from, XtRStringTable, &to);
new->calendar.digit_names = *(StringTable *)to.addr;
}
range_check(
XtNlineWidth,
new->calendar.line_width,
MIN_LINE_WIDTH,
MAX_LINE_WIDTH
);
range_check(
XtNdigitGravity,
new->calendar.digit_gravity,
NorthWest,
SouthEast
);
/* Next compute initial state: */
/* Compute the lengths of the month, weekday and digit names once and keep */
/* around so we won't have to recompute them every time they're drawn. */
for (i=0; i < DAYS_IN_WEEK; i++) {
new->calendar.weekday_name_lengths[i] = strlen(new->calendar.weekday_names[i]);
}
for (i=0; i < MONTHS_IN_YEAR; i++) {
new->calendar.month_name_lengths[i] = strlen(new->calendar.month_names[i]);
}
for (i=0; i < MAX_DAYS_IN_MONTH; i++) {
new->calendar.digit_name_lengths[i] = strlen(new->calendar.digit_names[i]);
}
initialize_line_GCs(new);
initialize_font_GCs_and_info(new);
compute_minimum_cell_size(new);
new->calendar.real_cell_width
= MAX(new->calendar.min_cell_width + 1, initial_cell_width);
new->calendar.real_cell_height
= MAX(new->calendar.min_cell_height + 1, initial_cell_height);
new->calendar.old_window_width = -1;
new->calendar.old_window_height = -1;
if (new->core.width == 0) {
new->core.width = WINDOW_WIDTH(new->calendar.real_cell_width);
}
if (new->core.height == 0) {
new->core.height = WINDOW_HEIGHT(new->calendar.real_cell_height);
}
compute_grid (new);
compute_cell_geometry (new);
/* Calendar defaults to current date. */
new->calendar.date = GetTodaysDate();
/* Allocate a fixed amount of space for the title string. */
new->calendar.title_string = (char *)XtMalloc(MAX_TITLE_LEN*sizeof(char));
/* Force a re-calculation of the month data for the current year. */
new->calendar.current_year = 0;
compute_month_data(new);
new->calendar.highlight_days_in_february
= new->calendar.days_in_february;
/* Calendar highlights current day by default, if highligh is true. */
new->calendar.highlight_date = new->calendar.date;
compute_title_string(new);
END("calendar_initialize_method");
}
/*ARGSUSED*/
static void calendar_expose_method(widget, event)
CalendarWidget widget;
XExposeEvent *event;
{
BEGIN("calendar_expose_method");
/* Okay, it's safe to draw now. */
widget->calendar.state = solid;
draw_weekdays (widget);
draw_title (widget);
draw_digits (widget);
if (SYNC(widget)) {
toggle_highlight (widget, ON);
}
draw_grid (widget);
END("calendar_expose_method");
}
static Boolean calendar_set_values_method(current, request, new)
CalendarWidget current;
CalendarWidget request;
CalendarWidget new;
{
Boolean redraw = False;
Boolean resize = False;
Boolean new_weekday_names = False;
Boolean new_starting_weekday = False;
Display * display;
XGCValues values;
XtGCMask mask;
int i;
int new_width;
int new_height;
BEGIN("calendar_set_values_method");
display = XtDisplay(current);
/* Line Width: */
if (request->calendar.line_width != current->calendar.line_width) {
new->calendar.line_width = bound(
request->calendar.line_width,
MIN_LINE_WIDTH,
MAX_LINE_WIDTH
);
}
if (new->calendar.line_width != current->calendar.line_width) {
mask = GCLineWidth;
values.line_width = new->calendar.line_width;
XChangeGC(
display,
new->calendar.draw_gc,
mask,
&values
);
redraw = True;
}
/* Digit Gravity: */
if (request->calendar.digit_gravity != current->calendar.digit_gravity) {
new->calendar.digit_gravity = (XtGravity) bound(
(int) request->calendar.digit_gravity,
(int) NorthWest,
(int) SouthEast
);
}
if (new->calendar.digit_gravity != current->calendar.digit_gravity) {
redraw = True;
}
/* Weekday Names: */
if (request->calendar.weekday_names != current->calendar.weekday_names) {
if (StringTableNumber(request->calendar.weekday_names) != DAYS_IN_WEEK) {
/*VARARGS*/
fprintf(stderr, "CalendarWidget: must specify 7 weekday names\n");
} else {
/* Copy the string table so we can rotate it if necessary. */
new->calendar.weekday_names =
StringTableCopy(request->calendar.weekday_names);
for (i=0; i < DAYS_IN_WEEK; i++) {
new->calendar.weekday_name_lengths[i]
= strlen(request->calendar.weekday_names[i]);
}
new_weekday_names = True;
redraw = True;
resize = True;
}
}
/* Month Names: */
if (request->calendar.month_names != current->calendar.month_names) {
if (StringTableNumber(request->calendar.month_names) != MONTHS_IN_YEAR) {
/*VARARGS*/
fprintf(stderr, "CalendarWidget: must specify 12 month names\n");
} else {
new->calendar.month_names = request->calendar.month_names;
for (i=0; i < MONTHS_IN_YEAR; i++) {
new->calendar.month_name_lengths[i]
= strlen(request->calendar.month_names[i]);
}
compute_title_string(new);
redraw = True;
resize = True;
}
}
/* Digit Names: */
if (request->calendar.digit_names != current->calendar.digit_names) {
if (StringTableNumber(request->calendar.digit_names) != MAX_DAYS_IN_MONTH) {
/*VARARGS*/
fprintf(stderr, "CalendarWidget: must specify 31 digit names\n");
} else {
new->calendar.digit_names = request->calendar.digit_names;
for (i=0; i < MAX_DAYS_IN_MONTH; i++) {
new->calendar.digit_name_lengths[i]
= strlen(request->calendar.digit_names[i]);
}
redraw = True;
resize = True;
}
}
/* Starting Weekday. */
new_starting_weekday = (request->calendar.starting_weekday
!= current->calendar.starting_weekday);
if (new_starting_weekday
&& (request->calendar.starting_weekday < SUNDAY
|| request->calendar.starting_weekday > SATURDAY)) {
/* Bogus value specified. */
/*VARARGS*/
fprintf(stderr, "CalendarWidget: invalid starting weekday, use [0..6]\n");
new_starting_weekday = FALSE;
}
if (new_starting_weekday && new_weekday_names) {
new->calendar.starting_weekday = request->calendar.starting_weekday;
rotate_weekdays(new);
} else if (new_weekday_names) {
rotate_weekdays(new);
} else if (new_starting_weekday) {
/* Rotate weekdays, taking into account current rotation. */
new->calendar.starting_weekday =
( (7 - current->calendar.starting_weekday)
+ request->calendar.starting_weekday ) %7;
rotate_weekdays(new);
new->calendar.starting_weekday = request->calendar.starting_weekday;
redraw = True;
}
/* Highlight. */
if (request->calendar.highlight != current->calendar.highlight) {
new->calendar.highlight = request->calendar.highlight;
}
if (resize) {
compute_minimum_cell_size(new);
new_width = WINDOW_WIDTH(new->calendar.min_cell_width);
new_height = WINDOW_HEIGHT(new->calendar.min_cell_height);
XtMakeResizeRequest(new, new_width, new_height, NULL, NULL);
}
END("calendar_set_values_method");
/* Return true if value change requires expose event to redraw. */
return (redraw);
}
static void calendar_resize_method(widget)
CalendarWidget widget;
{
int window_width = widget->core.width;
int window_height = widget->core.height;
float avail_cell_width = (float) window_width / (float) COLS;
float avail_cell_height = (float) window_height / (float) ROWS;
BEGIN("calendar_resize_method");
/* Now entering the foggy zone. */
widget->calendar.state = foggy;
widget->calendar.real_cell_width = low_bound(
avail_cell_width,
widget->calendar.min_cell_width
);
widget->calendar.real_cell_height = low_bound(
avail_cell_height,
widget->calendar.min_cell_height
);
compute_grid (widget);
compute_cell_geometry (widget);
/* Don't need to redraw the calendar, an expose event follows. */
END("calendar_resize_method");
}
static XtGeometryResult calendar_query_geometry_method(widget, proposed, preferred)
CalendarWidget widget;
XtWidgetGeometry *proposed;
XtWidgetGeometry *preferred;
{
/*
* There are three scenarios:
*
* 1. The geometry proposed by our parent is the same as our preferred
* geometry. Return XtGeometryYes to say, "Yes, I'm happy with your
* proposed geometry so please resize me to it."
*
* 2. My preferred geometry is the same as our current geometry. Return
* XtGeometryNo to say, "I'm at my preferred geometry so please don't
* resize me."
*
* 3. The geometry proposed by our parent is not our preferred geometry,
* nor am I at my preferred geometry. Return XtGeometryAlmost to say
* "I'm sending you my preferred geometry. Can't you try again to get
* me the size I want?"
*
* Note that my parent is free to totally ignore this information, just like
* most parents ignore what their kids tell them.
*/
BEGIN("calendar_query_geometry_method");
preferred->request_mode = CWWidth | CWHeight;
preferred->width = WINDOW_WIDTH(widget->calendar.min_cell_width) + 7;
preferred->height = WINDOW_HEIGHT(widget->calendar.min_cell_height) + 8;
if ( ((proposed->request_mode & (CWWidth | CWHeight)) == (CWWidth | CWHeight))
&& ((proposed->width == preferred->width)
&& (proposed->height == preferred->height)) ) {
/* Yes, I like your proposal. */
return XtGeometryYes;
} else {
if ( (preferred->width == widget->core.width) &&
(preferred->height == widget->core.height) ) {
/* Don't resize me! I'm already at my preferred geometry. */
return XtGeometryNo;
} else {
/* Please try again. */
return XtGeometryAlmost;
}
}
END("calendar_query_geometry_method");
}
static void calendar_destroy_method(w)
Widget w;
{
free_line_GCs(w);
free_font_GCs_and_info(w);
}
/*==========================================================================*/
/* Action Definitions: */
/*==========================================================================*/
static void select_cell_action(widget, event)
CalendarWidget widget;
XButtonEvent *event;
{
int window_width = widget->core.width;
int window_height = widget->core.height;
float cell_width = widget->calendar.real_cell_width;
float cell_height = widget->calendar.real_cell_height;
int current_x_cell = widget->calendar.current_x_cell;
int current_y_cell = widget->calendar.current_y_cell;
int x = event->x;
int y = event->y;
int new_y_cell = y / cell_height;
int new_x_cell = x / cell_width;
int cellnum;
if (widget->calendar.state == foggy) {
return;
}
/* Ignore events that occur outside the window. These are motion */
/* events that started inside the window but were dragged outside. */
if ( (x<1) || (x>window_width-1) || (y<1) || (y>window_height-1) ) {
return;
}
/* Ignore repeat events for the same cell. */
if (new_x_cell == current_x_cell && new_y_cell == current_y_cell) {
return;
}
/* Ignore events in the month/year box. */
if (new_y_cell<2) {
return;
}
/* Ignore events outide the cells that we care about for current month. */
cellnum = CELLXYtoCELLNUM(new_x_cell, new_y_cell);
if (cellnum < widget->calendar.month_start_cellnum ||
cellnum > widget->calendar.month_end_cellnum) {
return;
}
widget->calendar.cell_selected = True;
/* Reset the old selected cell. */
if (SYNC(widget)) {
toggle_highlight(widget, OFF);
}
/* Highlight the newly selected cell. */
widget->calendar.highlight_date.year = widget->calendar.date.year;
widget->calendar.highlight_date.month = widget->calendar.date.month;
widget->calendar.highlight_date.day = CELLNUMtoDAYNUM(cellnum);
toggle_highlight(widget, ON);
return;
}
/*ARGSUSED*/
static void default_button_up_action(widget, event)
CalendarWidget widget;
XButtonEvent *event;
{
if (!widget->calendar.cell_selected) {
return;
}
widget->calendar.cell_selected = False;
/* The button-down or button-motion event inverted the cell, so all */
/* we have to do is call any callbacks that the application registered. */
XtCallCallbacks(widget, XtNcallback, &widget->calendar.highlight_date);
}
/*==========================================================================*/
/* Method Support Routines: */
/*==========================================================================*/
static void initialize_line_GCs(widget)
CalendarWidget widget;
{
XGCValues values;
XtGCMask mask;
mask = GCForeground | GCBackground | GCLineWidth;
values.foreground = widget->calendar.foreground;
values.background = widget->calendar.background;
values.line_width = widget->calendar.line_width;
widget->calendar.draw_gc = XtGetGC(widget, mask, &values);
mask = GCForeground | GCBackground;
values.foreground = widget->calendar.background;
values.background = widget->calendar.foreground;
widget->calendar.undraw_gc = XtGetGC(widget, mask, &values);
mask = GCForeground | GCBackground | GCFunction;
values.foreground = widget->calendar.foreground;
values.background = widget->calendar.background;
values.function = GXinvert;
widget->calendar.invert_gc = XtGetGC(widget, mask, &values);
}
static void initialize_font_GCs_and_info(widget)
CalendarWidget widget;
{
XGCValues values;
XtGCMask mask;
/* Digit font */
mask = GCForeground | GCBackground | GCFont;
values.foreground = widget->calendar.foreground;
values.background = widget->calendar.background;
values.font = widget->calendar.digit_font;
widget->calendar.digit_draw_gc = XtGetGC(widget, mask, &values);
mask = GCForeground | GCBackground | GCFont;
values.foreground = widget->calendar.background;
values.background = widget->calendar.foreground;
values.font = widget->calendar.digit_font;
widget->calendar.digit_undraw_gc = XtGetGC(widget, mask, &values);
widget->calendar.digit_fsp = XQueryFont(
XtDisplay(widget),
XGContextFromGC(widget->calendar.digit_draw_gc)
);
/* Weekday font */
mask = GCForeground | GCBackground | GCFont;
values.foreground = widget->calendar.foreground;
values.background = widget->calendar.background;
values.font = widget->calendar.weekday_font;
widget->calendar.weekday_draw_gc = XtGetGC(widget, mask, &values);
mask = GCForeground | GCBackground | GCFont;
values.foreground = widget->calendar.background;
values.background = widget->calendar.foreground;
values.font = widget->calendar.weekday_font;
widget->calendar.weekday_undraw_gc = XtGetGC(widget, mask, &values);
widget->calendar.weekday_fsp = XQueryFont(
XtDisplay(widget),
XGContextFromGC(widget->calendar.weekday_draw_gc)
);
/* Title font */
mask = GCForeground | GCBackground | GCFont;
values.foreground = widget->calendar.foreground;
values.background = widget->calendar.background;
values.font = widget->calendar.title_font;
widget->calendar.title_draw_gc = XtGetGC(widget, mask, &values);
mask = GCForeground | GCBackground | GCFont;
values.foreground = widget->calendar.background;
values.background = widget->calendar.foreground;
values.font = widget->calendar.title_font;
widget->calendar.title_undraw_gc = XtGetGC(widget, mask, &values);
widget->calendar.title_fsp = XQueryFont(
XtDisplay(widget),
XGContextFromGC(widget->calendar.title_draw_gc)
);
/* Info font */
mask = GCForeground | GCBackground | GCFont;
values.foreground = widget->calendar.foreground;
values.background = widget->calendar.background;
values.font = widget->calendar.info_font;
widget->calendar.info_draw_gc = XtGetGC(widget, mask, &values);
mask = GCForeground | GCBackground | GCFont;
values.foreground = widget->calendar.background;
values.background = widget->calendar.foreground;
values.font = widget->calendar.info_font;
widget->calendar.info_undraw_gc = XtGetGC(widget, mask, &values);
widget->calendar.info_fsp = XQueryFont(
XtDisplay(widget),
XGContextFromGC(widget->calendar.info_draw_gc)
);
}
/* Compute the minimum allowable height and width of a cell based on */
/* the specified fonts. This is done during initialization and when */
/* XtSetValues changes a font or string table. */
static void compute_minimum_cell_size(new)
CalendarWidget new;
{
int max_day_digit_width = 0;
int max_day_digit_height = 0;
int max_day_name_width = 0;
int max_day_name_height = 0;
int max_month_name_width = 0;
int max_year_width = 0;
int max_title_width;
int max_title_height;
XFontStruct * font;
char digit_string[4];
char * string;
int length;
int i;
int temp;
/* WIDTH: */
/* Compute the max width needed for all digits in the digit font. */
font = new->calendar.digit_fsp;
for (i=0; i < MAX_DAYS_IN_MONTH; i++) {
temp =
XTextWidth(
font,
new->calendar.digit_names[i],
new->calendar.digit_name_lengths[i]
);
max_day_digit_width = MAX(max_day_digit_width, temp);
}
/* Add in a space to avoid cramming. */
max_day_digit_width += XTextWidth(font, " ", 1);
/* Compute the max width needed for all weekday names in the names font. */
font = new->calendar.weekday_fsp;
for (i=0; i < DAYS_IN_WEEK; i++) {
string = new->calendar.weekday_names[i];
length = new->calendar.weekday_name_lengths[i];
temp = XTextWidth(font, string, length);
max_day_name_width = MAX(max_day_name_width, temp);
}
max_day_name_width += XTextWidth(font, " ", 1);
/* Compute the max width needed for all month names in title font. */
font = new->calendar.title_fsp;
for (i=0; i < MONTHS_IN_YEAR; i++) {
string = new->calendar.month_names[i];
length = new->calendar.month_name_lengths[i];
temp = XTextWidth(font, string, length);
max_month_name_width = MAX(max_month_name_width, temp);
}
/* Compute the max width needed to hold any 5 digit year in title font. */
digit_string[1] = NULL;
for (i=0; i < 10; i++) {
digit_string[0] = (char) ('0' + i);
temp = XTextWidth(font, digit_string, 1);
max_year_width = MAX(max_year_width, temp);
}
max_year_width *= 5;
max_title_width
= max_month_name_width
+ max_year_width
+ XTextWidth(font, " ", 1) ;
/* Divide this by the number of rows. Add one for spacing. */
max_title_width /= (ROWS + 1);
/* Set the minimum cell width to the max width required above. */
new->calendar.min_cell_width =
(float) MAX3(max_day_digit_width,
max_day_name_width,
max_title_width);
/* HEIGHT: */
font = new->calendar.digit_fsp;
max_day_digit_height = font->ascent + font->descent;
font = new->calendar.weekday_fsp;
max_day_name_height = font->ascent + font->descent;
font = new->calendar.title_fsp;
max_title_height = font->ascent + font->descent;
/* Set the minimum cell height to the max height required above plus */
/* whatever we need to for the line width and borders. */
new->calendar.min_cell_height =
(float) MAX3(max_day_digit_height, max_day_name_height, max_title_height)
+ 2 * INVERT_BORDER
+ 2 * new->calendar.line_width
+ 2;
}
static void free_line_GCs(widget)
CalendarWidget widget;
{
Display *display;
display = XtDisplay(widget);
XFreeGC(display, widget->calendar.draw_gc);
XFreeGC(display, widget->calendar.undraw_gc);
XFreeGC(display, widget->calendar.invert_gc);
}
static void free_font_GCs_and_info(widget)
CalendarWidget widget;
{
Display *display;
display = XtDisplay(widget);
XFreeGC(display, widget->calendar.digit_draw_gc);
XFreeGC(display, widget->calendar.digit_undraw_gc);
XFreeFont(display, widget->calendar.digit_fsp);
XFreeGC(display, widget->calendar.weekday_draw_gc);
XFreeGC(display, widget->calendar.weekday_undraw_gc);
XFreeFont(display, widget->calendar.weekday_fsp);
XFreeGC(display, widget->calendar.title_draw_gc);
XFreeGC(display, widget->calendar.title_undraw_gc);
XFreeFont(display, widget->calendar.title_fsp);
XFreeGC(display, widget->calendar.info_draw_gc);
XFreeGC(display, widget->calendar.info_undraw_gc);
XFreeFont(display, widget->calendar.info_fsp);
}
static void rotate_weekdays(widget)
CalendarWidget widget;
{
int weekday;
int i;
char * save_names[DAYS_IN_WEEK];
int save_lengths[DAYS_IN_WEEK];
weekday = widget->calendar.starting_weekday;
if (!weekday) {
return;
}
/* Save a copy of the table. */
for (i=0; i < DAYS_IN_WEEK; i++) {
save_names[i] = widget->calendar.weekday_names[i];
save_lengths[i] = widget->calendar.weekday_name_lengths[i];
}
/* Rotate the original. */
for (i=0; i < DAYS_IN_WEEK; i++) {
widget->calendar.weekday_names[i] = save_names[weekday];
widget->calendar.weekday_name_lengths[i] = save_lengths[weekday];
weekday++;
if (weekday == DAYS_IN_WEEK) {
weekday = 0;
}
}
}
/*
For simplicity and convenience, the window is divided into a grid of 7 by 8
rectangles. However, vertical lines are not drawn in the top 2 rows of boxes
because that's where the month/year text and day-of-week names will go.
*/
static void compute_grid(widget)
CalendarWidget widget;
{
int window_width = widget->core.width;
int window_height = widget->core.height;
float cell_width = widget->calendar.real_cell_width;
float cell_height = widget->calendar.real_cell_height;
float min_cell_width = widget->calendar.min_cell_width;
float min_cell_height = widget->calendar.min_cell_height;
int bottom_showing = (window_height >= ROWS * min_cell_height);
int right_showing = (window_width >= COLS * min_cell_width);
int line_width = widget->calendar.line_width;
int fudge = line_width % 2;
int seg;
int i;
/* Compute horizontal segments. */
seg = 0;
for (i=2; i < HORIZ_SEGMENTS+2; i++, seg++) {
/* Starting point. */
widget->calendar.segments[seg].x1 = (short) (0);
widget->calendar.segments[seg].y1 = (short) (cell_height * i);
/* Ending point. */
widget->calendar.segments[seg].x2 = (short) (window_width);
widget->calendar.segments[seg].y2 = (short) (cell_height * i);
}
/* Compute vertical segments. */
for (i=1; i < VERTI_SEGMENTS+1; i++, seg++) {
/* Starting point. */
widget->calendar.segments[seg].x1 = (short) (cell_width * i);
widget->calendar.segments[seg].y1 = (short) (cell_height * 2);
/* Ending point. */
widget->calendar.segments[seg].x2 = (short) (cell_width * i);
widget->calendar.segments[seg].y2 = (short) (window_height);
}
/* Draw a border around the whole grid. I could do this with an */
/* XRectangle, but since I'm calling XDrawSegments, it's one less */
/* function call to do it this way (could still be slower though) */
/* draw solid top */
widget->calendar.segments[seg].x1 = (short) (0);
widget->calendar.segments[seg].y1 = (short) (0);
widget->calendar.segments[seg].x2 = (short) (window_width);
widget->calendar.segments[seg].y2 = (short) (0);
/* draw solid left */
seg++;
widget->calendar.segments[seg].x1 = (short) (0);
widget->calendar.segments[seg].y1 = (short) (0);
widget->calendar.segments[seg].x2 = (short) (0);
widget->calendar.segments[seg].y2 = (short) (window_height);
/* draw solid bottom */
if (bottom_showing) {
seg++;
widget->calendar.segments[seg].x1 = (short) (0);
widget->calendar.segments[seg].y1 = (short) (window_height - fudge);
widget->calendar.segments[seg].x2 = (short) (window_width);
widget->calendar.segments[seg].y2 = (short) (window_height - fudge);
}
/* draw solid right */
if (right_showing) {
seg++;
widget->calendar.segments[seg].x1 = (short) (window_width - fudge);
widget->calendar.segments[seg].y1 = (short) (0);
widget->calendar.segments[seg].x2 = (short) (window_width - fudge);
widget->calendar.segments[seg].y2 = (short) (window_height);
}
widget->calendar.number_segments = seg+1;
}
static void draw_grid(widget)
CalendarWidget widget;
{
if (widget->calendar.state == foggy) {
return;
}
/* Draw the grid. */
XDrawSegments(
XtDisplay(widget),
XtWindow(widget),
widget->calendar.draw_gc,
widget->calendar.segments,
widget->calendar.number_segments
);
}
static void compute_cell_geometry(widget)
CalendarWidget widget;
{
int window_width = widget->core.width;
int window_height = widget->core.height;
int old_window_width = widget->calendar.old_window_width;
int old_window_height = widget->calendar.old_window_height;
float cell_width = widget->calendar.real_cell_width;
float cell_height = widget->calendar.real_cell_height;
int line_width = widget->calendar.line_width;
int line_factor = line_width / 2;
int fudge = line_width % 2;
int temp_x;
int temp_y;
int x,y;
/* If the window size has changed, re-compute the cell_geometry array. */
/* This takes a little while (lots of float arithmetic) but that's okay */
/* because it only happens on resize, and we should punish the user. */
if (old_window_width != window_width || old_window_height != window_height) {
/* Compute geometries for cells not along top, right or bottom edge. */
for (x=0; x < COLS-1; x++) {
for (y=1; y < ROWS-1; y++) {
GEOMETRY(widget, x, y).x = temp_x
= (short) (x * cell_width + line_factor + fudge + INVERT_BORDER);
GEOMETRY(widget, x, y).y = temp_y
= (short) (y * cell_height + line_factor + fudge + INVERT_BORDER);
GEOMETRY(widget, x, y).width
= (unsigned short) ((x+1) * cell_width - line_factor - INVERT_BORDER - temp_x);
GEOMETRY(widget, x, y).height
= (unsigned short) ((y+1) * cell_height - line_factor - INVERT_BORDER - temp_y);
}
}
/* Compute geometries for cells along right edge. */
x = COLS-1;
for (y=0; y < ROWS-1; y++) {
GEOMETRY(widget, x, y).x = temp_x
= (short) (x * cell_width + line_factor + fudge + INVERT_BORDER);
GEOMETRY(widget, x, y).y = temp_y
= (short) (y * cell_height + line_factor + fudge + INVERT_BORDER);
GEOMETRY(widget, x, y).width
= (unsigned short) (COLS * cell_width - line_factor - INVERT_BORDER - temp_x - fudge);
GEOMETRY(widget, x, y).height
= (unsigned short) ((y+1) * cell_height - line_factor - INVERT_BORDER - temp_y);
}
/* Compute geometries for cells along bottom edge. */
/* For a valid calendar, at most the first two cells in the bottom */
/* row will be used. */
y = ROWS-1;
for (x=0; x < 3; x++) {
GEOMETRY(widget, x, y).x = temp_x
= (short) (x * cell_width + line_factor + fudge + INVERT_BORDER);
GEOMETRY(widget, x, y).y = temp_y
= (short) (y * cell_height + line_factor + fudge + INVERT_BORDER);
GEOMETRY(widget, x, y).width
= (unsigned short) ((x+1) * cell_width - line_factor - INVERT_BORDER - temp_x);
GEOMETRY(widget, x, y).height
= (unsigned short) (ROWS * cell_height - line_factor - INVERT_BORDER - temp_y - fudge);
}
/* Finally, compute the available rectangle for centering the title. */
widget->calendar.title_geometry.x = temp_x
= (short) (line_factor + fudge + INVERT_BORDER);
widget->calendar.title_geometry.y = temp_y
= (short) (line_factor + fudge + INVERT_BORDER);
widget->calendar.title_geometry.width
= (unsigned short) (COLS * cell_width - line_factor - INVERT_BORDER - temp_x - fudge);
widget->calendar.title_geometry.height
= (unsigned short) (cell_height - line_factor - INVERT_BORDER - temp_y);
/* Factor in the day names font size for a better appearance. */
widget->calendar.title_geometry.height
= (unsigned short) (widget->calendar.title_geometry.height
+ cell_height
- widget->calendar.weekday_fsp->ascent
- widget->calendar.weekday_fsp->descent
- 1);
widget->calendar.old_window_height = window_height;
widget->calendar.old_window_width = window_width;
}
}
static void draw_weekdays(widget)
CalendarWidget widget;
{
if (widget->calendar.state == foggy) {
return;
}
DrawStringsInRects(
XtDisplay(widget),
XtWindow(widget),
widget->calendar.weekday_draw_gc,
widget->calendar.weekday_fsp,
widget->calendar.weekday_names,
widget->calendar.weekday_name_lengths,
&GEOMETRY(widget, 0, 1),
DAYS_IN_WEEK,
South,
1,0
);
}
static void draw_digits(widget)
CalendarWidget widget;
{
int x,y;
int start = widget->calendar.month_start_cellnum;
int end = widget->calendar.month_end_cellnum;
if (widget->calendar.state == foggy) {
return;
}
CELLNUMtoCELLXY(start,x,y);
DrawStringsInRects(
XtDisplay(widget),
XtWindow(widget),
widget->calendar.digit_draw_gc,
widget->calendar.digit_fsp,
widget->calendar.digit_names,
widget->calendar.digit_name_lengths,
&GEOMETRY(widget, x, y),
end - start + 1,
widget->calendar.digit_gravity,
1,0
);
}
static void draw_title(widget)
CalendarWidget widget;
{
if (widget->calendar.state == foggy) {
return;
}
DrawStringInRect(
XtDisplay(widget),
XtWindow(widget),
widget->calendar.title_draw_gc,
widget->calendar.title_fsp,
widget->calendar.title_string,
widget->calendar.title_string_length,
&widget->calendar.title_geometry,
Center,
1,0
);
}
/* Routine to fill in the widget->calendar.month_starting_weekdays[] and */
/* the widget->calendar.days_in_february field, given a certain date. */
/* The data is only computed when the widget's current_year has changed. */
static void compute_month_data(widget)
CalendarWidget widget;
{
int month = widget->calendar.date.month - 1;
int year = widget->calendar.date.year;
int leap_days;
int total_days;
int weekday;
int i;
if (widget->calendar.current_year != year) {
/* Compute data for the whole year while we're at it. */
leap_days = 1; /* Year 0 was a leap year. */
leap_days += year/4; /* Add all years divisible by 4. */
leap_days -= year/100; /* Subtract all century years. */
leap_days += year/400; /* Add back century years divisible by 400. */
if (A_LEAP_YEAR(year)) {
leap_days--;
widget->calendar.days_in_february = 29;
} else {
widget->calendar.days_in_february = 28;
}
/* Compute days elapsed to beginning of the year. */
total_days = (year*365) + leap_days;
/* Compute the starting weekday for january in this year. */
weekday = DAYStoWEEKDAY(total_days);
weekday = ROTATE(widget, weekday);
widget->calendar.month_starting_weekdays[JANUARY] = weekday;
/* Compute the starting weekday for february in this year. */
total_days += days_in_month[JANUARY];
weekday = DAYStoWEEKDAY(total_days);
weekday = ROTATE(widget, weekday);
widget->calendar.month_starting_weekdays[FEBRUARY] = weekday;
/* Compute the starting weekday for the rest of the months. */
total_days += widget->calendar.days_in_february;
for (i=MARCH; i <= DECEMBER; i++) {
weekday = DAYStoWEEKDAY(total_days);
weekday = ROTATE(widget, weekday);
widget->calendar.month_starting_weekdays[i] = weekday;
total_days += days_in_month[i];
}
widget->calendar.current_year = year;
/* Compute the new year string. */
if (widget->calendar.show_year) {
sprintf(widget->calendar.year_string, "%d" ,widget->calendar.date.year);
widget->calendar.year_string_length = strlen(widget->calendar.year_string);
} else {
strcpy(widget->calendar.year_string, "");
widget->calendar.year_string_length = 0;
}
}
widget->calendar.month_start_cellnum
= widget->calendar.month_starting_weekdays[month];
widget->calendar.month_end_cellnum
= widget->calendar.month_start_cellnum
+ DAYS_IN_MONTH(widget) - 1;
}
static void compute_title_string(widget)
CalendarWidget widget;
{
int month = widget->calendar.date.month - 1;
/* Compute the title string length. */
widget->calendar.title_string_length = widget->calendar.month_name_lengths[month];
/* Fill in the title string. */
strcpy(widget->calendar.title_string, widget->calendar.month_names[month]);
if (widget->calendar.show_year) {
strcat(widget->calendar.title_string, " ");
widget->calendar.title_string_length += 1;
strcat(widget->calendar.title_string, widget->calendar.year_string);
widget->calendar.title_string_length += widget->calendar.year_string_length;
}
}
static void clear_calendar(widget)
CalendarWidget widget;
{
int month_start_cellnum = widget->calendar.month_start_cellnum;
int month_end_cellnum = widget->calendar.month_end_cellnum;
Display * display;
Window window;
int x,y;
if (widget->calendar.state == foggy) {
return;
}
BEGIN("clear_calendar");
display = XtDisplay(widget);
window = XtWindow(widget);
CELLNUMtoCELLXY(month_start_cellnum, x, y);
/* Erase the digits. */
XFillRectangles(
display,
window,
widget->calendar.undraw_gc,
&GEOMETRY(widget,x,y),
month_end_cellnum - month_start_cellnum + 1
);
/* Erase the title box. */
XFillRectangles(
display,
window,
widget->calendar.undraw_gc,
&widget->calendar.title_geometry,
1
);
END("clear_calendar");
}
static void calendar_update(widget)
CalendarWidget widget;
{
BEGIN("calendar_update");
clear_calendar (widget);
compute_month_data (widget);
compute_title_string (widget);
draw_title (widget);
draw_digits (widget);
if (SYNC(widget)) {
toggle_highlight(widget, ON);
} else {
widget->calendar.current_x_cell = 0;
widget->calendar.current_y_cell = 0;
}
END("calendar_update");
}
static void toggle_highlight(widget, state)
CalendarWidget widget;
int state;
{
int current_x_cell;
int current_y_cell;
int day;
Display * display;
Window window;
GC draw_gc;
GC undraw_gc;
if (widget->calendar.state == foggy) {
return;
}
BEGIN("toggle_highlight");
WIDGETtoCELLXY(
widget,
current_x_cell,
current_y_cell
);
day = widget->calendar.highlight_date.day - 1;
if (current_y_cell && widget->calendar.highlight) {
display = XtDisplay(widget);
window = XtWindow(widget);
if (state == ON) {
draw_gc = widget->calendar.draw_gc;
undraw_gc = widget->calendar.digit_undraw_gc;
} else {
draw_gc = widget->calendar.undraw_gc;
undraw_gc = widget->calendar.digit_draw_gc;
}
XFillRectangles(
display,
window,
draw_gc,
&GEOMETRY(widget, current_x_cell, current_y_cell),
1
);
DrawStringInRect(
display,
window,
undraw_gc,
widget->calendar.digit_fsp,
widget->calendar.digit_names[day],
widget->calendar.digit_name_lengths[day],
&GEOMETRY(widget, current_x_cell, current_y_cell),
widget->calendar.digit_gravity,
1,0
);
/* Eventually this will need to draw the day info as well. */
}
if (state == OFF) {
widget->calendar.current_x_cell = 0;
widget->calendar.current_y_cell = 0;
} else {
widget->calendar.current_x_cell = current_x_cell;
widget->calendar.current_y_cell = current_y_cell;
}
END("toggle_highlight");
}
/*==========================================================================*/
/* Public Functions: */
/*==========================================================================*/
void CalendarIncMonth(widget)
CalendarWidget widget;
{
if (widget->calendar.date.month == DECEMBER+1) {
widget->calendar.date.month = JANUARY+1;
widget->calendar.date.year++;
} else {
widget->calendar.date.month++;
}
calendar_update(widget);
}
void CalendarDecMonth(widget)
CalendarWidget widget;
{
if (widget->calendar.date.month == JANUARY+1) {
widget->calendar.date.month = DECEMBER+1;
widget->calendar.date.year--;
} else {
widget->calendar.date.month--;
}
calendar_update(widget);
}
void CalendarIncYear(widget)
CalendarWidget widget;
{
widget->calendar.date.year++;
calendar_update(widget);
}
void CalendarDecYear(widget)
CalendarWidget widget;
{
widget->calendar.date.year--;
calendar_update(widget);
}
void CalendarIncDay(widget, show)
CalendarWidget widget;
Boolean show;
{
if (!widget->calendar.highlight_date.day) {
return;
}
BEGIN("CalendarIncDay");
if (SYNC(widget)) {
toggle_highlight(widget, OFF);
}
if (widget->calendar.highlight_date.day == DAYS_IN_HIGHLIGHT_MONTH(widget)) {
/* We've rolled over a month. */
widget->calendar.highlight_date.day = 1;
if (widget->calendar.highlight_date.month == DECEMBER+1) {
widget->calendar.highlight_date.month = JANUARY+1;
widget->calendar.highlight_date.year++;
/* We've rolled over a year, get new days in feb. */
widget->calendar.highlight_days_in_february
= 28 + A_LEAP_YEAR(widget->calendar.highlight_date.year);
} else {
widget->calendar.highlight_date.month++;
}
if (show) {
CalendarShowMonth(widget, widget->calendar.highlight_date);
}
} else {
widget->calendar.highlight_date.day++;
}
if (SYNC(widget)) {
toggle_highlight(widget, ON);
}
END("CalendarIncDay");
}
void CalendarDecDay(widget, show)
CalendarWidget widget;
Boolean show;
{
if (!widget->calendar.highlight_date.day) {
return;
}
BEGIN("CalendarDecDay");
if (SYNC(widget)) {
toggle_highlight(widget, OFF);
}
if (widget->calendar.highlight_date.day == 1) {
/* We've rolled over a month. */
if (widget->calendar.highlight_date.month == JANUARY+1) {
widget->calendar.highlight_date.month = DECEMBER+1;
widget->calendar.highlight_date.year--;
widget->calendar.highlight_date.day = 31;
/* We've rolled over a year, get new days in feb. */
widget->calendar.highlight_days_in_february
= 28 + A_LEAP_YEAR(widget->calendar.highlight_date.year);
} else {
widget->calendar.highlight_date.month--;
widget->calendar.highlight_date.day = DAYS_IN_HIGHLIGHT_MONTH(widget);
}
if (show) {
CalendarShowMonth(widget, widget->calendar.highlight_date);
}
} else {
widget->calendar.highlight_date.day--;
}
if (SYNC(widget)) {
toggle_highlight(widget, ON);
}
END("CalendarDecDay");
}
void CalendarSetDate(widget, date)
CalendarWidget widget;
Date date;
{
if (!is_valid_date(&date)) {
return;
}
BEGIN("CalendarSetDate");
if (SYNC(widget)) {
/* Reset any already highlighted date. */
toggle_highlight(widget, OFF);
}
if (widget->calendar.date.month == date.month &&
widget->calendar.date.year == date.year) {
/* The date specified is on the calendar currently being shown. */
/* Highlight the specified date. */
widget->calendar.highlight_date = date;
toggle_highlight(widget, ON);
} else {
/* The date specified is not on the current calendar. */
if (date.year != widget->calendar.highlight_date.year) {
widget->calendar.highlight_days_in_february
= 28 + A_LEAP_YEAR(date.year);
}
widget->calendar.highlight_date = date;
}
END("CalendarSetDate");
}
void CalendarShowMonth(widget, date)
CalendarWidget widget;
Date date;
{
date.day = 1;
if (!is_valid_date(&date)) {
return;
}
BEGIN("CalendarShowMonth");
if (widget->calendar.date.month == date.month
&& widget->calendar.date.year == date.year) {
/* The corresponding calendar is currently being displayed. */
} else {
/* The corresponding calendar is not currently being displayed. */
widget->calendar.date.month = date.month;
widget->calendar.date.year = date.year;
calendar_update(widget);
}
END("CalendarShowMonth");
}
void CalendarGetDate(widget, date_ptr)
CalendarWidget widget;
Date * date_ptr;
{
*date_ptr = widget->calendar.highlight_date;
}
Date DateConverter(widget, string)
CalendarWidget widget;
char * string;
{
/* Call the real converter. This just allows us to hide details. */
return (convert_string_to_date(widget->calendar.month_names, string));
}
/* Returns a Date structure corresponding to the current date. */
/* Most applications are going to need it so I'll just provide it. */
Date GetTodaysDate()
{
Date date;
time_t current_time = time(NULL);
struct tm * current_tm = localtime(¤t_time);
date.month = current_tm->tm_mon + 1;
date.day = current_tm->tm_mday;
date.year = current_tm->tm_year + 1900;
return date;
}
/* Return a string like "Thursday October 15, 1990" */
char * CalendarPrettyDate(widget)
CalendarWidget widget;
{
static char buffer[MAX_PRETTY_DATE_STRING_LENGTH];
Date highlight_date;
Date showing_date;
int weekday;
int month;
highlight_date = widget->calendar.highlight_date;
showing_date = widget->calendar.date;
if (showing_date.year == highlight_date.year) {
month = highlight_date.month - 1;
weekday = widget->calendar.month_starting_weekdays[month];
weekday = (weekday + highlight_date.day - 1) % 7;
} else {
/* No info available, so resort to brute force. */
weekday = compute_weekday(highlight_date);
weekday = ROTATE(widget, weekday);
}
/*VARARGS*/
sprintf(
buffer,
"%s %s %d, %d",
widget->calendar.weekday_names[weekday],
widget->calendar.month_names[highlight_date.month-1],
highlight_date.day,
highlight_date.year
);
return buffer;
}
static int compute_weekday(date)
Date date;
{
int leap_days;
int total_days;
int month;
int days_in_february;
leap_days = 1; /* Year 0 was a leap year. */
leap_days += date.year/4; /* Add all years divisible by 4. */
leap_days -= date.year/100; /* Subtract all century years. */
leap_days += date.year/400; /* Add back century years divisible by 400. */
if (A_LEAP_YEAR(date.year)) {
leap_days--;
days_in_february = 29;
} else {
days_in_february = 28;
}
total_days = (date.year*365) + leap_days;
for (month=0; month < date.month - 1; month ++) {
if (month == FEBRUARY) {
total_days += days_in_february;
} else {
total_days += days_in_month[month];
}
}
total_days += date.day - 1;
return DAYStoWEEKDAY(total_days);
}