home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.0 / NeXTSTEP3.0.iso / NextDeveloper / Examples / AppKit / BusyBox / ClockView.m < prev    next >
Text File  |  1992-05-28  |  11KB  |  373 lines

  1. /* 
  2.  * ClockView.m, a simple clock view 
  3.  * Author: Ali T. Ozer, NeXT Developer Support Group
  4.  * Created: May 26, 1989 (for version 0.9)
  5.  * Modified: June 14 and Aug 14, 1989 (for version 1.0)
  6.  * Redesigned for 2.0 by Julie Zelenski, NeXT Developer Support
  7.  * Modified some for 3.0 by Ali Ozer, AppKit Group, May 28, 1992
  8.  *
  9.  * Subclass of view to implement a simple clock. This view is pretty generic 
  10.  * and can probably be added to any program. The setClockType: method lets 
  11.  * you display an analog, digital, or sundial face.  You have the option of 
  12.  * turning the seconds hand on or off, as well as controlling whether the date 
  13.  * is also displayed.  
  14.  *  
  15.  * You may freely copy, distribute and reuse the code in this example.  
  16.  * NeXT disclaims any warranty of any kind, expressed or implied, as to its 
  17.  * fitness for any particular use.
  18.  */
  19.  
  20. #import <appkit/appkit.h>
  21. #import "ClockView.h"
  22. #import "Clock.h"    // PSwrap routines
  23. #import <objc/NXStringTable.h>
  24. #import <string.h>
  25. #import <sys/time.h>    
  26.  
  27.  
  28. @implementation ClockView:View
  29.  
  30.  
  31. #define PI (double)3.1415926535897
  32.     
  33. #define ANALOG    0    
  34. #define DIGITAL    1
  35. #define SUNDIAL 2
  36.  
  37.  
  38.  
  39. /* ShowTime() is the timed entry function called by the
  40.  * timed entry mechanism. It first writes the time out on the face 
  41.  * of the clock, and then reinstalls the timed entry if the clock is 
  42.  * not showing the seconds. If the se()s are being shown, no need to 
  43.  * reinstall the timed entry; a second skipped here and then won't matter. 
  44.  * If minutes are being shown, we want to make sure that the minute jumps 
  45.  * at the next top of the minute, regardless of how long it took to service 
  46.  * the timed entry.
  47.  */
  48. void ShowTime (teNum, now, clock) 
  49. DPSTimedEntry teNum;
  50. double now;
  51. id clock;
  52. {
  53.     [clock display];    
  54.     if ([clock showSeconds] == NO) [[clock stopTimedEntry] startTimedEntry:NO];
  55. }
  56.  
  57.  
  58.  
  59.  
  60. - initFrame:(const NXRect *)frameRect
  61. /* initFrame for newly created view, initializes the various parameters,
  62.  * grabs some fonts to be used later.
  63.  */
  64. {
  65.     [super initFrame:frameRect];
  66.  
  67.     face = [[NXImage allocFromZone:[self zone]] initSize:&bounds.size];
  68.     [face useDrawMethod:@selector(drawFace:) inObject:self];
  69.  
  70.     littleFont = [Font newFont:"Helvetica" size:12 style:0
  71.                 matrix:NX_IDENTITYMATRIX];
  72.     mediumFont = [Font newFont:"Times-Roman" size:14 style:0
  73.                 matrix:NX_IDENTITYMATRIX];
  74.     bigFont = [Font newFont:"Times-Roman" size:24 style:0
  75.                 matrix:NX_IDENTITYMATRIX];
  76.  
  77.     /* Set the default state (analog face, no seconds, date on) */
  78.     clockType = ANALOG;
  79.     showSeconds = NO;
  80.     showDate = YES;
  81.     center.x = bounds.size.width/2.0;
  82.     center.y = bounds.size.height/2.0 + [mediumFont pointSize]/2.0;
  83.     radius = MIN(center.x,center.y-[mediumFont pointSize]);
  84.     [face recache];
  85.     
  86.     /* Start the time entry. YES indicates that this is the first time */
  87.     [self startTimedEntry:YES];
  88.     [self display];
  89.     return self;
  90. }
  91.  
  92.  
  93. - free
  94. /* Good idea to get rid of the timed entry while freeing... 
  95.  */
  96. {
  97.     [face free];
  98.     [self stopTimedEntry];
  99.     return [super free];
  100. }
  101.  
  102.  
  103. /* SET/GET CLOCK PARAMETERS */
  104.  
  105. - setShowSeconds:(BOOL)newValue
  106. /* setShowSeconds: sets whether or not the seconds hand is shown.
  107.  * The timed entry must be reinstalled whenever this setting is changed
  108.  * as time to the next firing changes.
  109.  */
  110.     showSeconds = newValue;
  111.     [[self stopTimedEntry] startTimedEntry:NO];
  112.     [self display];
  113.     return self;
  114. }
  115.  
  116. - setShowDate:(BOOL)newValue
  117. /* setShowDate: sets whether or not the date is shown.
  118.  */
  119.     showDate = newValue;
  120.     [self display];
  121.     return self;
  122. }
  123.  
  124. - setClockType:(int)newValue
  125. /* setClockType: sets which type of clock is drawn (analog, digital, or 
  126.  * sundial).
  127.  */
  128. {
  129.     clockT(0= newValue;
  130.     [face recache];
  131.     [self display];
  132.     return self;
  133. }
  134.  
  135. - (BOOL)showSeconds 
  136.     return showSeconds; 
  137. }
  138. - (BOOL)showDate
  139.     return showDate; 
  140. }
  141. - (int)clockType
  142. {
  143.     return clockType;
  144. }
  145.  
  146.  
  147. /* TARGET/ACTION METHODS */
  148.  
  149. - changeShowDate:sender;
  150. {
  151.      return [self setShowDate:[[sender selectedCell] state]];
  152. }
  153. - changeShowSeconds:sender
  154. {
  155.     return [self setShowSeconds:[[sender selectedCell] state]];
  156. }
  157. - changeClockType:sender
  158. {
  159.     return [self setClockType:[sender selectedTag]];
  160. }
  161.  
  162. /* String keys used for look-up into string tables */
  163.  
  164. static const char *monthKeys[12] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
  165. static const char *months[12];
  166. static const char *weekKeys[7] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
  167. static const char *weekdays[7];
  168. static const char *romanDays[31] = {"I","II","III","IV","V","VI","VII","VIII","IX","X","XI","XII",
  169. "XIII","XIV","XV","XVI","XVII","XVIII","XIX","XX","XXI","XXII","XXIII",
  170. "XXIV","XXV","XXVI","XXVII","XXVIII","XXIX","XXX", "XXXI"};
  171.  
  172. - setSTable:anObject;
  173. /* Get string values for months, weekdays from NXStringTable, they are stored
  174.  * as static class variables.
  175.  */  
  176. {
  177.     int i;
  178.     
  179.     sTable = anObject;
  180.     for (i = 0; i < 7; i++)
  181.          weekdays[i] = [sTable valueForStringKey:weekKeys[i]];
  182.     for (i = 0; i < 12; i++) 
  183.          months[i] = [sTable valueForStringKey:monthKeys[i]];
  184.     return self;
  185. }
  186.  
  187. /* PRIVATE METHODS */
  188.  
  189. #define HOURRATIO    0.5    /* Hour hand length compared face size */
  190. #define MINUTERATIO    0.85    /* Minute & seconds hands */
  191.  
  192. - drawAnalog:(struct tm *)time;
  193. /* drawAnalog draws the clock hands and date for the analog clock face
  194.  */
  195. {
  196.     int min,hour,sec;   
  197.     char dateString[15];
  198.  
  199.     min  = time->tm_min;
  200.     hour = time->tm_hour; 
  201.     sec  = time->tm_sec;
  202.     
  203.     if (showSeconds) 
  204.         PSWdrawClockHand (center.x,center.y,-6.0 * sec, radius*MINUTERATIO, 
  205.             NX_DKGRAY, 0.0);
  206.     PSWdrawClockHand (center.x,center.y, -(hour+min/60.0) * 30.0, 
  207.             radius*HOURRATIO, NX_BLACK, 1.0);
  208.     PSWdrawClockHand (center.x,center.y,- fmod(min,60.0) * 6.0, 
  209.             radius*MINUTERATIO, NX_BLACK, 1.0);
  210.     if (showDate) {
  211.         [littleFont set];
  212.     sprintf(dateString,"%s %s %d",weekdays[time->tm_wday],
  213.                 months[time->tm_mon],
  214.                 time->tm_mday);
  215.     PSWcenterShow(center.x+1.0,3.0,dateString,NX_WHITE);
  216.     (1enterShow(center.x,3.0,dateString,NX_DKGRAY);
  217.     }
  218.     return self;
  219. }
  220.  
  221. - drawDigital:(struct tm *)time;
  222. /* drawDigital draws the time and date for the digital clock face
  223.  */
  224. {
  225.     int hour;
  226.     char timeString[10];
  227.     char dateString[15];
  228.     
  229.     hour = fmod(time->tm_hour,12); /* get us off military time */
  230.     if (!hour) hour = 12;  /* if noon or midnight */
  231.     if (showSeconds)
  232.         sprintf(timeString,"%d:%.2d:%.2d",hour,
  233.                         time->tm_min,
  234.                         time->tm_sec);
  235.     else 
  236.         sprintf(timeString,"%d:%.2d", hour,time->tm_min);
  237.     [bigFont set];
  238.     PSWcenterShow(center.x,center.y-8.0,timeString,NX_BLACK);
  239.     if (showDate) {
  240.     sprintf(dateString,"%s %s %d",weekdays[time->tm_wday],
  241.                 months[time->tm_mon],
  242.                 time->tm_mday);
  243.     [mediumFont set];
  244.     PSWcenterShow(center.x,center.y-24.0,dateString,NX_BLACK);
  245.     }
  246.     return self;
  247. }
  248.  
  249. #define SHADOWRATIO .95        /* shadow length when compared to radius */
  250. #define MARKERRATIO .15        /* height of marker when compared to radius */
  251.  
  252. - drawSundial:(struct tm *)time;
  253. /* drawSundial draws the shadow and date for the sundial clock face
  254.  */
  255. {   
  256.     float percentOfDay;
  257.     char dateString[15];
  258.     NXPoint edge;
  259.  
  260.     if (showSeconds) 
  261.         PSWdrawSweep (center.x,center.y,-6.0 *time->tm_sec,radius*.75);
  262.     percentOfDay = (time->tm_hour*60 + time->tm_min)/(24.0*60.0);
  263.     edge.x  = sin(percentOfDay*PI*2)*radius*SHADOWRATIO;
  264.     edge.y = cos(percentOfDay*PI*2)*radius*SHADOWRATIO;
  265.     PSWdrawShadow(center.x,center.y,edge.x,edge.y,radius*MARKERRATIO);
  266.     if (showDate) {
  267.     [mediumFont set];
  268.     sprintf(dateString,"%s %s", months[time->tm_mon],
  269.             romanDays[time->tm_mday-1]);
  270.     PSWcenterShow(center.x,12.0,dateString,NX_BLACK);
  271.     }
  272.     return self;
  273. }
  274.  
  275. - drawFace:image
  276. /* drawFace draws the clock face image.  This
  277.  * image is composited on screen and then the hands, shadow,
  278.  * whatever is drawn on top of the face for the current time.
  279.  */
  280. {
  281.     PSsetgray (NX_LTGRAY);
  282.     NXRectFill (&bounds);    // Erase background
  283.     switch (clockType) {
  284.         case ANALOG:     PSWdrawAnalogFace(center.x,center.y,radius);
  285.             break;
  286.     case DIGITAL:     /* digital "face" is just blank */
  287.             break;
  288.     case SUNDIAL:     PSWdrawSundialFace(center.x,center.y, radius);
  289.             break;
  290.     }
  291.     return self;
  292. }
  293.  
  294.  
  295. - drawSelf:(NXRect *)rects :(int)rectCount
  296. /* Draws face and hands of clock.
  297.  * The clock face image(2face is copied into the bounds of the 
  298.  * view, and a routine is called to display the current date and time
  299.  */ 
  300. {    
  301.     struct tm *localTime;
  302.     struct timeval currentTime;
  303.  
  304.     // If recache was called, or if printing, this will first redraw
  305.     // the clock face by calling drawFace:
  306.     [face composite:NX_COPY toPoint:&bounds.origin];
  307.  
  308.     gettimeofday (¤tTime, NULL);
  309.     localTime = localtime (&(currentTime.tv_sec));
  310.     switch (clockType) {
  311.         case ANALOG:     [self drawAnalog:localTime];
  312.             break;
  313.     case DIGITAL:     [self drawDigital:localTime];
  314.             break;
  315.     case SUNDIAL:     [self drawSundial:localTime];
  316.             break;
  317.     }
  318.     return self;
  319. }
  320.  
  321.  
  322. - startTimedEntry:(BOOL)fireASAP
  323. /* startTimedEntry will install the timed entry. If fireASAP is YES, the
  324.  * timed entry is set to fire off as soon as possible (this would be the case
  325.  * at the start of the program, for instance). If fireASAP is NO, then the
  326.  * timed entry is set to fire off in one second (if seconds are being shown)
  327.  * or at the top of the next minute (in anytime between 0 and 60 seconds).
  328.  */
  329. {
  330.     double fireIn;
  331.  
  332.     if (fireASAP) fireIn = 0.0;          // Fire as soon as possible!
  333.     else if (showSeconds) fireIn = 1.0;      // Fire in a second (good enough)
  334.     else {
  335.         struct timeval currentTime;
  336.         gettimeofday (¤tTime, NULL);
  337.     fireIn = 60.0 - (currentTime.tv_sec % 60);  // Top of the minute
  338.     }
  339.     
  340.     teNum = DPSAddTimedEntry(fireIn, &ShowTime, self, NX_MODALRESPTHRESHOLD);
  341.     return self;
  342. }
  343.  
  344. - stopTimedEntry
  345. /* Removes the clock timed entry.
  346.  */
  347. {
  348.     if (teNum)
  349.          DPSRemoveTimedEntry (teNum);
  350.     teNum = (DPSTimedEntry)0;
  351.     return self;
  352. }
  353.  
  354.  
  355. - sizeTo:(NXCoord)w :(NXCoord)h
  356. /* Overriding sizeTo:: allows us to resize and fix up the clock whenever
  357.  * the size is changed.  Figure the radius of the clock (based on the size
  358.  * of the view) and then redraw
  359.  */
  360. {
  361.     [super sizeTo:w :h];
  362.     center.x = bounds.size.width/2.0;
  363.     center.y = bounds.size.height/2.0 + [mediumFont pointSize]/2.0;
  364.     radius = MIN(center.x, center.y-[mediumFont pointSize]);
  365.     [face setSize:&bounds.size];
  366.     return self;
  367. }
  368.  
  369. @end