/* ClockView.m, a simple clock view 
 * Author: Ali T. Ozer, NeXT Developer Support Group
 * Created: May 26, 1989 (for version 0.9)
 * Modified: June 14 and Aug 14, 1989 (for version 1.0)
 * Redesigned for 2.0 by Julie Zelenski, NeXT Developer Support Group
 * Adapted by Mai Nguyen for this mini-example. Basically, only the digital
 * view of the clock is being used.
 *
 * Subclass of view to implement a simple clock. This view is pretty generic 
 * and can probably be added to any program. 
 * You may freely copy, distribute and reuse the code in this example.  
 * NeXT disclaims any warranty of any kind, expressed or implied, as to its 
 * fitness for any particular use.
 */

#import "ClockView.h"
#import "Clock.h"	// PSwrap routines
#import <appkit/Application.h>
#import <appkit/Button.h>
#import <appkit/Window.h>
#import <appkit/Font.h>
#import <objc/NXStringTable.h>
#import <string.h>
#import <sys/time.h>	
#import <dpsclient/wraps.h>	
#import <appkit/nextstd.h>

@implementation ClockView:View

		
/* ShowTime() is the timed entry function called by the
 * timed entry mechanism. It first writes the time out on the face 
 * of the clock, and then reinstalls the timed entry if the clock is 
 * not showing the seconds. If the seconds are being shown, no need to 
 * reinstall the timed entry; a second skipped here and then won't matter. 
 * If minutes are being shown, we want to make sure that the minute jumps 
 * at the next top of the minute, regardless of how long it took to service 
 * the timed entry.
 */
void ShowTime (teNum, now, clock) 
DPSTimedEntry teNum;
double now;
id clock;
{
    [clock display];
	[[clock stopTimedEntry] startTimedEntry:NO];    
    
}



 
- initFrame:(const NXRect *)frameRect
/* initFrame for newly created view, initializes the various paramaters. 
 * The constants above determine the lengths of the clock hands.
 */
{
    [super initFrame:frameRect];
    cacheWindow = [Window newContent:frameRect 
                               style:NX_PLAINSTYLE
			     backing:NX_RETAINED 
			  buttonMask:0 
			       defer:NO];    
    littleFont = [Font newFont:"Helvetica" size:12 style:0
    			matrix:NX_IDENTITYMATRIX];
    mediumFont = [Font newFont:"Times-Roman" size:14 style:0
    			matrix:NX_IDENTITYMATRIX];
    bigFont = [Font newFont:"Times-Roman" size:24 style:0
    			matrix:NX_IDENTITYMATRIX];

    /* Set the default state (NO seconds, no date) */
    
    showSeconds = NO;
    center.x = bounds.size.width/2.0;
    center.y = bounds.size.height/2.0 + [mediumFont pointSize]/2.0;
    radius = MIN(center.x,center.y-[mediumFont pointSize]);
    needRedraw = YES;
    
    /* Start the time entry. YES indicates that this is the first time */
    [self startTimedEntry:YES];
    [self display];
    return self;
}


- free
/* Good idea to get rid of the timed entry while freeing... 
 */
{
    [cacheWindow free];
    [self stopTimedEntry];
    return [super free];
}




/* PRIVATE METHODS */

- drawFace;
/* drawFace draws the clock face image in the offscreen window.  This
 * offscreen cache is composited on screen and then the hands, shadow,
 * whatever is drawn on top of the face for the current time. Just
 * erase the background before redrawing the digital time.
 */
{
    
    [[cacheWindow contentView] lockFocus];

    PSsetgray (NX_LTGRAY);
    NXRectFill (&bounds);	// Erase background    
    [[cacheWindow contentView] unlockFocus]; 
    needRedraw = NO;
    return self;
}

- drawDigital:(struct tm *)time;
/* drawDigital draws the time and date for the digital clock face
 */
{
    int hour;
    char timeString[10];
   
    
    hour = fmod(time->tm_hour,12); /* get us off military time */
    if (!hour) hour = 12;  /* if noon or midnight */
    if (showSeconds)
        sprintf(timeString,"%d:%.2d:%.2d",hour,
    					time->tm_min,
    					time->tm_sec);
    else 
        sprintf(timeString,"%d:%.2d", hour,time->tm_min);
    [mediumFont set];
    PSWcenterShow(center.x,center.y-8.0,timeString,NX_BLACK);
    return self;
}




- drawSelf:(NXRect *)rects :(int)rectCount
/* Draws face and hands of clock. If needRedraw is YES, a parameter has 
 * changed and the face must be redrawn in the offscreen window. 
 * Otherwise, the image in cacheWindow is copied into the bounds of the 
 * view, and a routine is called to display the current date and time
 */ 
{	
    struct tm *localTime;
    struct timeval currentTime;

    if (needRedraw) [self drawFace];
    PScomposite(0.0, 0.0, bounds.size.width, bounds.size.height,
		[cacheWindow gState], bounds.origin.x, bounds.origin.y, 
		NX_COPY);

    gettimeofday (&currentTime, NULL);
    localTime = localtime (&(currentTime.tv_sec));
	[self drawDigital:localTime];
    return self;
}


- startTimedEntry:(BOOL)fireASAP
/* startTimedEntry will install the timed entry. If fireASAP is YES, the
 * timed entry is set to fire off as soon as possible (this would be the case
 * at the start of the program, for instance). If fireASAP is NO, then the
 * timed entry is set to fire off in one second (if seconds are being shown)
 * or at the top of the next minute (in anytime between 0 and 60 seconds).
 */
{
    double fireIn;

    if (fireASAP) fireIn = 0.0;		  // Fire as soon as possible!
    else if (showSeconds) fireIn = 1.0;	  // Fire in a second (good enough)
    else {
        struct timeval currentTime;
    	gettimeofday (&currentTime, NULL);
	fireIn = 60.0 - (currentTime.tv_sec % 60);  // Top of the minute
    }
	
    teNum = DPSAddTimedEntry(fireIn, &ShowTime, self, NX_MODALRESPTHRESHOLD);
    return self;
}

- stopTimedEntry
/* Removes the clock timed entry.
 */
{
    if (teNum)
         DPSRemoveTimedEntry (teNum);
    teNum = (DPSTimedEntry)0;
    return self;
}


- sizeTo:(NXCoord)w :(NXCoord)h
/* Overriding sizeTo:: allows us to resize and fix up the clock whenever
 * the size is changed.  Figure the radius of the clock (based on the size
 * of the view) and then redraw
 */
{
    [super sizeTo:w :h];
    center.x = bounds.size.width/2.0;
    center.y = bounds.size.height/2.0 + [mediumFont pointSize]/2.0;
    radius = MIN(center.x, center.y-[mediumFont pointSize]);
    needRedraw = YES;
    return self;
}



@end