source: trunk/athena/lib/Xj/DClock.c @ 20591

Revision 20591, 12.5 KB checked in by ghudson, 20 years ago (diff)
Fix the default date format so that "2001" doesn't show up as "201".
Line 
1/*
2 * $Id: DClock.c,v 1.4 2004-07-24 22:41:33 ghudson Exp $
3 *
4 * Copyright 1990, 1991 by the Massachusetts Institute of Technology.
5 *
6 * For copying and distribution information, please see the file
7 * <mit-copyright.h>.
8 *
9 */
10
11#if  (!defined(lint))  &&  (!defined(SABER))
12static char *rcsid =
13"$Id: DClock.c,v 1.4 2004-07-24 22:41:33 ghudson Exp $";
14#endif
15
16#include "mit-copyright.h"
17#include <stdio.h>
18#include <X11/Xos.h>            /* needed for <time.h> or <sys/time.h> */
19#include <ctype.h>
20#include "Jets.h"
21#include "DClock.h"
22
23
24static char *wday[] =
25{
26  "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
27};
28
29static char *month[] =
30{
31  "January", "February", "March", "April", "May", "June", "July",
32  "August", "September", "October", "November", "December",
33};
34
35#define DEF_FMT "%.3w %.3n %2D %02e:%02m:%02s %C%02Y"
36#define offset(field) XjOffset(DClockJet,field)
37
38static XjResource resources[] = {
39  { XjNx, XjCX, XjRInt, sizeof(int),
40      offset(core.x), XjRString, XjInheritValue },
41  { XjNy, XjCY, XjRInt, sizeof(int),
42      offset(core.y), XjRString, XjInheritValue },
43  { XjNwidth, XjCWidth, XjRInt, sizeof(int),
44      offset(core.width), XjRString, XjInheritValue },
45  { XjNheight, XjCHeight, XjRInt, sizeof(int),
46      offset(core.height), XjRString, XjInheritValue },
47  { XjNcenterY, XjCCenter, XjRBoolean, sizeof(Boolean),
48      offset(dClock.centerY), XjRBoolean, (caddr_t) False },
49  { XjNjustify, XjCJustify, XjRJustify, sizeof(int),
50      offset(dClock.justify), XjRString, XjCenterJustify },
51  { XjNpadding, XjCPadding, XjRInt, sizeof(int),
52      offset(dClock.padding), XjRString, "0" }, 
53  { XjNforeground, XjCForeground, XjRColor, sizeof(int),
54      offset(dClock.foreground), XjRString, XjDefaultForeground },
55  { XjNbackground, XjCBackground, XjRColor, sizeof(int),
56      offset(dClock.background), XjRString, XjDefaultBackground },
57  { XjNreverseVideo, XjCReverseVideo, XjRBoolean, sizeof(Boolean),
58      offset(dClock.reverseVideo), XjRBoolean, (caddr_t) False },
59  { XjNfont, XjCFont, XjRFontStruct, sizeof(XFontStruct *),
60      offset(dClock.font), XjRString, XjDefaultFont },
61  { XjNformat, XjCFormat, XjRString, sizeof(char *),
62      offset(dClock.format[0]), XjRString, DEF_FMT },
63  { XjNformat2, XjCFormat, XjRString, sizeof(char *),
64      offset(dClock.format[1]), XjRString, DEF_FMT },
65  { XjNupdate, XjCInterval, XjRInt, sizeof(int),
66      offset(dClock.update), XjRString, "1" },
67  { XjNblinkColons, XjCBlink, XjRBoolean, sizeof(Boolean),
68      offset(dClock.blink_colons), XjRBoolean, (caddr_t) False },
69  { XjNtimeOffset, XjCTimeOffset, XjRInt, sizeof(int),
70      offset(dClock.timeOffset), XjRString, "0" },
71};
72
73#undef offset
74
75static void wakeup(), expose(), realize(), querySize(),
76  move(), resize(), destroy();
77static char *get_label();
78static Boolean event_handler();
79
80DClockClassRec dClockClassRec = {
81  {
82    /* class name */            "DigitalClock",
83    /* jet size   */            sizeof(DClockRec),
84    /* classInitialize */       NULL,
85    /* classInitialized? */     1,
86    /* initialize */            NULL,
87    /* prerealize */            NULL,
88    /* realize */               realize,
89    /* event */                 event_handler,
90    /* expose */                expose,
91    /* querySize */             querySize,
92    /* move */                  move,
93    /* resize */                resize,
94    /* destroy */               destroy,
95    /* resources */             resources,
96    /* number of 'em */         XjNumber(resources)
97  }
98};
99
100JetClass dClockJetClass = (JetClass)&dClockClassRec;
101
102
103/*
104 * Things are currently broken screenwise.
105 * It will be fun to fix later. :)
106 */
107static void realize(me)
108     DClockJet me;
109{
110  unsigned long valuemask;
111  XGCValues values;
112  int w, len;
113
114  me->dClock.current_fmt = w = 1;
115  len = strlen(get_label(me)) + 15;
116  me->dClock.current_fmt = w = 0;
117  len = MAX(len, strlen(get_label(me)) + 15);
118
119/*
120 *  Add in some extra.  9 is the difference between the longest
121 *  {month,day}name and the shortest of each. Add in some more for extra
122 *  security.
123 *
124 *  This will actually give us a rectangle that will be too big, but
125 *  better too big than too small.
126 */
127
128  w = len * (me->dClock.font->max_bounds.rbearing
129             - me->dClock.font->min_bounds.lbearing);
130  me->dClock.pmap_ht = (me->dClock.font->max_bounds.ascent
131                        + me->dClock.font->max_bounds.descent);
132
133  me->dClock.pmap = XjCreatePixmap(me->core.display, me->core.window,
134                                   w, me->dClock.pmap_ht,
135                                   DefaultDepth(me->core.display,
136                                             DefaultScreen(me->core.display)));
137
138  me->dClock.colons_on = False;
139
140  if (me->dClock.reverseVideo)
141    {
142      int temp;
143
144      temp = me->dClock.foreground;
145      me->dClock.foreground = me->dClock.background;
146      me->dClock.background = temp;
147    }
148
149  values.foreground = me->dClock.foreground;
150  values.background = me->dClock.background;
151  values.font = me->dClock.font->fid;
152  values.graphics_exposures = False;
153  valuemask = ( GCForeground | GCBackground | GCFont
154               | GCGraphicsExposures );
155
156  me->dClock.gc = XjCreateGC(me->core.display,
157                             me->core.window,
158                             valuemask,
159                             &values);
160  XCopyGC(me->core.display, DefaultGC(me->core.display,
161                                      DefaultScreen(me->core.display)),
162          GCFunction, me->dClock.gc);
163
164  values.foreground = me->dClock.background;
165  values.function = GXcopy;
166  values.graphics_exposures = False;
167  valuemask = GCForeground | GCFunction | GCGraphicsExposures;
168
169  me->dClock.gc_bkgnd = XjCreateGC(me->core.display,
170                                   me->core.window,
171                                   valuemask,
172                                   &values);
173
174  me->dClock.timerid = XjAddWakeup(wakeup, me, 1000 * me->dClock.update);
175
176  XjRegisterWindow(me->core.window, (Jet) me);
177  XjSelectInput(me->core.display, me->core.window, ButtonPressMask);
178}
179
180
181static void querySize(me, size)
182     DClockJet me;
183     XjSize *size;
184{
185  char *label;
186
187  label = get_label(me);
188  size->width = XTextWidth(me->dClock.font, label, strlen(label));
189
190  size->height = me->dClock.font->ascent + me->dClock.font->descent;
191}
192
193
194static void move(me, x, y)
195     DClockJet me;
196     int x, y;
197{
198  me->core.x = x;
199  me->core.y = y;
200}
201
202
203static void resize(me, size)
204     DClockJet me;
205     XjSize *size;
206{
207  if (me->core.width == size->width
208      && me->core.height == size->height)
209    return;
210
211  if (me->dClock.gc_bkgnd != NULL)
212    XFillRectangle(me->core.display, me->core.window,
213                   me->dClock.gc_bkgnd,
214                   me->core.x, me->core.y,
215                   me->core.width, me->core.height);
216
217  me->core.width = size->width;
218  me->core.height = size->height;
219}
220
221
222static void destroy(me)
223     DClockJet me;
224{
225  XjFreeGC(me->core.display, me->dClock.gc);
226  XjFreeGC(me->core.display, me->dClock.gc_bkgnd);
227
228  (void)XjRemoveWakeup(me->dClock.timerid);
229}
230
231
232static int draw(me)
233     DClockJet me;
234{
235/*   struct timeval now; */
236  char *label;
237  int len, w, max_w;
238  int x, y;
239  static int old_w = 0;
240  int start = 0;
241
242  label = get_label(me);
243  len = strlen(label);
244
245  w = XTextWidth(me->dClock.font, label, len);
246  max_w = MAX(w, old_w);
247
248  switch(me->dClock.justify)
249    {
250    case Left:
251      start = 0;
252      x = me->core.x + me->dClock.padding;
253      break;
254
255    case Right:
256      start = MAX(0, old_w - w);
257      x = me->core.x + (me->core.width - max_w - me->dClock.padding);
258      break;
259
260    default:                            /* Center, default */
261      start = (MAX(0, old_w - w))/2;
262      x = me->core.x + (me->core.width - max_w) / 2;
263      break;
264    }
265
266  y = me->core.y;
267  if (me->dClock.centerY)
268    y += (me->core.height -
269          (me->dClock.font->ascent + me->dClock.font->descent)) / 2;
270
271#if 1
272/*
273 *  Here we do some software double-buffering of the clock so it doesn't
274 *  flicker.  If you don't like it - tough.  Or use the part after the
275 *  #else below.
276 */
277  XFillRectangle(me->core.display, me->dClock.pmap, me->dClock.gc_bkgnd,
278                 0, 0, max_w, me->dClock.pmap_ht);
279
280  if (me->dClock.colons_on)
281    {
282      char tmp[1024];
283      int new_x = start;
284      int width, len2 = 0;
285      char *ptr, *ptr2;
286
287      strcpy(tmp, label);
288      for (ptr = ptr2 = tmp; (ptr2 = strchr(ptr, ':')) != 0; /*empty*/)
289        {
290          len2 = (int) (ptr2 - ptr);
291          *ptr2++ = '\0';
292          width = XTextWidth(me->dClock.font, ptr, len2);
293          XDrawString(me->core.display, me->dClock.pmap,
294                      me->dClock.gc,
295                      new_x, me->dClock.font->ascent,
296                      ptr, len2);
297          new_x += (XTextWidth(me->dClock.font, ":", 1) + width);
298          ptr = ptr2;
299        }
300      XDrawString(me->core.display, me->dClock.pmap,
301                  me->dClock.gc,
302                  new_x, me->dClock.font->ascent,
303                  ptr, strlen(ptr));
304
305      me->dClock.colons_on = False;
306    }
307  else
308    {
309      XDrawString(me->core.display, me->dClock.pmap,
310                  me->dClock.gc,
311                  start, me->dClock.font->ascent,
312                  label, len);
313
314      if (me->dClock.blink_colons)
315        me->dClock.colons_on = True;
316    }
317
318  XCopyArea(me->core.display, me->dClock.pmap, me->core.window,
319            me->dClock.gc,
320            0, 0, max_w, me->dClock.pmap_ht, x, y);
321#else
322  XDrawImageString(me->core.display, me->core.window,
323                   me->dClock.gc,
324                   x, y + me->dClock.font->ascent,
325                   label, len);
326#endif
327 
328  old_w = w;
329
330/*  if (me->dClock.update > 1) */
331    return(1000 * me->dClock.update);
332
333/*  gettimeofday(&now, NULL);
334  return(1000 - (now.tv_usec / 1000));
335*/
336}
337
338static void wakeup(me, id)
339     DClockJet me;
340     int id;
341{
342  me->dClock.timerid = XjAddWakeup(wakeup, me, draw(me));
343}
344
345static void expose(me, event)
346     DClockJet me;
347     XEvent *event;
348{
349  (void) draw(me);
350}
351
352
353static char *get_label(me)
354     DClockJet me;
355{
356  static char outbuf[BUFSIZ];
357  struct tm *tp;
358  struct timeval tv;
359  struct timezone tz;
360  char *in, *out, *start, fmbuf[10];
361
362  gettimeofday(&tv, &tz);
363  tv.tv_sec += me->dClock.timeOffset;
364
365  tp = localtime((time_t *) &tv.tv_sec);
366
367  in = me->dClock.format[me->dClock.current_fmt];
368  for (out = outbuf; *in; in++)
369    {
370      if (*in != '%')
371        *out++ = *in;
372      else
373        {
374          start = in++;
375          while (!isalpha(*in))
376            in++;
377          sprintf(fmbuf, "%.*s%c", in - start, start,
378                  strchr("MDCYehmso", *in) ? 'd' : 's');
379          switch (*in)
380            {
381            case 'w': /* 'w' = weekday name (Monday - Sunday) */
382              sprintf(out, fmbuf, wday[tp->tm_wday]);
383              break;
384            case 'n': /* 'n' = monthname (January - December) */
385            case 't': /* backward compatibility */
386              sprintf(out, fmbuf, month[tp->tm_mon]);
387              break;
388            case 'M': /* 'M' = Month number (1 - 12) */
389              sprintf(out, fmbuf, tp->tm_mon + 1);
390              break;
391            case 'D': /* 'D' = Day of month (1 - {28,30,31}) */
392              sprintf(out, fmbuf, tp->tm_mday);
393              break;
394            case 'C': /* 'C' = Century (all but last 2 dig of year - ex: 19) */
395              sprintf(out, fmbuf, (tp->tm_year + 1900) / 100);
396              break;
397            case 'Y': /* 'Y' = Year number (no century - ex: 91) */
398              sprintf(out, fmbuf, tp->tm_year % 100);
399              break;
400            case 'e': /* 'e' = european time (24 hour time) */
401              sprintf(out, fmbuf, tp->tm_hour);
402              break;
403            case 'h': /* 'h' = hour number (1 - 12) */
404              sprintf(out, fmbuf, (tp->tm_hour > 12) ? (tp->tm_hour - 12) :
405                      ((tp->tm_hour == 0) ? 12 : tp->tm_hour));
406              break;
407            case 'm': /* 'm' = minute (0 - 60) */
408              sprintf(out, fmbuf, tp->tm_min);
409              break;
410            case 's': /* 's' = seconds (0 - 60) */
411              sprintf(out, fmbuf, tp->tm_sec);
412              break;
413            case 'A': /* 'A' = AMPM ("AM" or "PM") */
414              sprintf(out, fmbuf, (tp->tm_hour > 11) ? "PM" : "AM");
415              break;
416            case 'a': /* 'a' = ampm ("am" or "pm") */
417              sprintf(out, fmbuf, (tp->tm_hour > 11) ? "pm" : "am");
418              break;
419            case 'z': /* 'z' = timeZone (ex: EDT, EST, CDT, CST, etc.) */
420#if defined(HAVE_TM_ZONE)
421              sprintf(out, fmbuf, tp->tm_zone);
422#elif defined(HAVE_TZNAME)
423              sprintf(out, fmbuf, tzname[tp->tm_isdst]);
424#endif
425              break;
426            case 'o': /* 'o' = day Of year (1 - 366) */
427              sprintf(out, fmbuf, tp->tm_yday + 1);
428              break;
429            default:
430              sprintf(out, fmbuf, "?");
431              break;
432            }
433          out += strlen(out);
434        }
435    }
436
437  *out = '\0';
438  return(outbuf);
439}
440
441static Boolean event_handler(me, event)
442     DClockJet me;
443     XEvent *event;
444{
445  if (event->type == ButtonPress)
446    {
447      int x, y, w, h, len;
448      char *label;
449
450      label = get_label(me);
451      len = strlen(label);
452      w = XTextWidth(me->dClock.font, label, len);
453      h = me->dClock.pmap_ht;
454      switch(me->dClock.justify)
455        {
456        case Left:
457          x = me->core.x + me->dClock.padding;
458          break;
459        case Right:
460          x = me->core.x + (me->core.width - w - me->dClock.padding);
461          break;
462        default:                        /* Center, default */
463          x = me->core.x + (me->core.width - w) / 2;
464          break;
465        }
466      y = me->core.y;
467      if (me->dClock.centerY)
468        y += (me->core.height -
469              (me->dClock.font->ascent + me->dClock.font->descent)) / 2;
470
471      if (event->xbutton.x > x
472          && event->xbutton.x < x+w
473          && event->xbutton.y > y
474          && event->xbutton.y < y+h)
475        {
476          me->dClock.current_fmt = !me->dClock.current_fmt;
477          (void) draw(me);
478          return True;
479        }
480    }
481
482  return False;
483}
Note: See TracBrowser for help on using the repository browser.