// Copyright (C) 1995 Jon Kutemeier
// Use is governed by the MiscKit license

#import <objc/List.h>
#import <appkit/Application.h>
#import <apps/InterfaceBuilder.h>
#import <appkit/Text.h>
#import <appkit/TextField.h>
#import <appkit/Font.h>
#import <appkit/Matrix.h>
#import <appkit/Box.h>
#import <appkit/color.h>
#import <appkit/drag.h>

#import "misckit/MiscCalendarViewConstants.h"
#import "misckit/DateDelegateProtocol.h"
#import "misckit/MiscCalendarView.h"

#import "DateSelectionCell.h"
#import "MiscCalendarMatrix.h"

typedef struct {
    int	xdelta;
    int width;
    const char *value;
} calHeaders;


static calHeaders headerSpecs[] = {
{0, 17, "S"},
{16, 19, "M"},
{17, 17, "T"},
{16, 20, "W"},
{18, 17, "T"},
{18, 17, "F"},
{17, 17, "S"},
{-1, -1, ""},
};

@interface MiscCalendarView (TextDelegate)

- (BOOL)textWillChange:sender;
- textDidChange:sender;
- (BOOL)textWillEnd:sender;
- textDidEnd:sender endChar:(unsigned short)whyEnd;
- textDidGetKeys:sender isEmpty:(BOOL)flag;

@end

@interface MiscCalendarView (Dragging)

- (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender;
- draggingExited:(id <NXDraggingInfo>)sender;
- (NXDragOperation)draggingUpdated:(id <NXDraggingInfo>)sender;
- (BOOL)prepareForDragOperation:(id <NXDraggingInfo>)sender;
- (BOOL)performDragOperation:(id <NXDraggingInfo>)sender;
- concludeDragOperation:(id <NXDraggingInfo>)sender;

@end

@interface MiscCalendarView (IBView)

- depositIBColor:(NXColor)aColor at:(NXPoint *)aPoint;
- acceptsIBColorAt:(NXPoint *)aPoint;

@end

@interface MiscCalendarView (Private)

- _createTextFieldWithFrame:(NXRect *)textFrame 
  stringValue:(const char *)aString;
- _acceptColor:(NXColor)aColor at:(NXPoint *)aPoint;

@end

/* This helps us get rid of unwanted compiler warnings and serves */
/* no useful purpose */

@protocol FAKEOUTCC

- getInspectorFor:object owner:owner;
- cellColorMatrix;

@end


@implementation MiscCalendarView

- finishUnarchiving
{
    /* This is called by IB In test interface mode. */
    /* Register a timed-entry to be called right after */
    /* this event-loop to do our final initialization. */

   /* only for IB */

    const char *pbTypes[] = {NXColorPboardType};

    [self registerForDraggedTypes:pbTypes count:1];
    [[self window] registerForDraggedTypes:pbTypes count:1];

   if([NXApp conformsTo:@protocol(IB)])
   {
       [self perform:@selector(awakeFromNib)
	with:nil afterDelay:0.01 cancelPrevious:NO];
   }

   return self;
}

- awakeFromNib
{
    const char *pbTypes[] = {NXColorPboardType};

    [self registerForDraggedTypes:pbTypes count:1];
    [[self window] registerForDraggedTypes:pbTypes count:1];

    helvetica_11 = [Font newFont:"Helvetica" size:11 style:0
		           matrix:NX_FLIPPEDMATRIX];
    system_11_bold = [Font boldSystemFontOfSize:11
		      matrix:NX_FLIPPEDMATRIX];

    helvetica_12 = [Font newFont:"Helvetica" size:12 style:0
		           matrix:NX_FLIPPEDMATRIX];
    system_12_bold = [Font boldSystemFontOfSize:12
		      matrix:NX_FLIPPEDMATRIX];
    
    /* Setup standard calendar action method */

    [calendarMatrix setAction:@selector(updateDate:)];
    [calendarMatrix setTarget:self];

    /* If there is a date delegate, set it */

    if (dateDelegate)
      [calendarMatrix setDateDelegate:dateDelegate];
    else
      dateDelegate = [calendarMatrix dateDelegate];

    /* If there is an external month field, set its value to */
    /* the month value */

    if (monthField && [monthField respondsTo:@selector(setStringValue:)])
      [monthField setStringValue:[[calendarMatrix dateDelegate]
				  monthStringValue]];

    /* If we are displaying the standard month header, set its value */
    /* to the month */

    if (displayMonthHeader)
      [monthSubview setStringValue:[[calendarMatrix dateDelegate]
				    monthStringValue]];

    /* If there is an external year field, set its value to */
    /* the year value */

    if (yearField && [yearField respondsTo:@selector(setIntValue:)])
      [yearField setIntValue:[[calendarMatrix dateDelegate] year]];

    /* If we are displaying the standard year header, set its value */
    /* to the year */

    if (displayYearHeader)
      [yearSubview setIntValue:[[calendarMatrix dateDelegate]
				year]];

    if (displayMonthAndYearHeader)
    {
	[inlineMonthSubview setStringValue:[[calendarMatrix dateDelegate]
					    monthStringValue]];

	[inlineYearSubview setIntValue:[[calendarMatrix dateDelegate]
					year]];
    }

    /* If there is a date field, set its value to the curent date */

    if (dateField && [dateField respondsTo:@selector(setStringValue:)])
      [dateField setStringValue:[[calendarMatrix dateDelegate]
				 dateStringValue]];

    return self;
}

- initFrame:(const NXRect *)frameRect
{
    NXRect 	subViewFrame;
    id		newSubview;
    calHeaders	*headers;
    const char *pbTypes[] = {NXColorPboardType};

    [super initFrame:frameRect];

    helvetica_11 = [Font newFont:"Helvetica" size:11 style:0
		           matrix:NX_FLIPPEDMATRIX];
    system_11_bold = [Font boldSystemFontOfSize:11
		      matrix:NX_FLIPPEDMATRIX];

    helvetica_12 = [Font newFont:"Helvetica" size:12 style:0
		           matrix:NX_FLIPPEDMATRIX];
    system_12_bold = [Font boldSystemFontOfSize:12
		      matrix:NX_FLIPPEDMATRIX];

    [self registerForDraggedTypes:pbTypes count:1];
    [[self window] registerForDraggedTypes:pbTypes count:1];

    colorDragAndDrop = NO;

    /* The list of textfield cells that holds the day of the week */
    /* headings */

    dowSubviewList = [[List alloc] init];

    displayDOWHeader = YES;
    displayMonthHeader = NO;
    displayYearHeader = NO;
    displayMonthAndYearHeader = NO;

    headers = headerSpecs;

    /* static view information for day-of-the-week textfield headers */

    NXSetRect(&subViewFrame, DEFAULT_MATRIX_XPOS+1, DEFAULT_HEADER_YPOS,
	      DEFAULT_MATRIX_WIDTH, DEFAULT_HEADER_HEIGHT);
		 
    /* Create the day of the week headers (S, M, T, W...) */

    while(headers->xdelta > -1)
    {
      NX_X(&subViewFrame) += headers->xdelta;
      NX_WIDTH(&subViewFrame) = headers->width;

      newSubview = [self _createTextFieldWithFrame:&subViewFrame
		    stringValue:headers->value];

      [dowSubviewList addObject:newSubview];

      [self addSubview:newSubview];

      headers++;
    }

    /* Create the month and year textfield headers */

    NXSetRect(&subViewFrame, DEFAULT_MATRIX_XPOS+1, DEFAULT_HEADER_YPOS,
	      DEFAULT_MATRIX_WIDTH, DEFAULT_HEADER_HEIGHT);

    monthSubview = [self _createTextFieldWithFrame:&subViewFrame
		    stringValue:"Month"];

    yearSubview = [self _createTextFieldWithFrame:&subViewFrame
		   stringValue:"Year"];

    [Font setUserFont:helvetica_12];

    [monthSubview setFont:system_12_bold];

    [yearSubview setFont:system_12_bold];

    /* Create the month and year textfield headers, where the month */
    /* and year are on the same line */

    NXSetRect(&subViewFrame, DEFAULT_MATRIX_XPOS+1, DEFAULT_HEADER_YPOS,
	      (int)(DEFAULT_MATRIX_WIDTH/2), DEFAULT_HEADER_HEIGHT);

    inlineMonthSubview = [self _createTextFieldWithFrame:&subViewFrame
			  stringValue:"Month"];

    [inlineMonthSubview setAlignment:NX_LEFTALIGNED];

    [inlineMonthSubview setFont:system_12_bold];

    NXSetRect(&subViewFrame, 
	      ((DEFAULT_MATRIX_WIDTH - (int)(DEFAULT_MATRIX_WIDTH/2) + 
		DEFAULT_MATRIX_XPOS+1)), 
	      DEFAULT_HEADER_YPOS, (int)(DEFAULT_MATRIX_WIDTH/2),
	      DEFAULT_HEADER_HEIGHT);

    inlineYearSubview = [self _createTextFieldWithFrame:&subViewFrame
			 stringValue:"Year"];

    [inlineYearSubview setAlignment:NX_RIGHTALIGNED];

    [inlineYearSubview setFont:system_12_bold];

    /* Create the actual calendar matrix view with the given */
    /* dimensions */

    NXSetRect(&subViewFrame, DEFAULT_MATRIX_XPOS, DEFAULT_MATRIX_YPOS,
	      DEFAULT_MATRIX_WIDTH, DEFAULT_MATRIX_HEIGHT);

    calendarMatrix = [[MiscCalendarMatrix alloc] initFrame:&subViewFrame];

    [calendarMatrix setAutosizing:NX_MINYMARGINSIZABLE | 
     NX_MAXYMARGINSIZABLE | NX_WIDTHSIZABLE | NX_HEIGHTSIZABLE];

    [calendarMatrix setAutosizeCells:YES];

    [calendarMatrix setAction:@selector(updateDate:)];
    [calendarMatrix setTarget:self];

    if (dateDelegate)
      [calendarMatrix setDateDelegate:dateDelegate];
    else
      dateDelegate = [calendarMatrix dateDelegate];

    previousDayCell = [calendarMatrix selectedCell];

    /* Added the calendar matrix to our view */

    [self addSubview:calendarMatrix];

    if (monthField && [monthField respondsTo:@selector(setStringValue:)])
      [monthField setStringValue:[[calendarMatrix dateDelegate]
				  monthStringValue]];

    if (yearField && [yearField respondsTo:@selector(setIntValue:)])
      [yearField setIntValue:[[calendarMatrix dateDelegate] year]];

    if (dateField && [dateField respondsTo:@selector(setStringValue:)])
      [dateField setStringValue:[[calendarMatrix dateDelegate]
				 dateStringValue]];

    NX_WIDTH(&defaultFrame) = frameRect->size.width;
    NX_HEIGHT(&defaultFrame) = frameRect->size.height;

    return self;
}

- getMinSize:(NXSize *)minSize maxSize:(NXSize *)maxSize from:(int)where
{
    /* The view is not sizable at this time */

    minSize->width = NX_WIDTH(&defaultFrame);
    minSize->height = NX_HEIGHT(&defaultFrame);

    maxSize->width = NX_WIDTH(&defaultFrame);
    maxSize->height = NX_HEIGHT(&defaultFrame);

    return self;
}

- calendarMatrix
{
    return calendarMatrix;
}

- preserveCellColors:(BOOL)yn
{
    [calendarMatrix overwriteCellColors:yn];

    return self;
}

- (BOOL)cellColorsPreserved
{
    return [calendarMatrix cellColorsOverwritten];
}

- setColorOf:(int)element to:(NXColor)aColor forCellAt:(int)xPos :(int)yPos
{
    switch (element)
    {
      case MISC_CV_CELLBACKGROUND:
	[calendarMatrix setBackgroundColor:aColor forCellAt:xPos :yPos];
	[calendarMatrix display];
	break;

      case MISC_CV_CELLTEXT:
	[calendarMatrix setTextColor:aColor forCellAt:xPos :yPos];
	[calendarMatrix display];
	break;

      case MISC_CV_CELLHIGHLIGHT:
	[calendarMatrix setHighlightColor:aColor forCellAt:xPos :yPos];
	[calendarMatrix display];
	break;

      case MISC_CV_CELLTEXTHIGHLIGHT:
	[calendarMatrix setHighlightTextColor:aColor forCellAt:xPos :yPos];
	[calendarMatrix display];
	break;

      default:
	break;
    }
    
    return self;
}

- (NXColor)colorOf:(int)element forCellAt:(int)xPos :(int)yPos
{
    NXColor color;

    color = NX_COLORBLACK;

    switch (element)
    {
      case MISC_CV_CELLBACKGROUND:
	color = [calendarMatrix backgroundColorForCellAt:xPos :yPos];
	break;

      case MISC_CV_CELLTEXT:
	color = [calendarMatrix textColorForCellAt:xPos :yPos];
	break;

      case MISC_CV_CELLHIGHLIGHT:
	color = [calendarMatrix highlightColorForCellAt:xPos :yPos];
	break;

      case MISC_CV_CELLTEXTHIGHLIGHT:
	color =[calendarMatrix highlightTextColorForCellAt:xPos :yPos];
	break;

      default:
	break;
    }
    
    return color;
}

- setColorOf:(int)element to:(NXColor)aColor
{
    switch (element)
    {
      case MISC_CV_CELLBACKGROUND:
	[calendarMatrix setCellBackgroundColor:aColor];
	[calendarMatrix display];
	break;

      case MISC_CV_CELLTEXT:
	[calendarMatrix setCellTextColor:aColor];
	[calendarMatrix display];
	break;

      case MISC_CV_CELLHIGHLIGHT:
	[calendarMatrix setCellHighlightColor:aColor];
	[calendarMatrix display];
	break;

      case MISC_CV_CELLTEXTHIGHLIGHT:
	[calendarMatrix setCellHighlightTextColor:aColor];
	[calendarMatrix display];
	break;

      case MISC_CV_MONTHHEADER:
	[monthSubview setTextColor:aColor];
	[monthSubview display];
	break;

      case MISC_CV_YEARHEADER:
	[yearSubview setTextColor:aColor];
	[yearSubview display];
	break;

      case MISC_CV_INLINEMONTHHEADER:
	[inlineMonthSubview setTextColor:aColor];
	[inlineMonthSubview display];
	break;

      case MISC_CV_INLINEYEARHEADER:
	[inlineYearSubview setTextColor:aColor];
	[inlineYearSubview display];
	break;

      default:
	break;
    }
    
    return self;
}

- (NXColor)colorOf:(int)element
{
    NXColor color;

    switch (element)
    {
      case MISC_CV_CELLBACKGROUND:
	color = [calendarMatrix cellBackgroundColor];
	break;

      case MISC_CV_CELLTEXT:
	color = [calendarMatrix cellTextColor];
	break;

      case MISC_CV_CELLHIGHLIGHT:
	color = [calendarMatrix cellHighlightColor];
	break;

      case MISC_CV_CELLTEXTHIGHLIGHT:
	color = [calendarMatrix cellHighlightTextColor];
	break;

      case MISC_CV_MONTHHEADER:
	color = [monthSubview textColor];
	break;

      case MISC_CV_YEARHEADER:
	color = [yearSubview textColor];
	break;

      case MISC_CV_INLINEMONTHHEADER:
	color = [inlineMonthSubview textColor];
	break;

      case MISC_CV_INLINEYEARHEADER:
	color = [inlineYearSubview textColor];
	break;

      default:
	color = NX_COLORBLACK;
	break;
    }
    
    return color;
}

- cellAt:(int)xPos :(int)yPos lock:(BOOL)yn
{
    [[calendarMatrix cellAt:xPos :yPos] setEnabled:yn];

    return self;
}

- forRow:(int)row lock:(BOOL)yn
{
    int i;

    for (i = 0; i < NUM_MATRIX_COLS; i++)
      [self cellAt:row :i lock:yn];

    return self;
}

- forColumn:(int)col lock:(BOOL)yn
{
    int i;

    for (i = 0; i < NUM_MATRIX_ROWS; i++)
      [self cellAt:i :col lock:yn];

    return self;
}

- setHighlightMode:(int)mode
{
    return [calendarMatrix setMode:mode];
}

- (int)highlightMode
{
    return [calendarMatrix mode];
}

- allowColorDragAndDrop:(BOOL)yn
{
    colorDragAndDrop = yn;

    return self;
}

- (BOOL)canDragAndDropColor
{
    return colorDragAndDrop;
}

- forHeader:(int)aHeader display:(BOOL)doDisplay
{
    int		yOffset,
		i;
    
    NX_HEIGHT(&defaultFrame) += doDisplay ? DEFAULT_HEADER_HEIGHT : 
      -DEFAULT_HEADER_HEIGHT;

    [self sizeBy:0 :(doDisplay ? DEFAULT_HEADER_HEIGHT : 
		     -DEFAULT_HEADER_HEIGHT)];
    
    yOffset = doDisplay ? DEFAULT_HEADER_HEIGHT :
      -DEFAULT_HEADER_HEIGHT;
	  
    switch (aHeader)
    {
      case MISC_CV_DOWHEADER:
	displayDOWHeader = doDisplay;

	if (displayMonthAndYearHeader)
	{
	    [inlineMonthSubview moveBy:0 :yOffset];
	    [inlineYearSubview moveBy:0 :yOffset];
	}

	if (displayMonthHeader)
	  [monthSubview moveBy:0 :yOffset];

	if (displayYearHeader)
	  [yearSubview moveBy:0 :yOffset];
	
	if (displayDOWHeader)
	  for (i = 0; i < [dowSubviewList count]; i++)
	    [self addSubview:[dowSubviewList objectAt:i]];
	else
	    for (i = 0; i < [dowSubviewList count]; i++)
	      [[dowSubviewList objectAt:i] removeFromSuperview];

	break;

      case MISC_CV_MONTHANDYEARHEADER:
	displayMonthAndYearHeader = doDisplay;
    
	if (displayYearHeader)
	  [yearSubview moveBy:0 :yOffset];

	if (displayMonthHeader)
	  [monthSubview moveBy:0 :yOffset];

	if (displayDOWHeader)
	{
	    [inlineMonthSubview moveBy:0 :yOffset];
	    [inlineYearSubview moveBy:0 :yOffset];
	}

	if (displayMonthAndYearHeader)
	{
	    [self addSubview:inlineMonthSubview];
	    [self addSubview:inlineYearSubview];
	}
	else
	{
	    [inlineMonthSubview removeFromSuperview];
	    [inlineYearSubview removeFromSuperview];
	}

	break;

      case MISC_CV_MONTHHEADER:
	displayMonthHeader = doDisplay;

	if (displayYearHeader)
	  [yearSubview moveBy:0 :yOffset];

	if (displayDOWHeader)
	  [monthSubview moveBy:0 :yOffset];

	if (displayMonthAndYearHeader)
	  [monthSubview moveBy:0 :yOffset];

	if (displayMonthHeader)
	  [self addSubview:monthSubview];
	else
	  [monthSubview removeFromSuperview];

	break;

      case MISC_CV_YEARHEADER:
	displayYearHeader = doDisplay;

	if (displayDOWHeader)
	  [yearSubview moveBy:0 :yOffset];

	if (displayMonthAndYearHeader)
	  [yearSubview moveBy:0 :yOffset];

	if (displayMonthHeader)
	  [yearSubview moveBy:0 :yOffset];

	if (displayYearHeader)
	  [self addSubview:yearSubview];
	else
	  [yearSubview removeFromSuperview];

	break;
	
      default:
	NX_HEIGHT(&defaultFrame) += !doDisplay ? DEFAULT_HEADER_HEIGHT : 
	  -DEFAULT_HEADER_HEIGHT;

	[self sizeBy:0 :(!doDisplay ? DEFAULT_HEADER_HEIGHT : 
			 -DEFAULT_HEADER_HEIGHT)];
    
	break;
    }

    return self;
}

- (BOOL)isHeaderDisplayed:(int)aHeader;
{
    switch (aHeader)
    {
      case MISC_CV_DOWHEADER:
	return displayDOWHeader;
	break;
	
      case MISC_CV_MONTHHEADER:
	return displayMonthHeader;
	break;

      case MISC_CV_YEARHEADER:
	return displayYearHeader;
	break;

      case MISC_CV_MONTHANDYEARHEADER:
	return displayMonthAndYearHeader;
	break;

      default:
	return NO;
    }
}

- setColorTo:(NXColor)aColor forDay:(MiscCVDays)aDay
{
    return [[[dowSubviewList objectAt:aDay] setTextColor:aColor] display];
}

- (NXColor)colorForDay:(MiscCVDays)aDay
{
    return [[dowSubviewList objectAt:aDay] textColor];
}

- incrementMonth:sender
{
    id date;

    date = [calendarMatrix dateDelegate];

    [date incrementMonth];

    [self updateDate:self];

    previousDayCell = [calendarMatrix selectedCell];

    return self;
}

- decrementMonth:sender
{
    id date;

    date = [calendarMatrix dateDelegate];

    [date decrementMonth];

    [self updateDate:self];

    previousDayCell = [calendarMatrix selectedCell];

    return self;
}

- incrementYear:sender
{
    id date;

    date = [calendarMatrix dateDelegate];

    [date incrementYear];

    [self updateDate:self];

    previousDayCell = [calendarMatrix selectedCell];

    return self;
}

- decrementYear:sender
{
    id date;

    date = [calendarMatrix dateDelegate];

    [date decrementYear];

    [self updateDate:self];

    previousDayCell = [calendarMatrix selectedCell];

    return self;
}

- updateDate:sender
{
    DateSelectionCell	*dayCell;
    id			date;
    int			day,
			month,
			year;

    if (!(dayCell = [calendarMatrix selectedCell]))
    {
	if (previousDayCell)
	  [calendarMatrix selectCell:previousDayCell];

	return nil;
    }

    dayCell = [calendarMatrix selectedCell];

    previousDayCell = dayCell;

    date = [calendarMatrix dateDelegate];

    day = [dayCell intValue];
    month = [date month];
    year = [date year];

    [date setYear:year month:month day:day];

    [self displayDate:self];

    return self;
}

- displayDate:sender
{
    if (monthField && [monthField respondsTo:@selector(setStringValue:)])
      [monthField setStringValue:[[calendarMatrix dateDelegate]
				  monthStringValue]];

    if (displayMonthHeader)
      [monthSubview setStringValue:[[calendarMatrix dateDelegate]
				    monthStringValue]];

    if (yearField && [yearField respondsTo:@selector(setIntValue:)])
      [yearField setIntValue:[[calendarMatrix dateDelegate] year]];

    if (displayYearHeader)
      [yearSubview setIntValue:[[calendarMatrix dateDelegate]
				year]];

    if (dateField && [dateField respondsTo:@selector(setStringValue:)])
      [dateField setStringValue:[[calendarMatrix dateDelegate]
				 dateStringValue]];

    if (displayMonthAndYearHeader)
    {
	[inlineMonthSubview setStringValue:[[calendarMatrix dateDelegate]
					    monthStringValue]];

	[inlineYearSubview setIntValue:[[calendarMatrix dateDelegate]
					year]];
    }

    [calendarMatrix displayDate:sender];

    return self;
}

- setDateDelegate:(id <DateDelegate>)anObject
{
    dateDelegate = anObject;
    [calendarMatrix setDateDelegate:dateDelegate];
    
    return self;
}

- (id <DateDelegate>)dateDelegate
{
    return dateDelegate;
}

- (const char *)getInspectorClassName
{
    return "MiscCalendarViewInspector";
}

- (const char *)getEditorClassName
{
    return "MiscCalendarViewEditor";
}

- read:(NXTypedStream *)aStream
{
    float	width,
		height;

    [super read:aStream];

    NXReadTypes(aStream, "fficcccc@@@@@@@@@@@", &width, &height,
		&titleDisplayMode, &displayDOWHeader, &displayMonthHeader, 
		&displayYearHeader, &displayMonthAndYearHeader,
		&colorDragAndDrop,
		&calendarMatrix, &monthField, &previousDayCell, &monthSubview,
		&dateDelegate, &yearField, &dateField, &dowSubviewList, 
		&yearSubview, &inlineMonthSubview, &inlineYearSubview);

    NX_WIDTH(&defaultFrame) = width;
    NX_HEIGHT(&defaultFrame) = height;

    return self;
}

- write:(NXTypedStream *)aStream
{
    [super write:aStream];

    NXWriteTypes(aStream, "fficcccc@@@@@@@@@@@", &(NX_WIDTH(&defaultFrame)),
		 &(NX_HEIGHT(&defaultFrame)), &titleDisplayMode, 
		 &displayDOWHeader, &displayMonthHeader, &displayYearHeader,
		 &displayMonthAndYearHeader, &colorDragAndDrop, 
		 &calendarMatrix, &monthField,
		 &previousDayCell, &monthSubview, &dateDelegate, &yearField,
		 &dateField, &dowSubviewList, &yearSubview, 
		 &inlineMonthSubview, &inlineYearSubview);

    return self;
}

@end

@implementation MiscCalendarView (TextDelegate)

- (BOOL)textWillChange:sender
{
    if (textDelegate && [textDelegate respondsTo:@selector(textWillChange:)])
      return [textDelegate textWillChange:sender];
    else
      return NO;
}

- textDidChange:sender
{
    if (textDelegate && [textDelegate respondsTo:@selector(textDidChange:)])
      return [textDelegate textDidChange:sender];
    else
      return self;
}

- (BOOL)textWillEnd:sender
{
    if (textDelegate && [textDelegate respondsTo:@selector(textWillEnd:)])
      return [textDelegate textWillEnd:sender];
    else
      return NO;
}

- textDidEnd:sender endChar:(unsigned short)whyEnd
{
    id retCode = self;

    if (textDelegate && [textDelegate 
			 respondsTo:@selector(textDidEnd:endChar:)])
    {
	retCode = [textDelegate textDidEnd:sender endChar:whyEnd];

	[self updateDate:self];
    }

    return retCode;
}

- textDidGetKeys:sender isEmpty:(BOOL)flag
{
    if (textDelegate && [textDelegate 
			 respondsTo:@selector(textDidGetKeys:isEmpty:)])
      return [textDelegate textDidGetKeys:sender isEmpty:flag];
    else
      return self;
}

@end

@implementation MiscCalendarView (Dragging)

- (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
{
    NXRect	aRect;
    NXPoint	mouseLocation;

    if (([sender draggingSourceOperationMask] & NX_DragOperationCopy) &&
	colorDragAndDrop)
    {
	NXSetRect(&aRect, DEFAULT_MATRIX_XPOS+1, DEFAULT_HEADER_YPOS,
		  DEFAULT_MATRIX_WIDTH, 0);

	if (displayDOWHeader)
	  NX_HEIGHT(&aRect) += DEFAULT_HEADER_HEIGHT;

	if (displayMonthAndYearHeader)
	  NX_HEIGHT(&aRect) += DEFAULT_HEADER_HEIGHT;

	if (displayMonthHeader)
	  NX_HEIGHT(&aRect) += DEFAULT_HEADER_HEIGHT;

	if (displayYearHeader)
	  NX_HEIGHT(&aRect) += DEFAULT_HEADER_HEIGHT;

	mouseLocation = [sender draggingLocation];

	[self convertPoint:&mouseLocation toView:self];

	if (NXMouseInRect(&mouseLocation, &aRect, NO))
	  return NX_DragOperationCopy;
    }

    return NX_DragOperationNone;
}

- draggingExited:(id <NXDraggingInfo>)sender
{
    return self;
}

- (NXDragOperation)draggingUpdated:(id <NXDraggingInfo>)sender
{
    return [self draggingEntered:sender];
}

- (BOOL)prepareForDragOperation:(id <NXDraggingInfo>)sender
{
    if ([self draggingEntered:sender] == NX_DragOperationCopy)
      return YES;

    return NO;
}

- (BOOL)performDragOperation:(id <NXDraggingInfo>)sender
{
    NXColor	color;
    NXPoint	mouseLocation;

    color = NXReadColorFromPasteboard([sender draggingPasteboard]);
    
    mouseLocation = [sender draggingLocation];
    
    if ([self _acceptColor:color at:&mouseLocation])
      return YES;

    return NO;
}

- concludeDragOperation:(id <NXDraggingInfo>)sender
{
    return self;
}

@end

/* Satan made me do it! Okay, well, maybe not, but jeez NeXT, if you */
/* would just publish some of these APIs so us lowly end-user programmer */
/* could do all the neat things you get to do, ugly rule-breaking hacks */
/* like this would not happen. sigh.... Anyway, these two methods allow */
/* my view to accept drag-and-drop color in IB, just like textfield does. */
/* Thanks to the person who wrote ClassDump and Robert Nicholson for */
/* helping me find these... */

@implementation MiscCalendarView (IBView)

- depositIBColor:(NXColor)aColor at:(NXPoint *)aPoint
{
    NXPoint	mouseLocation;
    int		cellColorSelection;

    mouseLocation.x = aPoint->x;
    mouseLocation.y = aPoint->y;

    if (![self _acceptColor:aColor at:&mouseLocation])
    {
	cellColorSelection = [[[[NXApp getInspectorFor:self owner:self] 
			      cellColorMatrix] selectedCell] tag];

	[self setColorOf:cellColorSelection to:aColor];
    }

    return self;
}

- acceptsIBColorAt:(NXPoint *)aPoint;
{
    return self;
}

@end

@implementation MiscCalendarView (Private)

- _createTextFieldWithFrame:(NXRect *)textFrame 
  stringValue:(const char *)aString
{
    TextField	*textField;

    textField = [[TextField alloc] initFrame:textFrame];
    [textField setEditable:NO];
    [textField setBezeled:NO];
    [textField setBordered:NO];
    [textField setSelectable:NO];
    [textField setAlignment:NX_CENTERED];
    [textField setBackgroundGray:-1];
    
    [textField setAutosizing:NX_MINYMARGINSIZABLE | 
     NX_MAXYMARGINSIZABLE | NX_WIDTHSIZABLE | NX_HEIGHTSIZABLE];

    [Font setUserFont:helvetica_11];

    [textField setFont:system_11_bold];

    [textField setStringValue:aString];

    return textField;
}

- _acceptColor:(NXColor)aColor at:(NXPoint *)aPoint
{
    TextField	*textField;
    NXPoint	mouseLocation;
    NXRect	aRect;
    int 	i;

    mouseLocation.x = aPoint->x;
    mouseLocation.y = aPoint->y;

    textField = nil;

    [self convertPoint:&mouseLocation fromView:[self superview]];

    if (displayDOWHeader)
      for (i = 0; i < [dowSubviewList count]; i++)
      {
	  [[dowSubviewList objectAt:i] getFrame:&aRect];

	  if (NXMouseInRect(&mouseLocation, &aRect, NO))
	  {
	      textField = [dowSubviewList objectAt:i];
	      i = [dowSubviewList count];
	  }
      }

    if (!textField && displayMonthAndYearHeader)
    {
	[inlineMonthSubview getFrame:&aRect];

	  if (NXMouseInRect(&mouseLocation, &aRect, NO))
	    textField = inlineMonthSubview;
    }

    if (!textField && displayMonthAndYearHeader)
    {
	[inlineYearSubview getFrame:&aRect];

	  if (NXMouseInRect(&mouseLocation, &aRect, NO))
	    textField = inlineYearSubview;
    }

    if (!textField && displayMonthHeader)
    {
	[monthSubview getFrame:&aRect];

	  if (NXMouseInRect(&mouseLocation, &aRect, NO))
	    textField = monthSubview;
    }

    if (!textField && displayYearHeader)
    {
	[yearSubview getFrame:&aRect];

	  if (NXMouseInRect(&mouseLocation, &aRect, NO))
	    textField = yearSubview;
    }

    if (textField)
    {
	[textField setTextColor:aColor];
	return self;
    }
    
    return nil;
}

@end

