#define XtStrlen(s)		((s) ? strlen(s) : 0)

#include <stdio.h>
#include <ctype.h>
#ifdef _AIX
#include <time.h>
#endif
#include <sys/time.h>
#include <X11/Xos.h>
#include <X11/StringDefs.h>
#include <X11/IntrinsicP.h>
#include <Xm/Xm.h>
#include "CalendarP.h"

char    *smon[]= {
        "January", "February", "March", "April",
        "May", "June", "July", "August",
        "September", "October", "November", "December",
};

/*
 * Default Translation table.
 */

static char defaultTranslations[] =
  "Shift<Btn1Down>: extend()\n\
   <Btn1Motion>: selectRange()\n\
   <Btn1Down>: startSelect()\n\
   <Btn1Up>: endSelect()";

/****************************************************************
 *
 * Full class record constant
 *
 ****************************************************************/

/* Private Data */
static void Initialize(), Realize(), Redisplay(), Resize();
static void startSelect();
static void endSelect();
static void selectRange();
static void extend();
static void doubleClick();
static void ClassInitialize();
static Boolean SetValues();

#define offset(field) XtOffset(CalendarWidget,calendar.field)
#define goffset(field) XtOffset(Widget,core.field)

static int zero = 0;

static XtResource resources[] = {
  { XmNclickProc, XmCCallback, XmRCallback, sizeof(XtCallbackProc),
     offset( clickProc ), XmRCallback, NULL },
  { XmNdateExtProc, XmCCallback, XmRCallback, sizeof(XtCallbackProc),
     offset( dateExtProc ), XmRCallback, NULL },
  { XmNdateProc, XmCCallback, XmRCallback, sizeof(XtCallbackProc),
     offset( dateProc ), XmRCallback, NULL },
  { XmNdayFont, XmCFont, XmRFontStruct, sizeof( XFontStruct * ),
     offset( dayFont ), XmRString, "XtDefaultFont" },
  { XmNdrawGrid, XmCBoolean, XmRBoolean, sizeof ( Boolean ),
     offset ( drawGrid ), XmRString, "TRUE" },
  { XmNfont, XmCFont, XmRFontStruct, sizeof( XFontStruct * ),
     offset( font ), XmRString, "XtDefaultFont" },
  { XmNforeground, XmCForeground, XmRPixel, sizeof( Pixel ),
     offset( foreground_pixel ), XmRString, "XtDefaultForeground" },
  { XmNheight, XmCHeight, XmRDimension, sizeof( Dimension ),
     goffset( height ), XmRString, "130" },
  { XmNhiliteFont, XmCFont, XmRFontStruct, sizeof( XFontStruct * ),
     offset( hiliteFont ), XmRString, "XtDefaultFont" },
  { XmNdJustify, XmCJustify, XmRJustify, sizeof( XtJustify ),
     offset( dateJustify ), XmRString, "left" },
  { XmNmonth, XmCDate, XmRInt, sizeof( int ),
     offset( month ), XmRInt, (caddr_t)&zero },
  { XmNmonthFont, XmCFont, XmRFontStruct, sizeof( XFontStruct * ),
     offset( monthFont ), XmRString, "XtDefaultFont" },
  { XmNmonthProc, XmCCallback, XmRCallback, sizeof(caddr_t),
     offset( monthProc ), XmRCallback, NULL },
  { XmNshowDays, "ShowDays", XmRBoolean, sizeof( Boolean ),
     offset ( showDays ), XmRString, "TRUE" },
  { XmNshowMonth, XmCBoolean, XmRBoolean, sizeof( Boolean ),
     offset ( showMonth ), XmRString, "TRUE" },
  { XmNshowYear, XmCBoolean, XmRBoolean, sizeof( Boolean ),
     offset ( showYear ), XmRString, "FALSE" },
  { XmNwidth, XmCWidth, XmRDimension, sizeof( Dimension ),
     goffset( width ), XmRString, "140" },
  { XmNyear, XmCDate, XmRInt, sizeof( int ),
     offset( year ), XmRInt, (caddr_t)&zero },
  { XmNyearProc, XmCCallback, XmRCallback, sizeof(caddr_t),
     offset( yearProc ), XmRCallback, NULL },
};

#undef offset
#undef goffset

static XtActionsRec actionTable[] = {
  {"startSelect", startSelect},
  {"endSelect",   endSelect},
  {"selectRange", selectRange},
  {"extend",      extend},
  {"doubleClick", doubleClick},
  {NULL,          NULL},
};

CalendarClassRec calendarClassRec = {
  {
/* core_class fields */	
    /* superclass	  	*/	(WidgetClass) &widgetClassRec,
    /* class_name	  	*/	"Calendar",
    /* widget_size	  	*/	sizeof(CalendarRec),
    /* class_initialize   	*/	ClassInitialize,
    /* class_part_initialize	*/	NULL,
    /* class_inited       	*/	FALSE,
    /* initialize	  	*/	Initialize,
    /* initialize_hook		*/	NULL,
    /* realize		  	*/	Realize,
    /* actions		  	*/	actionTable,
    /* num_actions	  	*/	XtNumber(actionTable),
    /* resources	  	*/	resources,
    /* num_resources	  	*/	XtNumber(resources),
    /* xrm_class	  	*/	NULLQUARK,
    /* compress_motion	  	*/	FALSE,
    /* compress_exposure  	*/	TRUE,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest	  	*/	FALSE,
    /* destroy		  	*/	NULL,
    /* resize		  	*/	Resize,
    /* expose		  	*/	Redisplay,
    /* set_values	  	*/	SetValues,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	XtInheritSetValuesAlmost,
    /* get_values_hook		*/	NULL,
    /* accept_focus	 	*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private   	*/	NULL,
    /* tm_table		   	*/	defaultTranslations,
    /* query_geometry		*/	XtInheritQueryGeometry,
    /* display_accelerator	*/	XtInheritDisplayAccelerator,
    /* extension		*/	NULL
  }
};
WidgetClass calendarWidgetClass = (WidgetClass)&calendarClassRec;
/****************************************************************
 *
 * Private Procedures
 *
 ****************************************************************/
/* must link with Xmu... */
static void ClassInitialize()
{
  extern void XmuCvtStringToJustify();
  XtAddConverter( XtRString, XtRJustify, XmuCvtStringToJustify, NULL, 0 );
}


static void GetnormalGC( cw )
    CalendarWidget cw;
{
    XGCValues values;
    XtGCMask valuemask;
    GC gc;

    valuemask = GCForeground | GCBackground | GCFont;
    values.foreground = cw->calendar.foreground_pixel;
    values.background = cw->core.background_pixel;
    if ( cw->calendar.font != NULL )
      values.font = cw->calendar.font->fid;
    else
      valuemask &= ~GCFont;

    gc = XtGetGC( (Widget)cw, valuemask, &values );
    cw->calendar.normal_GC = gc;

    values.foreground = cw->core.background_pixel;
    values.background = cw->calendar.foreground_pixel;
    gc = XtGetGC( (Widget)cw, valuemask, &values );

    cw->calendar.reverse_GC = gc;
}

/* Recalculates some geometry; needs to be called if any of
   core.width, core.height, calendar.monthFont,
   calendar.dayFont change, calendar.showDays, calendar.showMonth   */
static void initGeometry( cw )
     CalendarWidget cw;
{
  int cwidth = cw->calendar.calendar_width;
  int cheight = cw->calendar.calendar_height;
  int cal_height;

  /* Calculates origin of calendar grid */

  if ( cw->calendar.showMonth )
    cw->calendar.cells_top = cw->calendar.monthFont->ascent +
                             cw->calendar.monthFont->descent +
	            2 * cw->calendar.cell_BorderWidth; /* prob. not right */
  else
    cw->calendar.cells_top = 0;

  if ( cw->calendar.showDays )
    {
      cw->calendar.days_top = cw->calendar.cells_top;
      cw->calendar.cells_top += cw->calendar.dayFont->ascent +
                                cw->calendar.dayFont->descent +
		    2 * cw->calendar.cell_BorderWidth; /* prob. not right */
    }

  cw->calendar.cells_left = 0;

  cal_height = cw->core.height - cw->calendar.cells_top;


  cw->calendar.hStep = ( cw->core.width - cw->calendar.cell_BorderWidth - 1 )
                         / cwidth;
  
  cw->calendar.cell_width = cw->calendar.hStep -
                            cw->calendar.cell_BorderWidth;

  cw->calendar.hMax = (( cal_height - cw->calendar.cell_BorderWidth - 1 )
		         / cheight) * cheight;

  cw->calendar.vStep = ( cal_height - cw->calendar.cell_BorderWidth - 1 )
                         / cheight;
  
  cw->calendar.cell_height = cw->calendar.vStep -
                             cw->calendar.cell_BorderWidth;
  
  cw->calendar.vMax = (( cw->core.width - cw->calendar.cell_BorderWidth - 1 )
	                 / cwidth) * cwidth;
}

static void Realize(w, valueMask, attributes)
    register Widget w;
    Mask *valueMask;
    XSetWindowAttributes *attributes;
{
  XtCreateWindow( w, (unsigned int)InputOutput, (Visual *)CopyFromParent,
                  *valueMask, attributes );
}

static void initMonthYear( cw )
CalendarWidget cw;
{
  int i;
  char string[30], year[10];
  int mw, yw = 0, tw; /* init to shutup the compiler */

  if ( cw->calendar.showYear )
    {
      sprintf( year, "%d", cw->calendar.year );
      yw = XTextWidth( cw->calendar.monthFont, year, XtStrlen( year ));
    }

  sprintf( string, "%s", smon[cw->calendar.month - 1] );
  mw = XTextWidth( cw->calendar.monthFont, string, XtStrlen( string ));

  if ( cw->calendar.showYear )
    {
      strcat( string, ", " );
      strcat( string, year );
      tw = XTextWidth( cw->calendar.monthFont, string, XtStrlen( string ));
    }
  else
    tw = mw;

  i = ( cw->core.width - tw ) / 2;

  cw->calendar.monthLeft = i;
  cw->calendar.monthRight = i + mw;

  if ( cw->calendar.showYear )
    {
      cw->calendar.yearLeft = i + tw - yw;
      cw->calendar.yearRight = i + tw;
    }
  strcpy( cw->calendar.strMY, string );
}

static void showMonthYear( cw )
CalendarWidget cw;
{
  XSetFont( XtDisplay( (Widget)cw ), cw->calendar.normal_GC,
	   cw->calendar.monthFont->fid );

  XDrawString( XtDisplay( (Widget) cw), XtWindow( (Widget) cw),
	       cw->calendar.normal_GC,
	       cw->calendar.monthLeft, cw->calendar.monthFont->ascent +
	       cw->calendar.cell_BorderWidth, /* prob. not right */
	       cw->calendar.strMY,
	       XtStrlen( cw->calendar.strMY ));
  
  XSetFont( XtDisplay( (Widget)cw ), cw->calendar.normal_GC,
	   cw->calendar.font->fid );
}

/* generate an inverse mapping from the mapping */
static void makeinv( cw )
CalendarWidget cw;
{
  int cell;

  for ( cell = 0; cell < cw->calendar.calendar_width *
       cw->calendar.calendar_height; cell++ )
    cw->calendar.invmapping[cell] = -1; /* this is bad */

  for ( cell = 0; cell < cw->calendar.calendar_width *
       cw->calendar.calendar_height; cell++ )
    if ( cw->calendar.mapping[cell] != 0 )
      cw->calendar.invmapping[ cw->calendar.mapping[cell] ] = cell;
}

static void Initialize(request, new)
 Widget request, new;
{
  int i;
  CalendarWidget cw = (CalendarWidget) new;
  struct timeval time;
  struct tm *timestr;

  GetnormalGC( cw );

  cw->calendar.normal_ascent = cw->calendar.font->ascent;
  
  cw->calendar.cell_BorderWidth = 3; /* resource? */
  cw->calendar.calendar_width = 7;

  cw->calendar.calendar_height = 6;
  for ( i = 0; i < 100; i++ )
    cw->calendar.selection[i] = 0;

  /* if month or year is zero, fill it in with now */
  if ( cw->calendar.month == 0 || cw->calendar.year == 0 )
    {
      gettimeofday( &time, NULL );
      timestr = localtime( &time.tv_sec );
      if ( cw->calendar.month == 0 )
	cw->calendar.month = timestr->tm_mon + 1;
      if ( cw->calendar.year == 0 )
	cw->calendar.year = timestr->tm_year + 1900;
    }
  cal( cw->calendar.month, cw->calendar.year, cw->calendar.mapping );
  makeinv( cw );
  initGeometry( cw );
  initMonthYear( cw );
} /* Initialize */

static Boolean SetValues( current, request, new, args, num_args )
CalendarWidget current, request, new;
ArgList args;
Cardinal *num_args;
{
  int i;

  if ( new->calendar.month != current->calendar.month ||
       new->calendar.year != current->calendar.year )
    {
      cal( new->calendar.month,
	   new->calendar.year,
	   new->calendar.mapping );
      makeinv( new );
      initMonthYear( new );
      for (i = 0; i < new->calendar.calendar_width *
	   new->calendar.calendar_height; i++ )
	new->calendar.selection[i] = 0;
    }

  if ( new->calendar.showYear != current->calendar.showYear )
    initMonthYear( new );

  if ( new->calendar.showMonth != current->calendar.showMonth ||
       new->calendar.showYear != current->calendar.showYear ||
       new->calendar.showDays != current->calendar.showDays )
    initGeometry( new );

  return True;
}

static int cvtCoordToCell( cw, x, y )
     CalendarWidget cw;
     int x, y;
{
  int rem_x, rem_y, cell_x, cell_y;

  cell_x = ( x - cw->calendar.cells_left ) /
           (cw->calendar.cell_BorderWidth + cw->calendar.cell_width);
  cell_y = ( y - cw->calendar.cells_top ) /
           (cw->calendar.cell_BorderWidth + cw->calendar.cell_height);

  rem_x = ( x - cw->calendar.cells_left ) %
          (cw->calendar.cell_BorderWidth + cw->calendar.cell_width);
  rem_y = ( y - cw->calendar.cells_top ) %
          (cw->calendar.cell_BorderWidth + cw->calendar.cell_height);

  if ( (rem_x < cw->calendar.cell_BorderWidth) ||
       (rem_y < cw->calendar.cell_BorderWidth) ||
       (cell_x < 0) ||
       (cell_x >= cw->calendar.calendar_width) ||
       (cell_y < 0) ||
       (cell_y >= cw->calendar.calendar_height) )
    return IN_BORDER;

  return cell_x + cell_y * cw->calendar.calendar_width;
}

static void cvtXYToCoord( cw, cx, cy, x, y )
     CalendarWidget cw;
     int cx, cy, *x, *y;
{
  *x = cx * (cw->calendar.cell_BorderWidth + cw->calendar.cell_width) +
       cw->calendar.cell_BorderWidth + cw->calendar.cells_left;
  *y = cy * (cw->calendar.cell_BorderWidth + cw->calendar.cell_height) +
       cw->calendar.cell_BorderWidth + cw->calendar.cells_top;
}

static void cvtCellToXY( cw, cell, x, y )
     CalendarWidget cw;
     int cell, *x, *y;
{
  *x = cell % cw->calendar.calendar_width;
  *y = cell / cw->calendar.calendar_width;
}

static int cvtXYToCell( cw, x, y )
     CalendarWidget cw;
     int x, y;
{
  return x + y * cw->calendar.calendar_width;
}

static int cellInRange( x, y, sx, sy, ex, ey )
int x, y, sx, sy, ex, ey;
{
  int swap;

  if ( sx > ex )
    {
      swap = sx; sx = ex; ex = swap;
    }

  if ( sy > ey )
    {
      swap = sy; sy = ey; ey = swap;
    }

  if ( x < sx || x > ex || y < sy || y > ey )
    return 0;
  return 1;
}

static void drawCell( cw, cell, fillmode )
     CalendarWidget cw;
     int cell;
     int fillmode;
{
  static char date[5];
  int cx, cy, x, y, extraJust = 0; /* shutup! */
  GC gc = NULL, agc = NULL; /* shutup! */
  
  switch( fillmode )
    {
    case C_SELECT:
      gc = cw->calendar.normal_GC;
      agc = cw->calendar.reverse_GC;
      break;

    case C_UNSELECT:
      gc = cw->calendar.reverse_GC;
      agc = cw->calendar.normal_GC;
      break;
    }

  cvtCellToXY( cw, cell, &cx, &cy );
  cvtXYToCoord( cw, cx, cy, &x, &y );

  XFillRectangle( XtDisplay( (Widget) cw ), XtWindow( (Widget) cw ),
		 gc,
		 x, y,
		 cw->calendar.cell_width,
		 cw->calendar.cell_height );
  
  if ( cw->calendar.mapping[cell] )
    {
      sprintf( date, "%d", cw->calendar.mapping[cell] );
      switch( cw->calendar.dateJustify )
	{
	case XtJustifyRight:
	  extraJust = cw->calendar.cell_width -
	    XTextWidth( cw->calendar.font, date, XtStrlen( date ) );
	  break;
	case XtJustifyCenter: /* who would want this? */
	  extraJust = (cw->calendar.cell_width -
	    XTextWidth( cw->calendar.font, date, XtStrlen( date ) )) / 2;
	  break;
	case XtJustifyLeft:
	  extraJust = 0;
	  break;
	}
      XDrawString(
		  XtDisplay( (Widget) cw), XtWindow( (Widget) cw),
		  agc, x + extraJust, y + cw->calendar.normal_ascent,
		  date, XtStrlen( date ));
    }
}

static void plotGrid( cw )
CalendarWidget cw;
{
  int where, max, step;

  /* vertical */
  step = cw->calendar.hStep;
  max = cw->calendar.hMax;
  for ( where = 0; where <= cw->calendar.calendar_width; where++ )
    XDrawLine( XtDisplay( (Widget)cw ), XtWindow( (Widget)cw ),
	      cw->calendar.normal_GC,
	      cw->calendar.cells_left + where * step + 1,
	      cw->calendar.cells_top + 1,
	      cw->calendar.cells_left + where * step + 1,
	      cw->calendar.cells_top + max + 1 );
  
  /* horizontal */
  step = cw->calendar.vStep;
  max = cw->calendar.vMax;
  for ( where = 0; where <= cw->calendar.calendar_height; where++ )
    XDrawLine( XtDisplay( (Widget)cw ), XtWindow( (Widget)cw ),
	      cw->calendar.normal_GC,
	      cw->calendar.cells_left + 1,
	      cw->calendar.cells_top + where*step + 1,
	      cw->calendar.cells_left + max + 1,
	      cw->calendar.cells_top + where*step + 1 );
}

static int inMonthYear( cw, x, y )
CalendarWidget cw;
int x, y;
{
  if (( y > cw->calendar.cell_BorderWidth ) &&
      ( y < cw->calendar.cell_BorderWidth +
        cw->calendar.monthFont->ascent +
        cw->calendar.monthFont->descent ))
    {
      if (( x > cw->calendar.monthLeft ) &&
	  ( x < cw->calendar.monthRight ))
	return INMONTH;
      if (( x > cw->calendar.yearLeft ) &&
	  ( x < cw->calendar.yearRight ))
	return INYEAR;
    }
  return NOTIN;
}

static char *daysOfWeek[] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
};

static void showDaysOfWeek( cw )
CalendarWidget cw;
{
  int i;
  int x, y;
  char day[20];

  XSetFont( XtDisplay( (Widget)cw ), cw->calendar.normal_GC,
	   cw->calendar.dayFont->fid );

  for ( i = 0; i < 7; i++ ) /* oh, no! hard-coded # of days in week! */
    {
      cvtXYToCoord( cw, i, 0, &x, &y );
      sprintf( day, "%s", daysOfWeek[i] );
      x += (cw->calendar.cell_width -
	   XTextWidth( cw->calendar.dayFont, day, XtStrlen( day ))) / 2;

      XDrawString( XtDisplay( (Widget) cw), XtWindow( (Widget) cw),
		   cw->calendar.normal_GC,
	           x, cw->calendar.dayFont->ascent +
		   cw->calendar.days_top +
		   cw->calendar.cell_BorderWidth, /* prob. not right */
		   day,
		   XtStrlen( day ));
  
    }
  XSetFont( XtDisplay( (Widget)cw ), cw->calendar.normal_GC,
	    cw->calendar.font->fid );
}

static void redraw( cw )
CalendarWidget cw;
{
  int ix, iy, i;

  for ( iy = 0; iy < cw->calendar.calendar_height; iy++ )
    for ( ix = 0; ix < cw->calendar.calendar_width; ix++ )
      {
	i = cvtXYToCell( cw, ix, iy );
	if ( !(cw->calendar.selection[i] & (SELECTED | SELECTING) ))
	  drawCell( cw, i, C_UNSELECT );
	else
	  drawCell( cw, i, C_SELECT );
      }
}

/*
 * Widget exposure
 */

static void Redisplay( w, event, region )
    Widget w;
    XEvent *event;
    Region region;
{
   CalendarWidget cw = (CalendarWidget) w;

   if ( cw->calendar.showMonth )
     showMonthYear( cw );

   if ( cw->calendar.showDays )
     showDaysOfWeek( cw );

   if ( cw->calendar.drawGrid )
     plotGrid( cw );

   redraw( cw );
}

static void Resize( w )
Widget w;
{
  initMonthYear( (CalendarWidget)w );
  initGeometry( (CalendarWidget)w );
}

static void startSelect( w, event, params, num_params )
Widget w;
XEvent *event;
String *params;
Cardinal *num_params;
{
  int i, cell;
  CalendarWidget cw = (CalendarWidget) w;

  cw->calendar.extended = 0;
  cell = cvtCoordToCell( cw, event->xbutton.x, event->xbutton.y );
  if ( cell != IN_BORDER )
    {
      XtCallCallbacks( w, XmNclickProc, NULL );

      for (i = 0; i < cw->calendar.calendar_width *
	              cw->calendar.calendar_height; i++ )
	if ( cw->calendar.selection[i] & (SELECTED | SELECTING) )
	  {
	    drawCell( cw, i, C_UNSELECT );
	    cw->calendar.selection[i] &= ~(SELECTED | SELECTING);
	  }
      if ( cw->calendar.mapping[cell] )
	{
	  cw->calendar.selection[cell] |= SELECTED; /* flicker; fix (easy) */
	  drawCell( cw, cell, C_SELECT );
	}
      cvtCellToXY( cw, cell,
		   &cw->calendar.select_start_x,
		   &cw->calendar.select_start_y );
      cw->calendar.select_end_x = cw->calendar.select_start_x;
      cw->calendar.select_end_y = cw->calendar.select_start_y;
      cw->calendar.select_mode = ADDING;
    }
  else
    {
      cw->calendar.select_mode = NOTHING;
      if ( cw->calendar.showMonth )
	switch( inMonthYear( cw, event->xbutton.x, event->xbutton.y ) )
	  {
	  case INMONTH:
	    XtCallCallbacks( w, XmNmonthProc, NULL );
	    break;
	  case INYEAR:
	    XtCallCallbacks( w, XmNyearProc, NULL );
	    break;
	  case NOTIN:
	    break;
	  }
    }
}

/* shit code */
static void doubleClick( w, event, params, num_params )
Widget w;
XEvent *event;
String *params;
Cardinal *num_params;
{
  CalendarWidget cw = (CalendarWidget) w;

  XtCallCallbacks( w, XmNdateProc, &cw->calendar.mapping[15] );
}

static void selectRange( w, event, params, num_params )
Widget w;
XEvent *event;
String *params;
Cardinal *num_params;
{
  int cell, cellx, celly, ix, iy, i, inold, innew;
  CalendarWidget cw = (CalendarWidget) w;

  cell = cvtCoordToCell( cw, event->xbutton.x, event->xbutton.y );
  if ( cw->calendar.select_mode != NOTHING ) 
    if ( cell != IN_BORDER )
      {
	cvtCellToXY( cw, cell, &cellx, &celly );
	if ( ( cellx != cw->calendar.select_end_x ) ||
	     ( celly != cw->calendar.select_end_y ) )
	  {
	    for ( iy = 0; iy < cw->calendar.calendar_height; iy++ )
	      for ( ix = 0; ix < cw->calendar.calendar_width; ix++ )
		{
		  i = cvtXYToCell( cw, ix, iy );
		  inold = cellInRange( ix, iy,
				      cw->calendar.select_start_x,
				      cw->calendar.select_start_y,
				      cw->calendar.select_end_x,
				      cw->calendar.select_end_y );
		  innew = cellInRange( ix, iy,
				      cw->calendar.select_start_x,
				      cw->calendar.select_start_y,
				      cellx,
				      celly );
		  if ( cw->calendar.mapping[i] )
		    switch( cw->calendar.select_mode )
		      {
		      case ADDING:
			if ( inold && !innew )
			  {
			    cw->calendar.selection[i] &= ~SELECTING;
			    if ( !(cw->calendar.selection[i] & SELECTED) )
			      drawCell( cw, i, C_UNSELECT );
			  }
			if ( !inold && innew )
			  {
			    cw->calendar.selection[i] |= SELECTING;
			    if ( !(cw->calendar.selection[i] & SELECTED) )
			      drawCell( cw, i, C_SELECT );
			  }
			break;
		      case SUBING:
			if ( inold && !innew )
			  {
			    cw->calendar.selection[i] &= ~SELECTING;
			    if ( (cw->calendar.selection[i] & SELECTED) )
			      drawCell( cw, i, C_SELECT );
			  }
			if ( !inold && innew )
			  {
			    cw->calendar.selection[i] |= SELECTING;
			    if ( (cw->calendar.selection[i] & SELECTED) )
			      drawCell( cw, i, C_UNSELECT );
			  }
			break;
		      }
		}
	    cw->calendar.select_end_x = cellx;
	    cw->calendar.select_end_y = celly;
	  }
      }
}

static void endSelect( w, event, params, num_params )
Widget w;
XEvent *event;
String *params;
Cardinal *num_params;
{
  int cell, pos = 0;
  CalendarWidget cw = (CalendarWidget) w;

  if ( cw->calendar.select_mode != NOTHING )
    {
      for ( cell = 0; cell < cw->calendar.calendar_width *
	                     cw->calendar.calendar_height; cell++ )
	if ( cw->calendar.selection[cell] & SELECTING )
	  switch( cw->calendar.select_mode )
	    {
	    case ADDING:
	      cw->calendar.selection[cell] |= SELECTED;
	      cw->calendar.selection[cell] &= ~SELECTING;
	      break;
	    case SUBING:
	      cw->calendar.selection[cell] &= ~(SELECTED | SELECTING);
	      break;
	    }
      if ( cw->calendar.extended )
	XtCallCallbacks( w, XmNdateExtProc, NULL );
      else
	XtCallCallbacks( w, XmNdateProc, NULL );
    }
}

void CalGetSelInfo( cw, mask, info )
CalendarWidget cw;
int mask; /* mask is one of {SELECTED, SPECIAL} */
CalendarSelection info[32];
{
  int cell;
  int pos = 0;

  for ( cell = 0; cell < cw->calendar.calendar_width *
       cw->calendar.calendar_height; cell++ )
    if ( cw->calendar.selection[cell] & mask )
      {
	info[pos].month = cw->calendar.month;
	info[pos].day = cw->calendar.mapping[cell];
	info[pos].year = cw->calendar.year;
	pos++;
      }
  info[pos].month = cw->calendar.month;
  info[pos].day = 0;
  info[pos].year = cw->calendar.year;
}

void CalSetSelInfo( cw, mask, info )
CalendarWidget cw;
int mask; /* mask is one of {SELECTED, SPECIAL} */
CalendarSelection info[32];
{
  int i = 0, cell;

  for ( cell = 0; cell < cw->calendar.calendar_width *
       cw->calendar.calendar_height; cell++ )
    cw->calendar.selection[cell] &= ~mask;

  while ( info[i].day != 0 )
    {
      if ( info[i].month == cw->calendar.month &&
	  info[i].year == cw->calendar.year )
	{
	  cell = info[i].day;
	  if ( cw->calendar.invmapping[cell] != -1 )
	    cw->calendar.selection[ cw->calendar.invmapping[cell] ] |= mask;
	}
      i++;
    }
  redraw( cw );
}

static void extend( w, event, params, num_params )
Widget w;
XEvent *event;
String *params;
Cardinal *num_params;
{
  int cell;
  CalendarWidget cw = (CalendarWidget) w;

  cw->calendar.extended = 1;
  cell = cvtCoordToCell( cw, event->xbutton.x, event->xbutton.y );
  if ( cell != IN_BORDER )
    {
      if ( cw->calendar.mapping[cell] )
	switch( cw->calendar.selection[cell] )
	  {
	  case 0: /* define */
	    cw->calendar.selection[cell] = 1;
	    drawCell( cw, cell, C_SELECT );
	    cw->calendar.select_mode = ADDING;
	    break;
	  case 1:
	    cw->calendar.selection[cell] = 0;
	    drawCell( cw, cell, C_UNSELECT );
	    cw->calendar.select_mode = SUBING;
	    break;
	  }

      cvtCellToXY( cw, cell,
		   &cw->calendar.select_start_x,
		   &cw->calendar.select_start_y );
      cw->calendar.select_end_x = cw->calendar.select_start_x;
      cw->calendar.select_end_y = cw->calendar.select_start_y;
    }
  else
    cw->calendar.select_mode = NOTHING;
}
