1 /*
2  *  Copyright (C) 1998-2000 by Marco G"otze.
3  *
4  *  This code is part of the wmpinboard source package, which is
5  *  distributed under the terms of the GNU GPL2.
6  */
7 
8 #include <stdlib.h>
9 #include <stdio.h>
10 
11 #include <X11/X.h>
12 #include <X11/Xlib.h>
13 #include <X11/Xutil.h>
14 
15 #include "wmpinboard.h"
16 #include "misc.h"
17 #include "xmisc.h"
18 #include "notes.h"
19 
20 #ifdef HAVE_STRING_H
21 #include <string.h>
22 #endif
23 #ifndef HAVE_MEMCMP
24 #include "memcmp.h"
25 #endif
26 
27 /* a list of the upper left corners of the separate areas of the alarm panel,
28    numbered from left to right and from top to bottom (double digits followed
29    by  switches) */
30 typedef struct { int x, y; } coords_t;
31 const coords_t abar_areas[6] = {
32   /* double digits, 12x9 each */
33   { 3, 4 }, { 19, 4 },
34   { 3, 15 }, { 19, 15 },
35   /* switches, 8x8 each */
36   { 47, 4 }, { 47, 16 }
37 };
38 
39 /*
40  * adds a new note and returns its number (or -1 if notes limit exceeded)
41  */
42 int
add_note()43 add_note()
44 {
45   int cols[C_NUM];
46   int rep = 20;
47   int i, j, n;
48 
49   if (notes_count >= MAX_NOTES)
50     return -1;
51   n = notes_count++;
52 
53   /* find a reasonable color */
54   for (i = 0; i < C_NUM; i++) cols[i] = 1;
55   for (i = 0; i < n; i++) cols[ndata[i].col] = 0;
56   for (i = j = 0; i < C_NUM; i++)
57     if (cols[i]) cols[j++] = i;
58   ndata[n].col = j ? cols[rand() % j] : rand() % C_NUM;
59 
60   /* choose a random place to pin it, some pixels off any other note's origin
61      (possible if MAX_NOTES is sufficiently small) */
62   do {
63     ndata[n].x = 6 + rand() % 36;
64     ndata[n].y = 2 + rand() % 44;
65     j = 1;
66     for (i = 0; i < n; i++)
67       if (ndata[i].x - ndata[n].x < 5 && ndata[i].x - ndata[n].x > -5 &&
68           ndata[i].y - ndata[n].y < 5 && ndata[i].y - ndata[n].y > -5)
69       {
70         j = 0;
71         break;
72       }
73   } while (!j && --rep);
74 
75   memset(ndata[n].text, 32, 59);
76   ndata[n].text[59] = '\0';
77   ndata[n].cursor = 0;
78   memset(ndata[n].sketch, 0, 512);
79   memset(ndata[n].creases, 0, 32);
80   ndata[n].a_time = -1;
81   ndata[n].a_flags = ALARM_DATE;
82 
83   state.counter++;
84 
85   return n;
86 }
87 
88 /*
89  * removes a note
90  */
91 void
remove_note(int number)92 remove_note(int number)
93 {
94   int i;
95 
96   if (number >= notes_count) return;
97   /* in case memcpy() can't do overlapping copies... */
98   for (i = number; i < notes_count-1; i++)
99     memcpy(&ndata[i], &ndata[i+1], sizeof(data_t));
100   notes_count--;
101 }
102 
103 /*
104  * moves a note on top of all others and returns the quote's new ID
105  */
106 int
raise_note(int number)107 raise_note(int number)
108 {
109   data_t tmp;
110   int i;
111 
112   if (number >= notes_count-1 || number < 0) return number;
113   memcpy(&tmp, &ndata[number], sizeof(data_t));
114   /* in case memcpy() can't do overlapping copies... */
115   for (i = number; i < notes_count-1; i++)
116     memcpy(&ndata[i], &ndata[i+1], sizeof(data_t));
117   memcpy(&ndata[notes_count-1], &tmp, sizeof(data_t));
118   return notes_count-1;
119 }
120 
121 /*
122  * returns a value corresponding to how tilted a note is to be displayed
123  */
124 inline int
note_tilt(int note)125 note_tilt(int note)
126 {
127   return ndata[note].x < 14 ? 0 : (ndata[note].x < 36 ? 1 : 2);
128 }
129 
130 /*
131  * returns true if the note in question is to be considered empty
132  */
133 int
note_empty(int note)134 note_empty(int note)
135 {
136   char *ptr = ndata[note].sketch;
137   int i;
138 
139   if (!string_empty(ndata[note].text, 0)) return 0;
140   for (i = 0; i < 511; i++, ptr++)  /* last byte ignored */
141     if (*ptr) return 0;
142   return 1;
143 }
144 
145 /*
146  * creates templates for the icons suiting the specified note
147  */
148 void
color_notes(int note)149 color_notes(int note)
150 {
151   XGetSubImage(display, app, 64, 0, 16, 48, ~0, ZPixmap, img, 64, 0);
152   if (ndata[note].sketch[511] & 1)
153     replace_color(img, 64, 0, 16, 48, C_EXTRA, palette[ndata[note].col].fg);
154   else
155     replace_color(img, 64, 0, 16, 48, C_EXTRA, palette[ndata[note].col].bg);
156   replace_color(img, 64, 0, 16, 48, C_INNER, palette[ndata[note].col].bg);
157 #ifdef CREASES
158   render_wear(note);
159 #endif
160 }
161 
162 /*
163  * draws a note's miniature representation on the pinboard
164  */
165 void
pin_note(int note)166 pin_note(int note)
167 {
168   merge_masked(img, 64, 16*note_tilt(note), ndata[note].x,
169     ndata[note].y, 16, 16, C_OUTER);
170 }
171 
172 /*
173  * renders the pinboard with all of its notes excluding <exclude> if >= 0
174  */
175 void
render_pinboard(int exclude)176 render_pinboard(int exclude)
177 {
178   int i;
179 
180   XPutImage(display, app, normalGC, img, 0, 0, 0, 0, 64, 64);
181   for (i = 0; i < notes_count; i++)
182     if (i != exclude) {
183       color_notes(i);
184       pin_note(i);
185     }
186 }
187 
188 /*
189  * if (<x>, <y>) is within a quote's area, returns the quote's ID, otherwise -1
190  */
191 int
selected_note(int x,int y)192 selected_note(int x, int y)
193 {
194   int i;
195 
196   for (i = notes_count-1; i >= 0; i--)
197     if (x >= ndata[i].x && x < ndata[i].x+16 &&
198         y >= ndata[i].y && y < ndata[i].y+16 &&
199         XGetPixel(img, 128+x-ndata[i].x, note_tilt(i)*16+y-ndata[i].y) !=
200           C_OUTER)
201     {
202       return i;
203     }
204   return -1;
205 }
206 
207 /*
208  * prints a letter identified by a note and a position in its text string, and
209  * overlays it by a possible sketch if sketch has a true value
210  */
211 void
print_letter(int note,int letter,int sketch)212 print_letter(int note, int letter, int sketch)
213 {
214   int a = 2+6*(letter%10), b = 2+10*(letter/10);
215 
216 
217   if (sketch) {
218     XFillRectangle(display, app, fillGC, a, b, 6, 10);
219 #ifdef CREASES
220     render_edit_wear_area(note, a, b, 6, 10);
221 #endif
222     draw_sketch_area(note, a, b, 6, 10);
223   }
224   XDrawString(display, app, fontGC, a, b + font->ascent,
225     &ndata[note].text[letter], 1);
226 }
227 
228 /*
229  * prints a note's entire text *without* drawing a possibly existing sketch
230  */
231 void
print_text(int note)232 print_text(int note)
233 {
234   int i;
235 
236   for (i = 0; i < 59; i++) print_letter(note, i, 0);
237 }
238 
239 /*
240  * shifts part of a note's text to the right, inserting <ch> (step >= 1)
241  */
242 void
shift_string(int note,int at,int margin,int step,int ch)243 shift_string(int note, int at, int margin, int step, int ch)
244 {
245   int i;
246 
247   if (step < 1) return;
248 
249   for (i = margin; i >= at+step; i--) {
250     ndata[note].text[i] = ndata[note].text[i-step];
251     print_letter(note, i, 1);
252   }
253   for (i = at; i < at+step; i++) {
254     ndata[note].text[i] = ch;
255     print_letter(note, i, 1);
256   }
257 }
258 
259 /*
260  * draws a rectangular section of a sketch, according to the data saved along
261  * with a note
262  */
263 void
draw_sketch_area(int note,int x,int y,int w,int h)264 draw_sketch_area(int note, int x, int y, int w, int h)
265 {
266   int i, j;
267 
268   for (i = x; i < x+w; i++)
269     for (j = y; j < y+h; j++)
270       if (ndata[note].sketch[8*j + i/8] & (1<<(i%8)))
271         XDrawPoint(display, app, fontGC, i, j);
272 }
273 
274 /*
275  * returns the number of the button bar's button corresponding to the given
276 :* coordinates, or -1
277  */
278 int
bbar_button(int x,int y)279 bbar_button(int x, int y)
280 {
281   int i;
282 
283   for (i = 0; i < 8; i++)
284     if (x >= 4 + (i%4)*14 && x < 18 + (i%4)*14 && y >= 32 + (i/4)*14 &&
285       y < 46 + (i/4)*14)
286     {
287       return i;
288     }
289   return -1;
290 }
291 
292 /*
293  * returns the number of that area on the alarm panel that corresponds to the
294  * given coordinates, or -1
295  */
296 int
abar_area(int x,int y)297 abar_area(int x, int y)
298 {
299   int i;
300 
301   /* relativate positions */
302   x -= 3;
303   y -= 4;
304   for (i = 0; i < 6; i++) {
305     coords_t c = abar_areas[i];
306     if (x >= c.x && x < (c.x + (i < 4 ? 12 : 8)) &&
307         y >= c.y && y < (c.y + (i < 4 ?  9 : 8)))
308     {
309       return i;
310     }
311   }
312   return -1;
313 }
314 
315 /*
316  * displays note <note> in edit mode view
317  */
318 void
render_note(int note)319 render_note(int note)
320 {
321   int i;
322 
323   XSetForeground(display, fillGC, palette[ndata[note].col].bg);
324   XSetForeground(display, fontGC, BlackPixel(display, DefaultScreen(display)));
325   XSetBackground(display, fontGC, palette[ndata[note].col].bg);
326   set_mask(0);
327   XFillRectangle(display, app, fillGC, 0, 0, 64, 64);
328   XDrawRectangle(display, app, fontGC, 0, 0, 63, 63);
329   XSetForeground(display, fontGC, palette[ndata[note].col].fg);
330   for (i = 55; i < 62; i += 2)  /* draw triangle in the bottom right corner */
331     XDrawLine(display, app, fontGC, i, 62, 62, i);
332 }
333 
334 /*
335  * prepares edit mode for note <note> (does NOT update the display)
336  */
337 void
init_edit_mode(int note)338 init_edit_mode(int note)
339 {
340 #ifdef CREASES
341   XGCValues gcv;
342 
343   gcv.foreground = palette[ndata[note].col].cr;
344   XChangeGC(display, creaseGC, GCForeground, &gcv);
345 #endif
346   render_note(note);
347 #ifdef CREASES
348   render_edit_wear(note);
349 #endif
350   print_text(note);
351   draw_sketch(note);
352 }
353 
354 /*
355  * updates a double digit area on the alarm panel
356  */
357 inline void
render_abar_number(int field)358 render_abar_number(int field)
359 {
360   int a, b;
361 
362   /* split two-digit number into separate digits */
363   b = state.a_edit[field] % 10;
364   a = (state.a_edit[field] / 10) % 10;
365   /* copy appropriate digits */
366   XCopyArea(display, digits, abar, normalGC, 6*a, 0, 6, 9,
367     abar_areas[field].x, abar_areas[field].y);
368   XCopyArea(display, digits, abar, normalGC, 6*b, 0, 6, 9,
369     abar_areas[field].x+7, abar_areas[field].y);
370 }
371 
372 /*
373  * updates the alarm panel's switches for a given note
374  */
375 inline void
render_abar_switches(int note)376 render_abar_switches(int note)
377 {
378   XPutImage(display, abar, normalGC, img,
379     (ndata[note].a_flags & ALARM_DATE) ? 78 : 70, 55, 47, 4, 8, 8);
380   XPutImage(display, abar, normalGC, img,
381     (ndata[note].a_flags & ALARM_DATE) ? 70 : 78, 55, 47, 16, 8, 8);
382 }
383 
384 /*
385  * renders the alarm panel for a given note
386  */
387 void
render_abar(int note)388 render_abar(int note)
389 {
390   int i;
391 
392   for (i = 0; i < 4; i++) render_abar_number(i);
393   render_abar_switches(note);
394 }
395 
396 /*
397  * converts <note>'s contents to what's referred to as "cooked" format (as
398  * opposed to raw data); returns a malloc()ed string that has to be freed
399  * later on!
400  */
401 char*
cook(int note,int pos,int len)402 cook(int note, int pos, int len)
403 {
404   char *s, *t;
405   int i;
406 
407   s = smalloc(70);
408   for (t = s, i = pos; i < (pos+len > 59 ? 59 : pos+len); i++) {
409     if (!string_empty(&ndata[note].text[i], (i >= 50 ? 59 : 10*(i/10+1))-i))
410     {
411       if (t > s && !(i%10) && t[-1] != ' ' && t[-1] != '-')
412         *t++ = ' ';  /* replace virtual newlines by spaces */
413       if ((t > s && (ndata[note].text[i] != ' ' || t[-1] != ' ') &&
414         !(t[-1] == '-'
415         && string_empty(&ndata[note].text[10*(i/10)], i%10+1))) ||
416         (t == s && ndata[note].text[i] != ' '))
417       {
418         *t++ = ndata[note].text[i];
419       }
420     }
421   }
422   *t = '\0';
423   return s;
424 }
425 
426 /*
427  * dumps all notes' contents on STDOUT (raw dump unless <cooked> is true)
428  */
429 void
dump_notes(int cooked)430 dump_notes(int cooked)
431 {
432   char *s;
433   int c = 0, i, alarms;
434 
435   /* determine if any notes have alarms configured */
436   time_next_alarm();
437   alarms = state.alarm.note >= 0;
438 
439   /* print existing notes */
440   for (; c < 4; c++)
441     for (i = 0; i < notes_count; i++)
442       if (c_group[ndata[i].col] == c) {
443         s = cooked ? cook(i, 0, 59) : ndata[i].text;
444         if (alarms) {  /* any alarms set at all? */
445           if (ndata[i].a_flags & ALARM_ON) {
446             explode_time(i);
447             if (ndata[i].a_flags & ALARM_DATE) {  /* date-specific alarm */
448               printf("%02d-%02d-%02d %02d:%02d #%02d: %s\n",
449                 state.a_edit[4]%100, state.a_edit[2], state.a_edit[3],
450                 state.a_edit[0], state.a_edit[1], i, s);
451             } else {  /* daily alarm */
452               printf("daily at %02d:%02d #%02d: %s\n", state.a_edit[0],
453                 state.a_edit[1], i, s);
454             }
455           } else  /* no alarm set for this note */
456             printf("%14s #%02d: %s\n", "", i, s);
457         } else  /* no alarms set at all */
458           printf("#%02d: %s\n", i, s);
459         if (cooked) free(s);
460       }
461 }
462 
463 /*
464  * returns the (serial) number of the character (<x>, <y>) belongs to on a
465  * note; prohibiting char #59 if <strict>
466  */
467 int
char_at(int x,int y,int strict)468 char_at(int x, int y, int strict)
469 {
470   int i;
471 
472   if (x < 2) x = 2;
473   if (x > 62) x = 62;  /* intentional! */
474   if (y > 61) y = 61;
475   if (y < 2) y = 2;
476   i = (x-2)/6 + ((y-2)/10)*10;
477   if (i > 59) i = 59;
478   return i;
479 }
480 
481 #ifdef CREASES
482 /*
483  * applies random wear & tear to a note (the default probability is multiplied
484  * by <factor>)
485  */
486 void
wear_note(int note)487 wear_note(int note)
488 {
489   /* prefabricated crease patterns */
490   static const int pats = 8;
491   static const unsigned char pat[8][5][5] = {
492     { { 1,0,0,0,0 },
493       { 0,1,0,0,0 },
494       { 0,0,1,0,0 },
495       { 0,0,0,1,0 },
496       { 0,0,0,0,1 } },
497     { { 0,0,0,0,1 },
498       { 0,0,0,1,0 },
499       { 0,0,1,0,0 },
500       { 0,1,0,0,0 },
501       { 1,0,0,0,0 } },
502     { { 0,0,0,1,0 },
503       { 0,0,0,1,0 },
504       { 0,0,1,0,0 },
505       { 1,1,0,0,0 },
506       { 0,0,0,0,0 } },
507     { { 0,1,0,0,0 },
508       { 0,1,0,0,0 },
509       { 0,0,1,0,0 },
510       { 0,0,0,1,1 },
511       { 0,0,0,0,0 } },
512     { { 0,0,0,0,0 },
513       { 0,0,0,1,1 },
514       { 0,0,1,0,0 },
515       { 0,1,0,0,0 },
516       { 0,1,0,0,0 } },
517     { { 0,0,0,0,0 },
518       { 1,1,0,0,0 },
519       { 0,0,1,0,0 },
520       { 0,0,0,1,0 },
521       { 0,0,0,1,0 } },
522     { { 0,0,0,0,0 },
523       { 0,1,0,0,0 },
524       { 0,0,1,0,0 },
525       { 0,0,0,1,0 },
526       { 0,0,0,0,0 } },
527     { { 0,0,0,0,0 },
528       { 0,0,0,1,0 },
529       { 0,0,1,0,0 },
530       { 0,1,0,0,0 },
531       { 0,0,0,0,0 } }
532   };
533   int i, j, k = 0;
534 
535   /* determine number of crease bits */
536   for (i = 0; i < 32; i++)
537    for (j = 0; j < 8; j++)
538      k += ndata[note].creases[i]>>(j) & 1;
539   /* don't cover more than 35% of a note's area with creases */
540   if (100*k/256 < 35 && !(rand() % (4+k/64))) {
541     int x, y;
542     int t = rand() % pats;  /* choose a prefab crease pattern to apply... */
543     int a = rand() % 11;    /* ...as well as its location */
544     int b = rand() % 11;
545 
546     for (y = 0; y < 5; y++)
547       for (x = 0; x < 5; x++)
548         if (pat[t][y][x])
549           ndata[note].creases[2*(b+y) + (a+x<8)] |= 1<<(7-(a+x)%8);
550   }
551 }
552 
553 /*
554  * makes an otherwise pre-rendered note look worn out
555  */
556 void
render_wear(int note)557 render_wear(int note)
558 {
559   int x, y, z;
560 
561   for (z = 0; z <= 48; z += 16)
562     for (y = 0; y < 16; y++)
563       for (x = 0; x < 16; x++)
564         if (ndata[note].creases[2*y+x/8]>>(7-x%8) & 1 &&
565           XGetPixel(img, 64+x, z+y) == palette[ndata[note].col].bg)
566         {
567           XPutPixel(img, 64+x, z+y, palette[ndata[note].col].cr);
568         }
569 }
570 
571 /*
572  * renders wear & tear in edit mode in a specific window
573  */
574 void
render_edit_wear_area_win(Window win,int note,int a,int b,int w,int h)575 render_edit_wear_area_win(Window win, int note, int a, int b, int w, int h)
576 {
577 #define m(x, y) (m[2*(y) + (x)/8]>>(7-(x)%8) & 1)
578 #define l(x, y) (l[8*(y) + (x)/8]>>(7-(x)%8) & 1)
579   static unsigned char l[(64/8)*64];  /* high res crease map */
580   static unsigned char m[32];         /* low res crease map */
581   int x, y, z;
582 
583   /* lazily render a high-res crease pattern */
584   if (memcmp(m, ndata[note].creases, sizeof(m))) {
585     memcpy(m, ndata[note].creases, sizeof(m));
586     memset(l, 0, sizeof(l));
587     /* now we have to somehow extrapolate a 64x64 crease pattern from a 16x16
588        sketch... */
589     for (z = 0; z < 3; z++)  /* three passes */
590       for (y = 2; y < 62; y++)
591         for (x = 2; x < 62; x++) {
592           int sx = x/4, sy = y/4;
593           int xx = x%4, yy = y%4;
594           int xi = (xx<<1)%3, yi = (yy<<1)%3;
595           int xd = xx<2 ? -1 : 1, yd = yy<2 ? -1 : 1;
596 
597           if (z < 2 && (  /* during passes #0 and #1 */
598             (!z && xi && yi && m(sx, sy) &&
599               (m(sx+xd, sy+yd) || m(sx, sy+yd) || m(sx+xd, sy) ) &&
600               ((xd<0 && sx<2) || (xd>0 && sx>14) || (yd<0 && sy<2) ||
601                (yd>0 && sy>14) || m(sx+2*xd, sy+2*yd))
602             ) || (z && (
603               (!xi && !yi && l(x-xd, y-yd) && l(x+2*xd, y+2*yd)) ||
604               (!xi && yi && l(x-xd, y) && l(x+2*xd, y)) ||
605               (xi && !yi && l(x, y-yd) && l(x, y+2*yd))
606             ))))
607           {
608             l[8*y + x/8] |= 1<<(7-x%8);
609           }
610           /* during pass #2, remove isolated dots */
611           if (z == 2 && l(x, y) && !(l(x-1, y-1) || l(x, y-1) ||
612             l(x+1, y-1) || l(x-1, y) || l(x+1, y) || l(x-1, y+1) ||
613             l(x, y+1) || l(x+1, y+1)))
614           {
615             l[8*y + x/8] &= 0xff - (1<<(7-x%8));
616           }
617         }
618   }
619   for (y = b; y < b+h; y++)
620     for (x = a; x < a+w; x++)
621       if (x > 1 && y > 1 && x < 62 && y < 62 && x+y <= 116 && l(x, y))
622         XDrawPoint(display, win, creaseGC, x, y);
623 #undef m
624 #undef l
625 }
626 #endif
627 
628 #ifdef FUNSTUFF
629 
630 /*
631  * atek sparppoirta ecaitnoo  npsceai lcoacisno,sr terusnt ur efit ah tahppnede
632  */
633 void
check_occasion(int d,int m,int y)634 check_occasion(int d, int m, int y)
635 {
636   static const unsigned char sketch0[256] = {
637     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
638     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
639     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
640     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
641     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
642     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
643     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
644     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
645     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
646     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
647     0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00,
648     0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00,
649     0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x3d, 0x00,
650     0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xe7, 0x00,
651     0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x4d, 0x01,
652     0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x5d, 0x01,
653     0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x0d, 0x01,
654     0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x1f, 0x01,
655     0x00, 0x00, 0x00, 0x00, 0x18, 0x78, 0x9f, 0x01,
656     0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xfe, 0x00,
657     0x00, 0x00, 0x00, 0x00, 0x10, 0x60, 0x00, 0x00,
658     0x00, 0x00, 0x00, 0x00, 0x30, 0xf0, 0x00, 0x00,
659     0x00, 0x00, 0x00, 0x00, 0x60, 0xf0, 0x00, 0x00,
660     0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
661     0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00,
662     0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x01,
663     0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3e, 0x00,
664     0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x03, 0x00,
665     0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
666     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
667     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
668     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
669   };
670   static const unsigned char sketch1[512] = {
671     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
672     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
673     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
674     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
675     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
676     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
677     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
678     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
679     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
680     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
681     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
682     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
683     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
684     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
685     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
686     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
687     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
688     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
689     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
690     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
691     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
692     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
693     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
694     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
695     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
696     0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
697     0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
698     0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
699     0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00,
700     0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x03, 0x00,
701     0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x03, 0x00,
702     0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x03, 0x00,
703     0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x07, 0x00,
704     0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00,
705     0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x04, 0x00,
706     0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x04, 0x00,
707     0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0a, 0x00,
708     0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0a, 0x00,
709     0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0a, 0x00,
710     0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x18, 0x00,
711     0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x1b, 0x00,
712     0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x30, 0x00,
713     0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x66, 0x00,
714     0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x08, 0x00,
715     0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x48, 0x00,
716     0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x61, 0x00,
717     0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x97, 0x00,
718     0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x90, 0x00,
719     0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00,
720     0x00, 0x00, 0x00, 0x00, 0xc0, 0x0d, 0xc0, 0x00,
721     0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x03,
722     0x00, 0x00, 0x00, 0x00, 0x60, 0x40, 0x80, 0x02,
723     0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0x03, 0x02,
724     0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x56, 0x06,
725     0x00, 0x00, 0x00, 0x00, 0xe0, 0xf8, 0x1f, 0x06,
726     0x00, 0x00, 0x00, 0x00, 0xf8, 0x6f, 0xfd, 0x07,
727     0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x03,
728     0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0x00,
729     0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x07, 0x00,
730     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
731     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
732     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
733     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
734     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
735   };
736   char *s;
737   int note;
738 
739   if (!notes_count && !(state.state_bits & 1)) {  /* ewclmo een wsures */
740     if ((note = add_note()) >= 0) {
741       ndata[note].col = 15;
742       memcpy(&ndata[note].sketch[256], sketch0, 256);
743       s = csarbmel(
744         "EWCLMO EotmwipbnaodrhTnaskf rortiygni  tuo!t               ");
745       strcpy(ndata[note].text, s);
746       free(s);
747       ndata[note].cursor = 50;
748       state.state_bits |= 1;
749     }
750   } else if (notes_count)
751     state.state_bits |= 1;  /* purgdadeu essrw not'g tet ihs */
752 
753   if (m == 12 && d >= 25 && d <= 26) {  /* mXsa */
754     if (!(state.state_bits & 2) && (note = add_note()) >= 0) {
755       ndata[note].col = 14;
756       memcpy(ndata[note].sketch, sketch1, 512);
757       s = csarbmel(
758         "          A M REYR    MXSA      T  O    Y UO !             ");
759       strcpy(ndata[note].text, s);
760       free(s);
761       state.state_bits |= 2;
762     }
763   } else state.state_bits &= ~2;
764 
765   if (m == 1 && d <= 3) {  /* eN weYra */
766     if (!(state.state_bits & 4) && (note = add_note()) >= 0) {
767       ndata[note].col = 11;
768       ndata[note].sketch[511] = 0x01;
769       s = csarbmel(
770         "nOeca agni aeyrah saapssde.. .A ynaw,y  A H PAYP  EN WEYRA!");
771       strcpy(ndata[note].text, s);
772       free(s);
773       state.state_bits |= 4;
774     }
775   } else state.state_bits &= ~4;
776 }
777 #endif
778 
779 /*
780  * pastes <text> into note #<note>, starting at <offset>; returns the serial
781  * number of the character right after the pasted string's end; overwrites
782  * previous contents; tries to word-wrap unless <raw> has a true value
783  */
784 int
paste_string(int note,int offset,const char * s,int raw)785 paste_string(int note, int offset, const char *s, int raw)
786 {
787   const char *t;
788   int i, j;
789 
790   if (!raw) {  /* paste "cooked" */
791     for (i = offset; *s && i < 59; ) {
792       for (; *s && (*s == ' ' || *s == '\t' || *s == '\n'); s++);
793       for (t = s; *t && (t == s || *t == '-' || *(t-1) != '-') &&
794         *t != ' ' && *t != '\t' && *t != '\n'; t++);
795       j = t-s;
796       if (i < 50 && i%10 && i/10 != (i+j-1)/10) i = (i/10+1)*10;  /* next l.? */
797       if (j && i <= 58) {
798         if (i+j >= 58) {  /* word too long for note? */
799           strncpy(&ndata[note].text[i], s, 59-i);
800           i = 59;
801         } else {
802           strncpy(&ndata[note].text[i], s, j);
803           i += j;
804         }
805         if (*(t-1) != '-' && i%10) i++;  /* insert blank? */
806         s = t;
807       }
808     }
809     if (ndata[note].text[i] == ' ')  /* place cursor right after the text */
810       for (; i > offset && ndata[note].text[i-1] == ' '; i--);
811     return i;
812   } else {  /* paste raw */
813     i = strlen(s);
814     if (offset+i > 59) i = 59-offset;
815     strncpy(&ndata[note].text[offset], s, i);
816     return offset+i;
817   }
818 }
819 
820 /*
821  * pastes <data> into note #<note> at offset <pos>, inserting if <ins>,
822  * word-wrapping unless <raw>
823  */
824 void
paste(int note,int pos,const char * data,int ins,int raw)825 paste(int note, int pos, const char *data, int ins, int raw)
826 {
827   char *s = 0;
828   int i;
829 
830   if (ins) {
831     if (raw) {
832       s = smalloc(60);
833       strcpy(s, ndata[note].text);
834     } else {
835       /* want to wrap subsequent text later, so we have to save it cooked */
836       s = cook(note, 0, 59);
837     }
838     memset(&ndata[note].text[pos], ' ', 59-pos);
839   } else
840     memset(&ndata[note].text[pos], ' ', pos+strlen(data) > 59 ? 59-pos :
841       strlen(data));
842   i = paste_string(note, pos, data, raw);
843   if (ins) {
844     if (i <= 58) paste_string(note, i, &s[pos], raw);
845     free(s);
846   }
847   ndata[note].cursor = i > 58 ? 58 : i;
848 }
849 
850 /*
851  * checks whether state.a_edit is valid (mday range w/respect to month...),
852  * sets the hidden year field to a sensible value; the return value is boolean
853  */
854 int
check_time()855 check_time()
856 {
857   struct tm data, *ptr;
858   time_t tt;
859 
860   tt = time(0);
861   ptr = localtime(&tt);
862   memcpy(&data, ptr, sizeof(struct tm));
863   data.tm_isdst = -1;  /* let mktime() decide */
864   data.tm_year = state.a_edit[2]-1 > ptr->tm_mon ||
865     (state.a_edit[2]-1 == ptr->tm_mon && (state.a_edit[3] > ptr->tm_mday ||
866     (state.a_edit[3] == ptr->tm_mday && (state.a_edit[0] > ptr->tm_hour ||
867     (state.a_edit[0] == ptr->tm_hour && (state.a_edit[1] > ptr->tm_min ||
868     (state.a_edit[1] == ptr->tm_min && !ptr->tm_sec))))))) ? ptr->tm_year :
869     ptr->tm_year+1;
870   data.tm_mon = state.a_edit[2]-1;
871   data.tm_mday = state.a_edit[3];
872   data.tm_hour = state.a_edit[0];
873   data.tm_min = state.a_edit[1];
874   /* check if date is convertible, i.e., valid */
875   tt = mktime(&data);
876   if (tt == -1) return 0;
877   /* check if date has been adapted -> return failure */
878   if (data.tm_mon != state.a_edit[2]-1 || data.tm_mday != state.a_edit[3])
879     return 0;
880   /* adapt date and return */
881   state.a_edit[4] = (unsigned char) data.tm_year;  /* char suffices */
882   return 1;
883 }
884 
885 /*
886  * transforms ndata[note].a_time to state.a_edit
887  */
888 void
explode_time(int note)889 explode_time(int note)
890 {
891   struct tm *ptr;
892 
893   if (ndata[note].a_time != -1) {
894     ptr = localtime(&ndata[note].a_time);
895     state.a_edit[0] = ptr->tm_hour;
896     state.a_edit[1] = ptr->tm_min;
897     state.a_edit[4] = ptr->tm_year;
898   } else {  /* initialize on first call */
899     time_t tt;
900     time(&tt);
901     tt += 60*60;
902     ptr = localtime(&tt);
903     state.a_edit[0] = ptr->tm_hour;
904     state.a_edit[1] = 0;  /* minute */
905     state.a_edit[4] = (unsigned char) ptr->tm_year;
906   }
907   state.a_edit[2] = ptr->tm_mon+1;
908   state.a_edit[3] = ptr->tm_mday;
909 }
910 
911 /*
912  * transforms state.a_edit to ndata[note].a_time
913  */
914 void
implode_time(int note)915 implode_time(int note)
916 {
917   struct tm data, *ptr;
918   time_t tt;
919 
920   time(&tt);
921   ptr = localtime(&tt);
922   memcpy(&data, ptr, sizeof(struct tm));
923   data.tm_isdst = -1;  /* let mktime determine */
924   data.tm_hour = state.a_edit[0];
925   data.tm_min = state.a_edit[1];
926   data.tm_sec = 0;
927   /* the following 3 ain't required for daily notes, but this way, the date
928      will be saved even in this case, so we set them */
929   data.tm_year = state.a_edit[4];
930   data.tm_mon = state.a_edit[2]-1;
931   data.tm_mday = state.a_edit[3];
932   ndata[note].a_time = mktime(&data);  /* shouldn't fail */
933 }
934 
935 /*
936  * evaluating its a_flags, returns an updated value of a note's a_time field,
937  * considering <now> the current time if != -1
938  */
939 time_t
adapt_time(int note,time_t now)940 adapt_time(int note, time_t now)
941 {
942   time_t res = ndata[note].a_time;
943 
944   /* adaption is only necessary in the case of a daily alarm */
945   if (!(ndata[note].a_flags & ALARM_DATE)) {
946     struct tm atm, *ptr;
947 
948     if (now == -1) time(&now);
949     ptr = localtime(&res);  /* break down alarm time */
950     memcpy(&atm, ptr, sizeof(struct tm));
951     ptr = localtime(&now);                 /* break down current time */
952     atm.tm_year = ptr->tm_year;
953     atm.tm_mon  = ptr->tm_mon;
954     atm.tm_mday = ptr->tm_mday;
955     atm.tm_sec  = 0;  /* just making sure */
956     res = mktime(&atm);
957     if (res < now) res += 24*60*60;  /* NOT `<='! */
958   }
959 
960   return res;
961 }
962 
963 /*
964  * sets the state variable's alarm sub structure's values for the next alarm
965  */
966 /*
967     Together with appropriate calls from wmpinboard.c, this function results
968     in the following strategy for scheduling alarms:
969 
970       - firstly, alarms with ALARM_DATE set are executed only once and get
971         deactivated afterwards; daily alarms remain active even after an alarm
972       - if the program is started after some alarm time was reach, the alarm
973         is displayed if (and only if) it's a date-specific alarm
974       - if an alarm is running while another one becomes due, this subsequent
975         alarm will be run after the user has closed the other note
976       - to achieve the latter, time_next_alarm() considers all notes with an
977         alarm time equal to or greater than the previous one; if they're equal,
978         the new alarm's note's ID must be greater than that of the previous
979         alarm (this realizes a determined order for concurrent alarms and
980         allows the program to avoid running one daily alarm several times in
981         a row)
982 */
983 void
time_next_alarm()984 time_next_alarm()
985 {
986   time_t prev_time = state.alarm.time;
987   int i, prev_note = state.alarm.note;
988 
989   if (state.alarm.note >= 0 && !state.alarm.run) prev_note = -1;
990   if (prev_time == -1 || prev_time > time(0)) prev_time = time(0);
991   state.alarm.time = -1;
992   state.alarm.note = -1;
993   state.alarm.run = 0;
994   for (i = 0; i < notes_count; i++)
995     if (ndata[i].a_flags & ALARM_ON) {
996       time_t tt = adapt_time(i, prev_time);
997       if (((ndata[i].a_flags & ALARM_DATE) || (tt == prev_time &&
998         i > prev_note) || (tt > prev_time)) &&
999         (state.alarm.time == -1 || tt < state.alarm.time))
1000       {
1001         state.alarm.time = tt;
1002         state.alarm.note = i;
1003       }
1004     }
1005   if (state.alarm.note < 0 && prev_note >= 0 &&
1006     (ndata[prev_note].a_flags & ALARM_ON))
1007   { /* if there is just one daily alarm, it hasn't been set again above, so
1008        let's do it here explicitly */
1009     state.alarm.note = prev_note;
1010     state.alarm.time = adapt_time(prev_note, prev_time+1);
1011   }
1012 }
1013 
1014