1 #include <unistd.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <string.h>
5 
6 #include "sunclock.h"
7 #include "langdef.h"
8 
9 extern char *   salloc();
10 extern void     StringReAlloc();
11 
12 extern char             **get_dir_list();
13 extern int              dup_strcmp();
14 extern void             free_dirlist();
15 
16 extern char *           num2str();
17 extern void             processKey();
18 extern int              readVMF();
19 extern void             buildMap();
20 extern int              parseCmdLine();
21 extern void             correctValues();
22 extern void             getFonts();
23 
24 extern void             createGData();
25 extern void             createGCs();
26 extern void             clearNightArea();
27 extern void             drawCities();
28 extern void             drawSunAndMoon();
29 extern void             drawDottedRectangle();
30 
31 extern Display *	dpy;
32 extern int		scr;
33 extern Colormap		cmap0, tmp_cmap;
34 
35 extern int              color_depth;
36 extern int              runlevel;
37 extern int              text_input;
38 
39 extern Atom		wm_delete_window;
40 extern Window		Root, Menu, Filesel, Zoom, Option, Urban;
41 extern Pixmap           zoompix, textpix, rootpix;
42 extern Pixel		black, white;
43 
44 extern Flags            gflags;
45 extern ZoomSettings     gzoom;
46 
47 extern TextEntry        option_entry, urban_entry[5];
48 
49 extern struct Sundata   *Seed;
50 extern struct Sundata   *MenuCaller, *FileselCaller,
51                         *ZoomCaller, *OptionCaller, *UrbanCaller, *RootCaller;
52 extern struct Geometry	ClockGeom, MapGeom;
53 extern struct Geometry  MenuGeom, FileselGeom, ZoomGeom, OptionGeom, UrbanGeom;
54 
55 extern char *	        ProgName;
56 extern char *	        Title;
57 extern char *           widget_type[7];
58 extern char *           EditorCommand;
59 
60 extern char *	        ClassName;
61 extern char *           ClockClassName;
62 extern char *           MapClassName;
63 extern char *           AuxilClassName;
64 
65 extern char *           ExternAction;
66 extern char *           CityInit;
67 
68 extern char *           share_maps_dir;
69 extern char **          dirtable;
70 extern char *           image_dir;
71 extern char *           Help[N_HELP];
72 extern char *           Label[L_END];
73 extern char *           FileselBanner[N_FILESEL];
74 extern char             WeakChars[];
75 
76 extern char             language[4];
77 
78 extern char *           Clock_img_file;
79 extern char *           Map_img_file;
80 extern char *           Zoom_img_file;
81 
82 extern int              city_cat;
83 extern int              *city_spotsizes;
84 extern int              *city_sizelimits;
85 extern int		do_menu, do_filesel, do_zoom, do_option, do_urban;
86 extern int              do_dock, do_sync, do_zoomsync, do_root;
87 
88 extern int              placement;
89 extern int              place_shiftx;
90 extern int              place_shifty;
91 
92 extern int              verbose;
93 extern int		button_pressed;
94 extern int              option_changes;
95 extern int              root_period;
96 
97 extern int              horiz_drift, vert_drift;
98 extern int              label_shift, filesel_shift, zoom_shift;
99 
100 extern int              num_lines, num_table_entries;
101 
102 extern int              zoom_mode, zoom_active;
103 
104 extern char	        menu_lasthint;
105 extern char	        option_lasthint;
106 extern char             zoom_lasthint;
107 extern char             urban_lasthint;
108 
109 extern KeySym	        menu_newhint;
110 extern char	        option_newhint;
111 extern char             zoom_newhint;
112 extern char             urban_newhint;
113 
114 extern long             last_time;
115 extern long             progress_value[6];
116 
117 extern double           darkness;
118 extern unsigned int     adjust_dark;
119 
120 extern int              urban_t[5], urban_x[5], urban_y[5], urban_w[5];
121 
122 int                     areaw, areah;
123 int			urbanh;
124 
125 void shutDown();
126 
127 /* Is it really more secure than plain tmpnam() ?? */
128 void
secure_tmpnam(char * name)129 secure_tmpnam(char *name)
130 {
131     char compl[4];
132     static int order = 0;
133     compl[0] = 'a' + ((order/676)%26);
134     compl[1] = 'a' + ((order/26)%26);
135     compl[2] = 'a' + (order%26);
136     compl[3] = '\0';
137     ++order;
138     sprintf(name, "%s/XXXXXX", P_tmpdir);
139     close(mkstemp(name));
140     unlink(name);
141     strcat(name, compl);
142 }
143 
144 void
showManual()145 showManual()
146 {
147 int i=0, j, k, l, c, cp;
148 char cmd[256];
149 char tmpname[80];
150 struct stat buf;
151 char *ptr, *text;
152 FILE *man;
153 
154      secure_tmpnam(tmpname);
155      strcat(tmpname, "_sunclock.man");
156      sprintf(cmd, "man sunclock > %s", tmpname);
157      system(cmd);
158      man = fopen(tmpname, "r");
159      if (!man) {
160         unlink(tmpname);
161 	return;
162      }
163      ptr = strdup(tmpname);
164      buf.st_mode = 0;
165      stat(ptr, &buf);
166      free(ptr);
167      text = malloc(buf.st_size+2);
168      if (!text) {
169         fclose(man);
170         return;
171      }
172      while ((c=fgetc(man))!=EOF) {
173        iter:
174         if (c=='_') {
175 	   cp = fgetc(man);
176 	   if (cp==EOF) break;
177 	   if (cp==8) continue;
178 	   text[i++] = (char)c;
179 	   c = cp;
180            goto iter;
181 	} else
182 	if (c==8) {
183 	   c = fgetc(man);
184 	   if (c==EOF) break;
185 	} else
186 	   text[i++] = (char)c;
187      }
188      text[i] = '\0';
189      fclose(man);
190      man = fopen(tmpname, "w");
191      if (!man) {
192         free(text);
193         return;
194      }
195      for (j=0; j<i; j++) {
196          if (text[j]=='\n' && !strncmp(text+j+1, "Sunclock-", 6)) {
197 	    k = j;
198 	    while (isspace(text[k])) k--;
199 	    while (text[k]!='\n') k++;
200 	    ptr = strstr(text+j+1, "sunclock(1x)");
201 	    if (ptr) ptr = strstr(ptr+2, "sunclock(1x)");
202 	    if (ptr) {
203                j = ptr-text+1;
204 	       while (text[j]!='\n') j++;
205 	       while (isspace(text[j])) j++;
206 	       while (text[j]!='\n') j--;
207 	       for (l=k; l<j; l++) text[l] = '\0';
208 	    } else
209 	       for (l=k+3; l<j; l++) text[l] = '\0';
210 	 }
211      }
212      for (j=0; j<i; j++) {
213          if (text[j]) fputc(text[j], man);
214      }
215      free(text);
216      fflush(man);
217      fclose(man);
218      chmod(tmpname, 0555);
219      sprintf(cmd, "(%s %s ; rm -f %s ) &",
220 	     EditorCommand, tmpname, tmpname);
221      system(cmd);
222 }
223 
224 int
getState(w)225 getState(w)
226 Window w;
227 {
228 XWindowAttributes xwa;
229    XGetWindowAttributes(dpy, w, &xwa);
230    return xwa.map_state;
231 }
232 
233 int
getPlacement(win,x,y,w,h)234 getPlacement(win, x, y, w, h)
235 Window win;
236 int *x, *y;
237 unsigned int *w, *h;
238 {
239    int xp, yp;
240    unsigned int b, d, n;
241    Window junk, root, parent, *children;
242 
243    XFlush(dpy);
244    XQueryTree(dpy, win, &root, &parent, &children, &n);
245    if (!parent) {
246       fprintf(stderr, "Cannot query window tree!\n");
247       return 1;
248    }
249 
250    if (parent == root) parent = win;
251 
252    XGetGeometry(dpy, parent, &root, x, y, w, h, &b, &d);
253 
254    XTranslateCoordinates(dpy, win, parent, 0, 0, x, y, &junk);
255    if (2*(*x) < *w) *w -= 2*(*x);
256    if ((*x)+(*y) < *h) *h -= (*x) + (*y);
257    XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &junk);
258    XTranslateCoordinates(dpy, parent, root, 0, 0, &xp, &yp, &junk);
259 
260    horiz_drift = *x - xp;
261    vert_drift = *y - yp;
262 /*
263    if (verbose) {
264       fprintf(stderr, "Window placement (%d,%d)\n", *x, *y);
265       fprintf(stderr, "Window drift (%d,%d)\n", horiz_drift, vert_drift);
266    }
267 */
268    return 0;
269 }
270 
271 void
272 checkGeom(Context, bool)
273 struct Sundata * Context;
274 int bool;
275 {
276 	int a, b;
277 
278         a = DisplayWidth(dpy, scr) - Context->geom.width - horiz_drift - 5;
279 	b = DisplayHeight(dpy, scr) - Context->geom.height
280               - Context->hstrip - vert_drift - 5;
281         if (Context->geom.x > a) Context->geom.x = a;
282         if (Context->geom.y > b) Context->geom.y = b;
283 	if (Context->geom.x < 0) Context->geom.x = 5;
284 	if (Context->geom.y < 0) Context->geom.y = 5;
285         if (bool) Context->geom.mask =
286                       XValue | YValue | WidthValue | HeightValue;
287 }
288 
289 void
adjustGeom(Context,which)290 adjustGeom(Context, which)
291 struct Sundata * Context;
292 int which;
293 {
294         int x, y, dx=0, dy=0, diff;
295         unsigned int w, h;
296 
297         if (getPlacement(Context->win, &x, &y, &w, &h)) return;
298 
299 	if (which) {
300 	   diff= MapGeom.height - ClockGeom.height +
301 	         Context->gdata->mapstrip - Context->gdata->clockstrip;
302 	   switch(placement) {
303 	      case CENTER:
304 		 dx = (MapGeom.width - ClockGeom.width)/2;
305 	         dy = diff/2;
306 	         break;
307 	      case NE:
308                  dx = MapGeom.width - ClockGeom.width;
309 	         break;
310 	      case SE:
311                  dx = MapGeom.width - ClockGeom.width;
312 	      case SW:
313 	         dy = diff;
314 	         break;
315 	      default:
316 	         break;
317 	   }
318 	}
319 
320         if (placement) {
321 	    dx = dx - place_shiftx;
322 	    dy = dy - place_shifty;
323 	    if (Context->wintype) {
324 	       dx = -dx;
325 	       dy = -dy;
326 	    }
327 	    if (placement >= CENTER) {
328 	       Context->geom.x = x + dx - horiz_drift;
329 	       Context->geom.y = y + dy - vert_drift;
330 	       checkGeom(Context, 1);
331 	    }
332 	}
333 }
334 
335 /*
336  * Set up stuff the window manager will want to know.  Must be done
337  * before mapping window, but after creating it.
338  */
339 
340 void
setSizeHints(Context,num)341 setSizeHints(Context, num)
342 struct Sundata *                Context;
343 int				num;
344 {
345 	XSizeHints		xsh;
346 	struct Geometry *       Geom = NULL;
347 	Window			win = 0;
348 
349         if (num<=1) {
350 	    if (!Context) return;
351 	    win = Context->win;
352 	    Geom = &Context->geom;
353 	    xsh.flags = PSize | PMinSize;
354 	    if (Geom->mask & (XValue | YValue)) {
355 		xsh.x = Geom->x;
356 		xsh.y = Geom->y;
357                 xsh.flags |= USPosition;
358 	    }
359 	    xsh.width = Geom->width;
360 	    xsh.height = Geom->height + Context->hstrip;
361 	    if (do_dock && Context==Seed) {
362               xsh.max_width = xsh.min_width = xsh.width;
363               xsh.max_height = xsh.min_height = xsh.height;
364 	      xsh.flags |= PMaxSize | PMinSize;
365 	    } else {
366               xsh.min_width = Geom->w_mini;
367               xsh.min_height = Geom->h_mini + Context->hstrip;
368 	    }
369 	} else
370 	if (num>=2) {
371 	    xsh.flags = PSize | USPosition | PMinSize;
372 	    if (num==2) {
373 	      win = Menu;
374 	      Geom = &MenuGeom;
375 	      xsh.flags |= PMaxSize;
376 	      xsh.min_width = xsh.max_width = Geom->width;
377               xsh.min_height = xsh.max_height = Geom->height;
378 	    }
379 	    if (num==3) {
380 	      win = Filesel;
381 	      Geom = &FileselGeom;
382 	      xsh.min_width = Geom->w_mini;
383               xsh.min_height = Geom->h_mini;
384 	    }
385 	    if (num==4) {
386 	      win = Zoom;
387 	      Geom = &ZoomGeom;
388 	      xsh.min_width = Geom->w_mini;
389               xsh.min_height = Geom->h_mini;
390 	    }
391 	    if (num==5) {
392 	      win = Option;
393 	      Geom = &OptionGeom;
394 	      xsh.flags |= PMaxSize;
395 	      xsh.min_width = Geom->w_mini;
396               xsh.max_width = 2000;
397               xsh.min_height = xsh.max_height = Geom->h_mini;
398 	    }
399 	    if (num==6) {
400 	      win = Urban;
401 	      Geom = &UrbanGeom;
402  	      xsh.flags |= PMaxSize;
403               xsh.max_width = 2000;
404               xsh.min_width = Geom->w_mini;
405               xsh.min_height = xsh.max_height = Geom->h_mini;
406 	    }
407 	    xsh.x = Geom->x;
408 	    xsh.y = Geom->y;
409 	    xsh.width = Geom->width;
410 	    xsh.height = Geom->height;
411 	}
412 
413 	if (!win) return;
414 	XSetNormalHints(dpy, win, &xsh);
415 }
416 
417 void
setClassHints(win,num)418 setClassHints(win, num)
419 Window win;
420 int    num;
421 {
422         char *titlename, *iconname;
423         XClassHint xch;
424 
425 	titlename = NULL;
426 	if (!Title) StringReAlloc(&Title, ProgName);
427 
428 	if (!ClassName) {
429 	   StringReAlloc(&ClassName, ProgName);
430 	   *ClassName = toupper(*ClassName);
431 	}
432 
433 	if (ClassName && !ClockClassName)
434            StringReAlloc(&ClockClassName, ClassName);
435 
436 	if (ClassName && !MapClassName)
437            StringReAlloc(&MapClassName, ClassName);
438 
439 	if (ClassName && !AuxilClassName)
440            StringReAlloc(&AuxilClassName, ClassName);
441 
442 	if (num == 0)
443 	  xch.res_class = ClockClassName;
444         else
445 	if (num == 1)
446 	  xch.res_class = MapClassName;
447 	else
448 	  xch.res_class = AuxilClassName;
449 
450         xch.res_name = ProgName;
451 
452         XSetClassHint(dpy, win, &xch);
453 
454 	iconname = (char *)
455            salloc((strlen(Title)+strlen(VERSION)+10)*sizeof(char));
456         sprintf(iconname, "%s %s", Title, VERSION);
457         XSetIconName(dpy, win, iconname);
458 	free(iconname);
459 
460 	titlename = (char *)
461            salloc((strlen(Title)+20)*sizeof(char));
462         sprintf(titlename, "%s / %s", Title, widget_type[num]);
463         XStoreName(dpy, win, titlename);
464 	free(titlename);
465 }
466 
467 void
setProtocols(Context,num)468 setProtocols(Context, num)
469 struct Sundata * Context;
470 int				num;
471 {
472 	Window			win = 0;
473 	long                    mask;
474 
475 	mask = FocusChangeMask | VisibilityChangeMask | ExposureMask |
476                StructureNotifyMask |
477                ButtonPressMask | ButtonReleaseMask | KeyPressMask;
478 
479 	if (num>=1)
480 	       mask |= PointerMotionMask;
481 
482         /* use StructureNotifyMask rather than  ResizeRedirectMask
483            to avoid bug in some window mangers... as enlightenment !
484            All events would be:
485 	   for (i=0; i<=24; i++) mask |= 1L<<i;
486          */
487 
488         switch(num) {
489 
490 	   case 0:
491 	   case 1:
492                 if (!Context) return;
493 	        mask |= EnterWindowMask | LeaveWindowMask;
494 		win = Context->win;
495 		break;
496 
497 	   case 2:
498 		win = Menu;
499 		break;
500 
501 	   case 3:
502 	        win = Filesel;
503 		break;
504 
505 	   case 4:
506 	        win = Zoom;
507 		break;
508 
509 	   case 5:
510 	        win = Option;
511 		mask |= KeyReleaseMask;
512 		break;
513 
514 	   case 6:
515 	        win = Urban;
516 		mask |= KeyReleaseMask;
517 		break;
518 
519  	   default:
520 	        break;
521 	}
522 
523 	if (!win) return;
524 
525        	XSelectInput(dpy, win, mask);
526 	XSetWMProtocols(dpy, win, &wm_delete_window, 1);
527 }
528 
529 Window
newWindow(Context,Geom,num)530 newWindow(Context, Geom, num)
531 struct Sundata * Context;
532 struct Geometry * Geom;
533 int num;
534 {
535         Window 		        win = 0;
536 	int                     strip;
537 
538 	if (num<=1) {
539  	   strip = (num)? Context->gdata->mapstrip :
540                           Context->gdata->clockstrip;
541 	   Context->hstrip = strip;
542 	   if (Geom->mask & XNegative)
543 	      Geom->x = DisplayWidth(dpy, scr) - Geom->width + Geom->x;
544 	   if (Geom->mask & YNegative)
545 	      Geom->y = DisplayHeight(dpy, scr) - Geom->height + Geom->y-strip;
546 	} else
547 	   strip = 0;
548 
549 	win = XCreateSimpleWindow(dpy, Root,
550                       Geom->x, Geom->y,
551                       Geom->width, Geom->height + strip, 0,
552 		      black, white);
553         if (win) setClassHints(win, num);
554 	return win;
555 }
556 
getWinParams(win,Context,keys,nkeys,y,width)557 void getWinParams(win, Context, keys, nkeys, y, width)
558 Window win;
559 struct Sundata **Context;
560 char **keys;
561 int *nkeys;
562 int *y;
563 int *width;
564 {
565     *Context = NULL;
566     if (win==Menu) {
567        *Context = MenuCaller;
568        *keys = MenuKey;
569        *nkeys = N_MENU;
570        *y = 0;
571        *width = MenuGeom.width;
572     }
573     if (win==Option) {
574        *Context = OptionCaller;
575        *keys = OptionKey;
576        *nkeys = N_OPTION;
577        *y = OptionGeom.height-2*OptionCaller->gdata->menustrip - 1;
578        *width = OptionGeom.width;
579     }
580     if (win==Zoom) {
581        *Context = ZoomCaller;
582        *keys = ZoomKey;
583        *nkeys = N_ZOOM;
584        *y = areah+64;
585        *width = ZoomGeom.width;
586     }
587     if (win==Urban) {
588        *Context = UrbanCaller;
589        *keys = UrbanKey;
590        *nkeys = N_URBAN;
591        *y = urbanh;
592        *width = UrbanGeom.width;
593     }
594     if (win==Filesel) {
595        *Context = FileselCaller;
596        *keys = WeakChars;
597        *nkeys = 6;
598        *y = 0;
599        *width = FileselGeom.width;
600     }
601 }
602 
603 int
getButton(win,x,y)604 getButton(win, x, y)
605 Window win;
606 int x, y;
607 {
608 struct Sundata * Context=NULL;
609 char *keys=NULL;
610 int nkeys=0;
611 int y1, w0, n, i, j=0, b, d;
612     getWinParams(win, &Context, &keys, &nkeys, &y1, &w0);
613     n = -1;
614     if (y<=y1 || y>=y1+Context->gdata->menustrip) return n;
615     b = 0;
616     d = Context->gdata->charspace;
617     if (win==Filesel) d = 2*d;
618     for (i=0; i<nkeys; i++) {
619 	j = i*d + b;
620 	b += 5*(keys[2*i+1]==';');
621 	if (x>j && x<j+d) {
622 	    n = i;
623 	    break;
624 	}
625     }
626     if (x>j+d+5) n = nkeys;
627     return n;
628 }
629 
630 void
drawBox(win,gc,pix,x1,y1,x2,y2,clicked)631 drawBox(win, gc, pix, x1, y1, x2, y2, clicked)
632 Window win;
633 GC gc;
634 Pixel *pix;
635 int x1, y1, x2, y2;
636 int clicked;
637 {
638     XSetForeground(dpy, gc, pix[BUTTONCOLOR]);
639     XFillRectangle(dpy, win, gc, x1, y1, x2-x1, y2-y1);
640 
641     XSetForeground(dpy, gc, pix[(clicked)?BUTTONFGCOLOR1:BUTTONFGCOLOR3]);
642     XDrawLine(dpy, win, gc, x1, y1, x1, y2);
643     XDrawLine(dpy, win, gc, x1, y1, x2, y1);
644 
645     XSetForeground(dpy, gc, pix[(clicked)?BUTTONFGCOLOR2:BUTTONFGCOLOR4]);
646     XDrawLine(dpy, win, gc, x1+1, y1+1, x1+1, y2-1);
647     XDrawLine(dpy, win, gc, x1+1, y1+1, x2-1, y1+1);
648 
649     XSetForeground(dpy, gc, pix[(clicked)?BUTTONFGCOLOR4:BUTTONFGCOLOR2]);
650     XDrawLine(dpy, win, gc, x1+1, y2-1, x2-1, y2-1);
651     XDrawLine(dpy, win, gc, x2-1, y1+1, x2-1, y2-1);
652 
653     XSetForeground(dpy, gc, pix[(clicked)?BUTTONFGCOLOR3:BUTTONFGCOLOR1]);
654     XDrawLine(dpy, win, gc, x1, y2, x2, y2);
655     XDrawLine(dpy, win, gc, x2, y1, x2, y2);
656 }
657 
658 void
drawButton(win,n,clicked)659 drawButton(win, n, clicked)
660 Window win;
661 int n;
662 int clicked;
663 {
664 int i, j=0, b, d, x1=0, y1=0, x2=0, y2=0, x, y, w0=0;
665 struct Sundata * Context=NULL;
666 char c[2] = { '\0', '\0'};
667 char *s = c;
668 char *keys=NULL;
669 int nkeys=0;
670 int charspace;
671 
672     getWinParams(win, &Context, &keys, &nkeys, &y1, &w0);
673     if (!Context) return;
674     charspace = Context->gdata->charspace;
675     if (win==Filesel) charspace = 2*charspace;
676 
677     b = 0;
678     for (i=0; i<=n; i++) {
679         j = i*charspace + b;
680         if (win!=Filesel)
681 	   b += 5*((i==nkeys-1) || (keys[2*i+1]==';'));
682     }
683     x1 = j;
684     y2 = y1+Context->gdata->menustrip;
685     if (n>=0 && n<nkeys) {
686         if (win==Filesel)
687 	   s = FileselBanner[n];
688 	else {
689            c[0] = keys[2*n];
690 	}
691         x2 = j+charspace-1;
692     } else {
693         if (win==Zoom && n==nkeys+1) {
694 	   c[0] = (zoom_active)?'+':'-';
695            x1 = areah+170,
696            y1 = 21;
697            x2 = x1+charspace;
698            y2 = y1+18;
699         } else {
700            s = Label[L_ESCAPE];
701            x2 = w0 - 1;
702 	}
703     }
704 
705     if (clicked<=-1) {
706        XSetForeground(dpy, Context->gdata->wingc,
707 	              Context->gdata->pixel[(clicked==-2)?
708                                   BUTTONCOLOR:MENUFGCOLOR]);
709        for (y=y1+2; y<=y2-3; y+=y2-y1-5)
710            for (x=x1+4; x<=x2-3; x++)
711               if ((x+y)&1) XDrawPoint(dpy, win,
712 			              Context->gdata->wingc, x, y);
713        for (y=y1+2; y<=y2-3; y++)
714            for (x=x1+4; x<=x2-3; x+=x2-x1-7)
715               if ((x+y)&1) XDrawPoint(dpy, win,
716 			              Context->gdata->wingc, x, y);
717        return;
718     }
719 
720     drawBox(win, Context->gdata->wingc,
721             Context->gdata->pixel, x1, y1, x2, y2, clicked);
722 
723     d = (5*Context->gdata->charspace)/12;
724     XSetBackground(dpy, Context->gdata->wingc,
725 		   Context->gdata->pixel[BUTTONCOLOR]);
726 
727     if (win==Menu && do_dock && (s[0]==c[0]) && index(WeakChars,c[0]))
728         XSetForeground(dpy, Context->gdata->wingc,
729                             Context->gdata->pixel[WEAKCOLOR]);
730     else
731         XSetForeground(dpy, Context->gdata->wingc,
732                             Context->gdata->pixel[MENUFGCOLOR]);
733 
734     XDrawImageString(dpy, win, Context->gdata->wingc,
735                      x1+d,
736                      y1+Context->gdata->font[MENUFONT]->max_bounds.ascent + 4,
737                      s, strlen(s));
738 }
739 
740 int
getNumCmd(key)741 getNumCmd(key)
742 char key;
743 {
744      int i;
745      for (i=0; i<N_HELP; i++)
746 	 if (key==CommandKey[i]) return i;
747      return -1;
748 }
749 
750 void
BasicSettings(Context)751 BasicSettings(Context)
752 Sundata * Context;
753 {
754         XSetBackground(dpy, Context->gdata->wingc, Context->gdata->pixel[MENUBGCOLOR]);
755         XSetForeground(dpy, Context->gdata->wingc, Context->gdata->pixel[MENUFGCOLOR]);
756         XSetFont(dpy, Context->gdata->wingc, Context->gdata->font[MENUFONT]->fid);
757 }
758 
759 void
helpHint(Context,key,hint)760 helpHint(Context, key, hint)
761 Sundata * Context;
762 char key;
763 char *hint;
764 {
765 	int i,j,k,l;
766         char more[128], prog_str[60];
767 
768 	if (key=='\033')
769            sprintf(hint, " %s", Label[L_ESCMENU]);
770 	else
771 	if (key==' ')
772            sprintf(hint, " %s", Label[L_CONTROLS]);
773 	else {
774 	   i = getNumCmd(key);
775 	   if (i>=0)
776               sprintf(hint, " %s", Help[i]);
777 	   else
778 	      sprintf(hint, " %s", Label[L_UNKNOWN]);
779         }
780 
781         more[0] = more[1] = ' ';
782         more[2] = '\0';
783 
784 	if (index("CDS", key))
785 	   sprintf(more, " (%s)", Label[L_DEGREE]);
786 	if (index("ABGJ'", key)) {
787            switch(Context->flags.progress) {
788               case 0: sprintf(prog_str, "1 %s", Label[L_MIN]); break;
789 	      case 1: sprintf(prog_str, "1 %s", Label[L_HOUR]); break;
790 	      case 2: sprintf(prog_str, "1 %s", Label[L_DAY]); break;
791 	      case 3: sprintf(prog_str, "7 %s", Label[L_DAYS]); break;
792 	      case 4: sprintf(prog_str, "30 %s", Label[L_DAYS]); break;
793       	      case 5: sprintf(prog_str, "%ld %s",
794                           progress_value[5], Label[L_SEC]); break;
795            }
796            sprintf(more, " ( %s %s%s   %s %.3f %s )",
797 		  Label[L_PROGRESS],
798                   (key=='A')? "+":((key=='B')? "-":""), prog_str,
799                   Label[L_TIMEJUMP], Context->jump/86400.0,
800 		  Label[L_DAYS]);
801 	}
802 	if (key == 'H') {
803 	    sscanf(RELEASE, "%d %d %d", &i, &j, &k);
804 	    sprintf(more+1, "(%s %s, %d %s %d, %s)",
805                       ProgName, VERSION, i, Month_name[j-1], k, COPYRIGHT);
806 	}
807 	if (key == 'X') {
808 	    more[1] = ':';
809             more[2] = ' ';
810 	    if (ExternAction)
811 	       strncpy(more+3, ExternAction, 123);
812             else
813 	       strcpy(more, "(null)");
814             more[126] = '0';
815 	}
816 
817 	if (key == '=')
818 	    sprintf(more+2, "(%c)", (do_sync)? '+' : '-');
819 	if (key == '[' && do_root <= 0)
820 	    sprintf(more+2, Label[L_ONCE]);
821 	if (key == '[' && do_root >= 1)
822 	    sprintf(more+2, Label[L_PERIODIC], root_period);
823 	if (key == ']' && do_root <= 0)
824 	    sprintf(more+2, Label[L_BLANKSCREEN]);
825 	if (key == ']' && do_root >= 1)
826 	    sprintf(more+2, Label[L_STARRYSKY]);
827         if (more[2])
828 	   strncat(hint, more, 120 - strlen(hint));
829         l = strlen(hint);
830 	if (l<120)
831 	    for (i=l; i<120; i++) hint[i] = ' ';
832 	hint[120] = '\0';
833 }
834 
835 void
showMenuHint(but_pos)836 showMenuHint(but_pos)
837 int but_pos;
838 {
839         static int move_pos = -1;
840         char key;
841 	char hint[128];
842 
843 	if (!do_menu) return;
844 	if (!MenuCaller) return;
845 
846         if (menu_newhint == XK_Shift_L || menu_newhint == XK_Shift_R) return;
847 	key = toupper((char) menu_newhint);
848 
849         if (move_pos>=0) drawButton(Menu, move_pos, -2);
850         if (but_pos>=0) drawButton(Menu, but_pos, -1);
851 	move_pos = but_pos;
852 
853 	if (key == menu_lasthint) return;
854 	helpHint(MenuCaller, key, hint);
855 
856         menu_lasthint = key;
857         BasicSettings(MenuCaller);
858 	XDrawImageString(dpy, Menu, MenuCaller->gdata->wingc, 4,
859               MenuCaller->gdata->font[MENUFONT]->max_bounds.ascent +
860                  MenuCaller->gdata->menustrip + 4,
861               hint, strlen(hint));
862 }
863 
864 void
setupMenu(mode)865 setupMenu(mode)
866 int mode;
867 {
868 	int i, l;
869         static int j=-1;
870         static char c;
871 
872 	if (!do_menu) return;
873 
874         if (j>=0) WeakChars[j] = c;
875 	l = strlen(WeakChars);
876 
877         if (MenuCaller->wintype) {
878 	   if (do_dock) j = 1; else j = 0;
879 	} else {
880 	   j = -1;
881 	   if (do_dock) {
882  	      if (MenuCaller != Seed) j = l-4;
883 	   } else j = l-5;
884 	}
885         if (j>=0) {
886 	   c = WeakChars[j] ;
887            WeakChars[j] = '\0';
888 	}
889 
890         BasicSettings(MenuCaller);
891         XSetWindowColormap(dpy, Menu, MenuCaller->gdata->cmap);
892         XSetWindowBackground(dpy, Menu, MenuCaller->gdata->pixel[MENUBGCOLOR]);
893         XClearArea(dpy, Menu,  0, 0, MenuGeom.width, MenuGeom.height, False);
894 
895 	for (i=0; i<=N_MENU; i++)
896             drawButton(Menu, i, 0);
897         menu_lasthint = '\0';
898 	showMenuHint(-1);
899 }
900 
901 void
PopMenu(Context)902 PopMenu(Context)
903 struct Sundata * Context;
904 {
905 	int    w, h, a, b, x=0, y=0;
906 
907 	do_menu = 1 - do_menu;
908 
909         if (!do_menu)
910 	  {
911 	  XDestroyWindow(dpy, Menu);
912 	  Menu = 0;
913 	  MenuCaller = NULL;
914 	  return;
915 	  }
916 
917         if (!Menu)
918            Menu = newWindow(NULL, &MenuGeom, 2);
919 
920 	XSelectInput(dpy, Menu, 0);
921         MenuCaller = Context;
922 
923         MenuGeom.width = MENU_WIDTH * Context->gdata->menustrip - 6;
924         MenuGeom.height = 2 * Context->gdata->menustrip;
925 
926 	if (!getPlacement(Context->win, &Context->geom.x,
927                                         &Context->geom.y, &w, &h)) {
928 	   x = Context->geom.x + MenuGeom.x - horiz_drift - 5;
929 	   a = Context->geom.y + h + 6;
930 
931            b = Context->geom.y - MenuGeom.height - MenuGeom.y - 2*vert_drift - 28;
932            if (b < TOPTITLEBARHEIGHT ) b = TOPTITLEBARHEIGHT;
933            if (a > (int) DisplayHeight(dpy,scr)
934                    - 2*MenuGeom.height -vert_drift -20)
935               a = b;
936 	   y = (placement<=NE)? a : b;
937 	}
938         setSizeHints(NULL, 2);
939         XMoveWindow(dpy, Menu, x, y);
940         XMapWindow(dpy, Menu);
941         XMoveWindow(dpy, Menu, x, y);
942         XFlush(dpy);
943 	usleep(2*TIMESTEP);
944 	menu_lasthint = ' ';
945 	setupMenu(-1);
946         setProtocols(NULL, 2);
947 }
948 
949 void
processMenuAction(Context,x,y,button,evtype)950 processMenuAction(Context, x, y, button, evtype)
951 Sundata * Context;
952 int x, y, button, evtype;
953 {
954 static int click_pos = -1;
955 int but_pos;
956 KeySym key;
957 	   if (evtype == ButtonRelease && click_pos>=0) {
958 	       drawButton(Menu, click_pos, 0);
959                click_pos = -1;
960 	   }
961            but_pos = getButton(Menu, x, y);
962            if (evtype == ButtonPress && but_pos>=0 && but_pos<=N_MENU) {
963 	       drawButton(Menu, but_pos, 1);
964 	       click_pos = but_pos;
965                return;
966 	   }
967            if (evtype == MotionNotify) {
968 	     if (but_pos >= N_MENU) {
969 		menu_newhint = XK_Escape;
970                 showMenuHint(N_MENU);
971 		return;
972 	     }
973 	     if (but_pos <= -1)
974 		menu_newhint = ' ';
975 	     else
976 	        menu_newhint = MenuKey[2*but_pos];
977              showMenuHint(getNumCmd(menu_newhint));
978              return;
979            }
980 	   if (but_pos==-1) return;
981            if (do_menu && but_pos >= N_MENU) {
982               PopMenu(MenuCaller);
983               return;
984            }
985            key = MenuKey[2*but_pos];
986 	   if (button<=2) key = tolower(key);
987            processKey(Menu, (KeySym)key);
988 }
989 
990 void
clearFileselPartially()991 clearFileselPartially()
992 {
993     XSetWindowBackground(dpy, Filesel, FileselCaller->gdata->pixel[MENUBGCOLOR]);
994     XClearArea(dpy, Filesel, 0, FileselCaller->gdata->menustrip+1,
995         FileselGeom.width-2, FileselGeom.height-FileselCaller->gdata->menustrip-2, False);
996 }
997 
998 void
setupFilesel(mode)999 setupFilesel(mode)
1000 int mode;
1001 {
1002 	int i, j, b, d, p, w, h, ht, skip;
1003 	char *s, *sp;
1004 
1005         if (!do_filesel) return;
1006 
1007         XSetWindowColormap(dpy, Filesel, FileselCaller->gdata->cmap);
1008         XSetWindowBackground(dpy, Filesel,
1009            FileselCaller->gdata->pixel[MENUBGCOLOR]);
1010 
1011 	d = FileselCaller->gdata->charspace/3;
1012         h = FileselCaller->gdata->menustrip;
1013 
1014 	if (mode < 0) {
1015           BasicSettings(FileselCaller);
1016           XClearArea(dpy, Filesel,  0, 0,
1017              FileselGeom.width, FileselGeom.height, False);
1018 	  for (i=0; i<=6; i++)
1019 	     drawButton(Filesel, i, 0);
1020 	}
1021 
1022         BasicSettings(FileselCaller);
1023 
1024         if (mode <= 0) {
1025             XClearArea(dpy, Filesel,  0, FileselCaller->gdata->menustrip+1,
1026                 FileselGeom.width - 15, FileselGeom.height, False);
1027             XClearArea(dpy, Filesel,
1028                 FileselGeom.width - 15, FileselCaller->gdata->menustrip+1,
1029                 13, FileselCaller->gdata->menustrip, False);
1030             XSetForeground(dpy,  FileselCaller->gdata->wingc,
1031                 FileselCaller->gdata->pixel[MENUFGCOLOR]);
1032             XDrawImageString(dpy, Filesel, FileselCaller->gdata->wingc,
1033                 d, FileselCaller->gdata->font[MENUFONT]->max_bounds.ascent +
1034                    FileselCaller->gdata->menustrip+4,
1035                 image_dir, strlen(image_dir));
1036 
1037             h = 2*FileselCaller->gdata->menustrip;
1038             XDrawLine(dpy, Filesel, FileselCaller->gdata->wingc,
1039                 0, h, FileselGeom.width, h);
1040 
1041             XDrawLine(dpy, Filesel, FileselCaller->gdata->wingc,
1042                 FileselGeom.width - 15, 2*FileselCaller->gdata->menustrip,
1043                 FileselGeom.width - 15, FileselGeom.height);
1044             /* Drawing small triangular icons */
1045             w = FileselGeom.width - 7;
1046             h = 2 * FileselCaller->gdata->menustrip + 1;
1047             for (i=0; i<=10; i++)
1048 	       XDrawLine(dpy, Filesel, FileselCaller->gdata->wingc,
1049                      w-i/2, h+i, w+i/2, h+i);
1050             h = FileselGeom.height-1;
1051             for (i=0; i<=10; i++)
1052                XDrawLine(dpy, Filesel, FileselCaller->gdata->wingc,
1053                      w-i/2, h-i, w+i/2, h-i);
1054 	}
1055 
1056 	if (mode<=1)
1057            XClearArea(dpy, Filesel,  0, 2*FileselCaller->gdata->menustrip+1,
1058                FileselGeom.width - 15, FileselGeom.height, False);
1059 
1060 	if (!dirtable)
1061 	   dirtable = get_dir_list(image_dir, &num_table_entries);
1062 	if (dirtable)
1063            qsort(dirtable, num_table_entries, sizeof(char *), dup_strcmp);
1064 	else {
1065 	   char error[] = "Directory inexistent or inaccessible !!!";
1066 	   XSetForeground(dpy, FileselCaller->gdata->wingc,
1067                                FileselCaller->gdata->pixel[IMAGECOLOR]);
1068            XDrawImageString(dpy, Filesel,
1069                      FileselCaller->gdata->wingc, d,
1070                      3*FileselCaller->gdata->menustrip,
1071 		     error, strlen(error));
1072 	   return;
1073 	}
1074 
1075 	skip = (3*FileselCaller->gdata->menustrip)/4;
1076 	num_lines = (FileselGeom.height-2*FileselCaller->gdata->menustrip)/skip;
1077         /* drawing the thumb */
1078         XSetForeground(dpy, FileselCaller->gdata->wingc,
1079            FileselCaller->gdata->pixel[ZOOMBGCOLOR]);
1080         XFillRectangle(dpy, Filesel, FileselCaller->gdata->wingc,
1081            FileselGeom.width - 14, 2*FileselCaller->gdata->menustrip+12,
1082            14, FileselGeom.height-2*FileselCaller->gdata->menustrip-23);
1083 
1084         w = FileselGeom.width - 12;
1085         p = FileselGeom.height - 2 * FileselCaller->gdata->menustrip - 28;
1086         ht = p * (num_lines+1) / (num_table_entries+1);
1087         if (ht<20) ht = 20;
1088         if (ht>p/2) ht = p/2;
1089         p = p - ht;
1090         h = 2 * FileselCaller->gdata->menustrip + 14;
1091         if (num_table_entries>2)
1092            h += (filesel_shift * p)/(num_table_entries-2);
1093 	drawBox(Filesel, FileselCaller->gdata->wingc,
1094                 FileselCaller->gdata->pixel, w, h, w+9, h+ht, 0);
1095 
1096         for (i=0; i<num_table_entries-filesel_shift; i++)
1097 	if (i<num_lines) {
1098 	  s = dirtable[i+filesel_shift];
1099 	  b = (s[strlen(s)-1]=='/');
1100           if (b==0) {
1101 	    if (strstr(s,".gif") || strstr(s,".jpg") ||
1102 		strstr(s,".png") || strstr(s,".xpm") ||
1103 		strstr(s,".vmf"))
1104 	       b=2;
1105 	  }
1106 	  j = FileselCaller->gdata->font[MENUFONT]->max_bounds.ascent +
1107               2 * FileselCaller->gdata->menustrip + i*skip + 3;
1108 	  sp = (FileselCaller->wintype)?
1109 	    FileselCaller->map_img_file : FileselCaller->clock_img_file;
1110 	  if (strstr(sp,s)) {
1111 	     if (mode<=3)
1112                 XClearArea(dpy, Filesel, 2,
1113 	   /* FileselCaller->gdata->font[MENUFONT]->max_bounds.ascent+ */
1114                       2 * FileselCaller->gdata->menustrip + 1, 3,
1115                    FileselGeom.height, False);
1116 	     if (mode==3) {
1117   	        XSetForeground(dpy, FileselCaller->gdata->wingc,
1118                                FileselCaller->gdata->pixel[CHANGECOLOR]);
1119                 XDrawRectangle(dpy, Filesel, FileselCaller->gdata->wingc,
1120 		  d/4, j-FileselCaller->gdata->font[MENUFONT]->max_bounds.ascent/2, 3,4);
1121 	     } else {
1122   	        XSetForeground(dpy, FileselCaller->gdata->wingc,
1123                                FileselCaller->gdata->pixel[CHOICECOLOR]);
1124                 XFillRectangle(dpy, Filesel, FileselCaller->gdata->wingc,
1125                   d/4, j-FileselCaller->gdata->font[MENUFONT]->max_bounds.ascent/2, 3,4);
1126 	     }
1127 	  }
1128 	  if (mode<=1) {
1129   	     XSetForeground(dpy, FileselCaller->gdata->wingc,
1130                 (b==0)? FileselCaller->gdata->pixel[MENUFGCOLOR] :
1131                         FileselCaller->gdata->pixel[DIRCOLOR+b-1]);
1132              XDrawImageString(dpy, Filesel, FileselCaller->gdata->wingc,
1133                 d, j, s, strlen(s));
1134 	  }
1135 	}
1136 }
1137 
1138 void
PopFilesel(Context)1139 PopFilesel(Context)
1140 struct Sundata * Context;
1141 {
1142         int a, b, w, h, x=0, y=0;
1143 
1144 	if (do_filesel)
1145             do_filesel = 0;
1146 	else
1147 	    do_filesel = 1;
1148 
1149         if (!do_filesel)
1150 	  {
1151 	  XDestroyWindow(dpy, Filesel);
1152 	  Filesel = 0;
1153 	  FileselCaller = NULL;
1154 	  if (dirtable) free_dirlist(dirtable);
1155 	  dirtable = NULL;
1156 	  return;
1157 	  }
1158 
1159         if (!Filesel)
1160            Filesel = newWindow(NULL, &FileselGeom, 3);
1161 
1162         XSelectInput(dpy, Filesel, 0);
1163 	FileselCaller = Context;
1164 	filesel_shift = 0;
1165 
1166 	if (!getPlacement(Context->win, &Context->geom.x,
1167                                         &Context->geom.y, &w, &h)) {
1168 	   x = Context->geom.x + FileselGeom.x - horiz_drift - 5;
1169 	   a = Context->geom.y + h + 6;
1170            if (do_menu && Context == MenuCaller)
1171                a += MenuGeom.height + MenuGeom.y + vert_drift + 2;
1172            b = Context->geom.y - FileselGeom.height - FileselGeom.y - 2*vert_drift - 28;
1173            if (b < TOPTITLEBARHEIGHT ) b = TOPTITLEBARHEIGHT;
1174            if (a > (int) DisplayHeight(dpy,scr)
1175                    - FileselGeom.height - vert_drift - 20)
1176                a = b;
1177 	   y = (placement<=NE)? a : b;
1178 	}
1179 
1180         setSizeHints(NULL, 3);
1181         XMoveWindow(dpy, Filesel, x, y);
1182         XMapRaised(dpy, Filesel);
1183         XMoveWindow(dpy, Filesel, x, y);
1184 	XFlush(dpy);
1185 	usleep(2*TIMESTEP);
1186 	setupFilesel(-1);
1187         setProtocols(NULL, 3);
1188 }
1189 
1190 void
processFileselAction(Context,x,y,evtype)1191 processFileselAction(Context, x, y, evtype)
1192 struct Sundata * Context;
1193 int x;
1194 int y;
1195 int evtype;
1196 {
1197 	static int pressed3 = 0;
1198 	static int move_pos = -1;
1199 	static int click_pos = -1;
1200 	static int but_pos;
1201         char newdir[1030];
1202 	char *s, *f, *path;
1203 
1204         if (evtype == MotionNotify && move_pos>=0) {
1205 	   drawButton(Filesel, move_pos, -2);
1206 	   move_pos = -1;
1207 	}
1208         if (evtype == ButtonRelease && click_pos>=0) {
1209 	   drawButton(Filesel, click_pos, 0);
1210            click_pos = -1;
1211 	}
1212 
1213         but_pos = getButton(Filesel, x, y);
1214 
1215 	if (evtype == ButtonPress) {
1216            if (click_pos>=0) drawButton(Filesel, click_pos, 0);
1217 	   if (but_pos>=0) drawButton(Filesel, but_pos, 1);
1218 	   click_pos = but_pos;
1219 	   pressed3 = 1;
1220 	   return;
1221 	}
1222 	if (evtype == ButtonRelease) {
1223 	   pressed3 = 0;
1224 	}
1225 
1226 	if (evtype == MotionNotify) {
1227 	   if (but_pos>=0 && but_pos!=click_pos) {
1228 	      drawButton(Filesel, but_pos, -1);
1229 	      move_pos = but_pos;
1230 	   }
1231            if (x < FileselGeom.width - 15 ||
1232                y <= 2 * Context->gdata->menustrip)
1233 	      return;
1234 	}
1235 
1236         if (y <= Context->gdata->menustrip) {
1237 	  if (but_pos==0 && getenv("HOME"))
1238 	     sprintf(image_dir, "%s/", getenv("HOME"));
1239 	  if (but_pos==1)
1240 	     StringReAlloc(&image_dir, share_maps_dir);
1241 	  if (but_pos==2)
1242 	     StringReAlloc(&image_dir, "/");
1243 	  if (but_pos==3 && getcwd(NULL,1024)) {
1244 	     sprintf(newdir, "%s/", getcwd(NULL,1024));
1245 	     StringReAlloc(&image_dir, newdir);
1246 	  }
1247 	  if (but_pos>=0 && but_pos<=3) {
1248 	     filesel_shift = 0;
1249 	     if (dirtable) {
1250 	        free(dirtable);
1251 	        dirtable = NULL;
1252 	     }
1253 	  }
1254 	  if (but_pos==4) {
1255 	      do_filesel = -1;
1256               processKey(Context->win, XK_w);
1257 	      return;
1258 	  }
1259 	  if (but_pos==5) {
1260               processKey(Context->win, XK_exclam);
1261 	      return;
1262 	  }
1263 	  if (but_pos>=6) {
1264 	     XUnmapWindow(dpy, Filesel);
1265 	     do_filesel = 0;
1266 	     return;
1267 	  }
1268 	  setupFilesel(0);
1269 	  return;
1270 	}
1271         if (y <= 2*Context->gdata->menustrip) {
1272 	  filesel_shift = 0;
1273 	  setupFilesel(0);
1274 	  return;
1275 	}
1276 
1277 	if (x > FileselGeom.width - 15) {
1278 	   int old_shift = filesel_shift;
1279 	   if (y <= 2*Context->gdata->menustrip + 10) {
1280 	      if (evtype != ButtonRelease) return;
1281 	      if (filesel_shift == 0) return;
1282 	      filesel_shift -= num_lines/2;
1283 	      if (filesel_shift <0) filesel_shift = 0;
1284 	   } else
1285 	   if (y >= FileselGeom.height - 10) {
1286 	      if (evtype != ButtonRelease) return;
1287 	      if (num_table_entries-filesel_shift<num_lines) return;
1288 	      filesel_shift += num_lines/2;
1289 	   } else {
1290 	      if (evtype == MotionNotify && !pressed3) {
1291 		 return;
1292 	      }
1293 	      filesel_shift = ( num_table_entries *
1294 		 ( y - 2*Context->gdata->menustrip - 10 ))
1295                  / (FileselGeom.height - 2*Context->gdata->menustrip - 20);
1296 	      if (filesel_shift > num_table_entries - num_lines/2)
1297                  filesel_shift = num_table_entries - num_lines/2;
1298 	      if (filesel_shift < 0) filesel_shift = 0;
1299 	   }
1300 	   if (filesel_shift != old_shift)
1301 	      setupFilesel(1);
1302 	   return;
1303 	}
1304 
1305 	y = (y-2*Context->gdata->menustrip-4)/(3*Context->gdata->menustrip/4)
1306             +filesel_shift;
1307 	if (y<num_table_entries) {
1308 	   s = dirtable[y];
1309 	   if (s==NULL || *s=='\0') return;
1310 	   if (x > XTextWidth (Context->gdata->font[MENUFONT], s,
1311                    strlen(s))+Context->gdata->charspace/4) return;
1312 	   y = strlen(s)-1;
1313 	   f = (char *) salloc(strlen(image_dir)+y+2);
1314 	   strcpy(f, image_dir);
1315            if (s[y] == '/') {
1316 	      int l;
1317 	      if (!strcmp(s, "../")) {
1318 	        l=strlen(f)-1;
1319 		if (l==0) return;
1320                 f[l--] = '\0';
1321 	        while (l>=0 && f[l] != '/')
1322 		   f[l--] = '\0';
1323 		s = "";
1324 	      }
1325               strcat(f, s);
1326 	      l=strlen(f);
1327               if (f[l-1] != '/') {
1328                  f[l] = 'l';
1329                  f[++l] = '\0';
1330 	      }
1331 	      if (dirtable) free_dirlist(dirtable);
1332 	      dirtable = NULL;
1333 	      filesel_shift = 0;
1334 	      num_table_entries=0;
1335 	      StringReAlloc(&image_dir, f);
1336 	      free(f);
1337               setupFilesel(0);
1338 	      return;
1339 	   } else {
1340 	      path = (Context->wintype)?
1341                     Context->map_img_file : Context->clock_img_file;
1342 	      f = (char *)
1343                 salloc((strlen(image_dir)+strlen(s)+2)*sizeof(char));
1344 	      sprintf(f, "%s%s", image_dir, s);
1345 	      if (!path || strcmp(f, path)) {
1346  		 if (Context->wintype)
1347                     StringReAlloc(&Context->map_img_file, f);
1348 		 else
1349 		    StringReAlloc(&Context->clock_img_file, f);
1350 		 setupFilesel(3);
1351 		 adjustGeom(Context, 0);
1352 	         shutDown(Context, 0);
1353 	         buildMap(Context, Context->wintype, 0);
1354 	      }
1355 	      free(f);
1356 	   }
1357 	}
1358 }
1359 
1360 void
checkZoomSettings(zoom)1361 checkZoomSettings(zoom)
1362 ZoomSettings *zoom;
1363 {
1364     if (zoom->fx<1.0) zoom->fx = 1.0;
1365     if (zoom->fx>100.0) zoom->fx = 100.0;
1366 
1367     if (zoom->fy<1.0) zoom->fy = 1.0;
1368     if (zoom->fy>100.0) zoom->fy = 100.0;
1369 
1370     if (zoom->fdx<0.0) zoom->fdx = 0.0;
1371     if (zoom->fdx>1.0) zoom->fdx = 1.0;
1372 
1373     if (zoom->fdy<0.01) zoom->fdy = 0.01;
1374     if (zoom->fdy>0.99) zoom->fdy = 0.99;
1375 }
1376 
setZoomAspect(Context,mode)1377 int setZoomAspect(Context, mode)
1378 struct Sundata * Context;
1379 int mode;
1380 {
1381    double a, b, f, p;
1382    int change;
1383 
1384    checkZoomSettings(&Context->newzoom);
1385 
1386    if (memcmp(&Context->newzoom, &Context->zoom, sizeof(ZoomSettings)))
1387       Context->oldzoom = Context->zoom;
1388 
1389    if (!Context->newzoom.mode) return 0;
1390 
1391    a = Context->newzoom.fx;
1392    b = Context->newzoom.fy;
1393    change = 0;
1394 
1395    if (Context->newzoom.mode == 1)
1396       f = 1.0;
1397    else
1398       f = (double)Context->geom.width /
1399           ((double)Context->geom.height *2.0 * sin(M_PI*Context->newzoom.fdy));
1400 
1401    if (mode == 3) {
1402       p = sqrt( Context->newzoom.fx * Context->newzoom.fy / f);
1403       Context->newzoom.fx = p;
1404       Context->newzoom.fy = p * f;
1405       f = 0;
1406       mode = 1;
1407    }
1408 
1409    if (mode == 1) {
1410       if (f == 0)
1411           mode = 2;
1412       else
1413           Context->newzoom.fx = Context->newzoom.fy / f;
1414       if (Context->newzoom.fx < 1.0) {
1415 	  Context->newzoom.fy = Context->newzoom.fy / Context->newzoom.fx;
1416 	  Context->newzoom.fx = 1.0;
1417       }
1418       if (Context->newzoom.fx > 100.0) {
1419 	  Context->newzoom.fy = 100.0 * Context->newzoom.fy / Context->newzoom.fx;
1420 	  Context->newzoom.fx = 100.0;
1421       }
1422    }
1423 
1424    if (mode == 2) {
1425       if (f!=0)
1426           Context->newzoom.fy = Context->newzoom.fx * f;
1427       if (Context->newzoom.fy < 1.0) {
1428 	  Context->newzoom.fx = Context->newzoom.fx / Context->newzoom.fy;
1429 	  Context->newzoom.fy = 1.0;
1430       }
1431       if (Context->newzoom.fy > 100.0) {
1432 	  Context->newzoom.fx = 100.0 * Context->newzoom.fx / Context->newzoom.fy;
1433 	  Context->newzoom.fy = 100.0;
1434       }
1435    }
1436 
1437    if (fabs(a-Context->newzoom.fx) > 1E-4) {
1438       zoom_mode |= 10;
1439       change = 1;
1440    } else
1441       Context->newzoom.fx = a;
1442 
1443    if (fabs(b-Context->newzoom.fy) > 1E-4) {
1444       zoom_mode |= 12;
1445       change = 1;
1446    } else
1447       Context->newzoom.fy = b;
1448 
1449    if (verbose && change)
1450       fprintf(stderr, "Adjusting zoom area aspect (%.2f , %.2f) --> "
1451             "(%.2f , %.2f)\n", a, b, Context->newzoom.fx , Context->newzoom.fy);
1452 
1453    return change;
1454 }
1455 
1456 void
setZoomDimension(Context)1457 setZoomDimension(Context)
1458 struct Sundata * Context;
1459 {
1460     double rx, ry;
1461 
1462     setZoomAspect(Context, 3);
1463 
1464     Context->newzoom.width = (int)
1465        ((double) Context->geom.width * Context->newzoom.fx + 0.25);
1466     Context->newzoom.height = (int)
1467        ((double) Context->geom.height * Context->newzoom.fy + 0.25);
1468 
1469     rx = 0.5/Context->newzoom.fx;
1470     ry = 0.5/Context->newzoom.fy;
1471     Context->newzoom.dx = (int)
1472        ((double) Context->newzoom.width * (Context->newzoom.fdx-rx) + 0.25);
1473     Context->newzoom.dy = (int)
1474        ((double) Context->newzoom.height * (Context->newzoom.fdy-ry) + 0.25);
1475 
1476     if (Context->newzoom.dx < 0) Context->newzoom.dx = 0;
1477     if (Context->newzoom.dy < 0) Context->newzoom.dy = 0;
1478     if (Context->newzoom.dx+Context->geom.width>Context->newzoom.width)
1479         Context->newzoom.dx = Context->newzoom.width - Context->geom.width;
1480     if (Context->newzoom.dy+Context->geom.height>Context->newzoom.height)
1481         Context->newzoom.dy = Context->newzoom.height - Context->geom.height;
1482 
1483     if (verbose && !button_pressed)
1484        fprintf(stderr, "Zoom (%.2f, %.2f)  centering at (%.2f, %.2f)\n",
1485 	  Context->newzoom.fx, Context->newzoom.fy,
1486           Context->newzoom.fdx, Context->newzoom.fdy);
1487 }
1488 
1489 void
showZoomHint(but_pos)1490 showZoomHint(but_pos)
1491 int but_pos;
1492 {
1493         static int move_pos = -1;
1494 	char hint[120];
1495 	int v;
1496 
1497         if (move_pos>=0) drawButton(Zoom, move_pos, -2);
1498         if (but_pos>=0) drawButton(Zoom, but_pos, -1);
1499 	move_pos = but_pos;
1500 
1501 	if (!do_zoom || zoom_lasthint==zoom_newhint) return;
1502 
1503 	zoom_lasthint = zoom_newhint;
1504 	v = ZoomGeom.height - ZoomCaller->gdata->menustrip;
1505 
1506 	if (zoom_newhint=='\033')
1507            strcpy(hint, Label[L_ESCMENU]);
1508 	else
1509 	if (zoom_newhint==' ')
1510            sprintf(hint,
1511                 "magx = %.3f, magy = %.3f,  lon = %.3f�, lat = %.3f�",
1512                 ZoomCaller->newzoom.fx, ZoomCaller->newzoom.fy,
1513 	 	360.0 * ZoomCaller->newzoom.fdx - 180.0,
1514                 90.0 - ZoomCaller->newzoom.fdy*180.0);
1515         else
1516 	   strcpy(hint, Help[getNumCmd(zoom_newhint)]);
1517 
1518 	BasicSettings(ZoomCaller);
1519         XSetWindowBackground(dpy, Zoom, ZoomCaller->gdata->pixel[MENUBGCOLOR]);
1520 	XClearArea(dpy, Zoom, 0, v+1, ZoomGeom.width,
1521              ZoomCaller->gdata->menustrip-1, False);
1522 	XDrawImageString(dpy, Zoom, ZoomCaller->gdata->wingc, 4,
1523               v + ZoomCaller->gdata->font[MENUFONT]->max_bounds.ascent + 3,
1524               hint, strlen(hint));
1525 }
1526 
1527 void
1528 PopZoom();
1529 
1530 void
setupZoom(mode)1531 setupZoom(mode)
1532 int mode;
1533 {
1534     int i, j, k;
1535     int zoomx, zoomy, zoomw, zoomh;
1536     char *num[] = { "1", "2", "5", "10", "20", "50", "100"};
1537     char *synchro = Label[L_SYNCHRO];
1538     char s[80];
1539 
1540     if (!do_zoom) return;
1541 
1542     BasicSettings(ZoomCaller);
1543     XSetWindowColormap(dpy, Zoom, ZoomCaller->gdata->cmap);
1544     XSetWindowBackground(dpy, Zoom, ZoomCaller->gdata->pixel[MENUBGCOLOR]);
1545 
1546     areaw = ZoomGeom.width - 74;
1547     areah = ZoomGeom.height - 2*ZoomCaller->gdata->menustrip - 65;
1548 
1549     if (mode == -1) {
1550        XClearArea(dpy, Zoom,  0,0, ZoomGeom.width, ZoomGeom.height, False);
1551        XDrawImageString(dpy, Zoom, ZoomCaller->gdata->wingc,
1552           160-XTextWidth(ZoomCaller->gdata->font[MENUFONT],
1553           synchro, strlen(synchro))+areah,
1554           24+ ZoomCaller->gdata->font[MENUFONT]->max_bounds.ascent,
1555           synchro, strlen(synchro));
1556 
1557        for (i=0; i<=N_ZOOM; i++)
1558 	  drawButton(Zoom, i, 0);
1559 
1560        BasicSettings(ZoomCaller);
1561 
1562        for (i=0; i<=6; i++) {
1563           j = 63 + (int) ( (areah-6)*log(atof(num[i]))/log(100.0));
1564           k = j - ZoomCaller->gdata->charspace*strlen(num[i])/10;
1565           XDrawImageString(dpy, Zoom, ZoomCaller->gdata->wingc, k,
1566              ZoomCaller->gdata->font[MENUFONT]->max_bounds.ascent + 3,
1567              num[i], strlen(num[i]));
1568           k = j + ZoomCaller->gdata->font[MENUFONT]->max_bounds.ascent/2 - 10;
1569           XDrawImageString(dpy, Zoom, ZoomCaller->gdata->wingc,
1570                 24-ZoomCaller->gdata->charspace*strlen(num[i])/4, k ,
1571                 num[i], strlen(num[i]));
1572           XDrawLine(dpy, Zoom, ZoomCaller->gdata->wingc, j, 20, j, 24);
1573           XDrawLine(dpy, Zoom, ZoomCaller->gdata->wingc, 30, j-10, 34, j-10);
1574        }
1575 
1576        XDrawLine(dpy, Zoom, ZoomCaller->gdata->wingc, 60, 22, areah+60, 22);
1577        XDrawLine(dpy, Zoom, ZoomCaller->gdata->wingc, 32, 50, 32, areah+50);
1578     }
1579 
1580     XSetWindowBackground(dpy, Zoom, ZoomCaller->gdata->pixel[ZOOMBGCOLOR]);
1581     if ((mode & 1) && !ZoomCaller->newzoom.mode)
1582        XClearArea(dpy, Zoom,  41, 31, 9, 9, False);
1583     if (mode & 2)
1584        XClearArea(dpy, Zoom,  61, 31, areah, 9, False);
1585     if (mode & 4)
1586        XClearArea(dpy, Zoom,  41, 51, 9, areah, False);
1587 
1588     if (!zoompix) {
1589        int oldlevel, oldfill, code, num;
1590 
1591        oldlevel = ZoomCaller->flags.colorlevel;
1592        oldfill = ZoomCaller->flags.fillmode;
1593        ZoomCaller->flags.colorlevel = MONOCHROME;
1594        ZoomCaller->flags.fillmode = 1;
1595        i = ZoomCaller->geom.width;
1596        j = ZoomCaller->geom.height;
1597        ZoomCaller->newzoom = ZoomCaller->zoom;
1598        ZoomCaller->zoom.width = ZoomCaller->geom.width = areaw;
1599        ZoomCaller->zoom.height = ZoomCaller->geom.height = areah;
1600        ZoomCaller->zoom.dx = ZoomCaller->zoom.dy = 0;
1601 
1602        code = readVMF(Zoom_img_file, ZoomCaller);
1603        if (code) {
1604 	  if (ZoomCaller->bits) free(ZoomCaller->bits);
1605 	  ZoomCaller->bits = NULL;
1606        }
1607        if (!ZoomCaller->bits) {
1608           num = ((ZoomCaller->geom.width+7)/8)*
1609 	                       ZoomCaller->geom.height*sizeof(char);
1610 	  ZoomCaller->bits = (char *) salloc(num);
1611        }
1612        if (code && ZoomCaller->bits)
1613 	  memset(ZoomCaller->bits, 0xFF, num);
1614        if (ZoomCaller->bits) {
1615           zoompix = XCreatePixmapFromBitmapData(dpy, Root,
1616                      ZoomCaller->bits, ZoomCaller->geom.width,
1617                      ZoomCaller->geom.height, 0, 1, 1);
1618           free(ZoomCaller->bits);
1619        }
1620 
1621        ZoomCaller->zoom = ZoomCaller->newzoom;
1622        ZoomCaller->geom.width = i;
1623        ZoomCaller->geom.height = j;
1624        ZoomCaller->flags.colorlevel = oldlevel;
1625        ZoomCaller->flags.fillmode = oldfill;
1626     }
1627 
1628     XSetWindowBackground(dpy, Zoom, ZoomCaller->gdata->pixel[CHOICECOLOR]);
1629     XSetBackground(dpy, ZoomCaller->gdata->wingc, ZoomCaller->gdata->pixel[CHOICECOLOR]);
1630     XSetForeground(dpy, ZoomCaller->gdata->wingc, ZoomCaller->gdata->pixel[CHANGECOLOR]);
1631 
1632     if (mode & 2) {
1633        i = (int) ( (double)(areah-6)*log(ZoomCaller->zoom.fx)/log(100.00));
1634        XClearArea(dpy, Zoom,  61+i, 31, 6, 9, False);
1635        if (ZoomCaller->newzoom.fx != ZoomCaller->zoom.fx) {
1636          i = (int) ((double)(areah-6)*log(ZoomCaller->newzoom.fx)/log(100.00));
1637          XDrawRectangle(dpy, Zoom, ZoomCaller->gdata->wingc, 61+i, 31, 5, 8);
1638        }
1639     }
1640     if (mode & 4) {
1641        j = (int) ( (double)(areah-6)*log(ZoomCaller->zoom.fy)/log(100.0));
1642        XClearArea(dpy, Zoom,  41, 51+j, 9, 6, False);
1643        if (ZoomCaller->newzoom.fy != ZoomCaller->zoom.fy) {
1644          j = (int) ((double)(areah-6)*log(ZoomCaller->newzoom.fy)/log(100.0));
1645          XDrawRectangle(dpy, Zoom, ZoomCaller->gdata->wingc, 41, 51+j, 8, 5);
1646        }
1647     }
1648 
1649     if (mode & 8) {
1650        zoomw = (areaw*ZoomCaller->geom.width)/ZoomCaller->zoom.width;
1651        zoomh = (areah*ZoomCaller->geom.height)/ZoomCaller->zoom.height;
1652        zoomx = (areaw*ZoomCaller->zoom.dx)/ZoomCaller->zoom.width;
1653        zoomy = (areah*ZoomCaller->zoom.dy)/ZoomCaller->zoom.height;
1654        i = areaw-zoomx-zoomw-1;
1655        j = areah-zoomy-zoomh-1;
1656 
1657        XSetBackground(dpy, ZoomCaller->gdata->wingc,
1658                       ZoomCaller->gdata->pixel[ZOOMBGCOLOR]);
1659        XSetForeground(dpy, ZoomCaller->gdata->wingc,
1660                       ZoomCaller->gdata->pixel[ZOOMFGCOLOR]);
1661 
1662        if (zoomy)
1663           XCopyPlane(dpy, zoompix, Zoom, ZoomCaller->gdata->wingc,
1664                 0, 0, areaw-1, zoomy, 61, 51, 1);
1665        if (j>0)
1666           XCopyPlane(dpy, zoompix, Zoom, ZoomCaller->gdata->wingc,
1667                 0, zoomy+zoomh+1, areaw, j, 61, 52+zoomy+zoomh, 1);
1668        if (zoomx)
1669           XCopyPlane(dpy, zoompix, Zoom, ZoomCaller->gdata->wingc,
1670                 0, 0, zoomx, areah-1, 61, 51, 1);
1671        if (i>0)
1672           XCopyPlane(dpy, zoompix, Zoom, ZoomCaller->gdata->wingc,
1673                 zoomx+zoomw+1, 0, i, areah, 62+zoomx+zoomw, 51, 1);
1674 
1675        if (ZoomCaller->flags.colorlevel == MONOCHROME) {
1676           XSetBackground(dpy, ZoomCaller->gdata->wingc,
1677                       ZoomCaller->gdata->pixel[ZOOMFGCOLOR]);
1678           XSetForeground(dpy, ZoomCaller->gdata->wingc,
1679                       ZoomCaller->gdata->pixel[ZOOMBGCOLOR]);
1680        } else
1681           XSetBackground(dpy, ZoomCaller->gdata->wingc,
1682                       ZoomCaller->gdata->pixel[CHOICECOLOR]);
1683 
1684        XCopyPlane(dpy, zoompix, Zoom, ZoomCaller->gdata->wingc,
1685                   zoomx, zoomy, zoomw+1, zoomh+1, 61+zoomx, 51+zoomy, 1);
1686 
1687        if (ZoomCaller->newzoom.fx!=ZoomCaller->zoom.fx ||
1688            ZoomCaller->newzoom.fy!=ZoomCaller->zoom.fy ||
1689            ZoomCaller->newzoom.fdx!=ZoomCaller->zoom.fdx ||
1690            ZoomCaller->newzoom.fdy!=ZoomCaller->zoom.fdy) {
1691           zoomw = (areaw*ZoomCaller->geom.width)/ZoomCaller->newzoom.width;
1692           zoomh = (areah*ZoomCaller->geom.height)/ZoomCaller->newzoom.height;
1693           zoomx = (areaw*ZoomCaller->newzoom.dx)/ZoomCaller->newzoom.width;
1694           zoomy = (areah*ZoomCaller->newzoom.dy)/ZoomCaller->newzoom.height;
1695 	  if (ZoomCaller->flags.colorlevel==MONOCHROME)
1696 	     drawDottedRectangle(dpy, Zoom, ZoomCaller->gdata->wingc,
1697 		61+zoomx, 51+zoomy, zoomw, zoomh,
1698 		ZoomCaller->gdata->pixel[ZOOMFGCOLOR],
1699                 ZoomCaller->gdata->pixel[ZOOMBGCOLOR]);
1700 	  else {
1701              XSetForeground(dpy, ZoomCaller->gdata->wingc,
1702 		   ZoomCaller->gdata->pixel[CHANGECOLOR]);
1703              XDrawRectangle(dpy, Zoom, ZoomCaller->gdata->wingc,
1704                 61+zoomx, 51+zoomy, zoomw, zoomh);
1705 	  }
1706        }
1707 
1708        i = 60 + (int) (areaw * ZoomCaller->newzoom.fdx);
1709        j = 50 + (int) (areah * ZoomCaller->newzoom.fdy);
1710 
1711        XSetForeground(dpy, ZoomCaller->gdata->wingc, ZoomCaller->gdata->pixel[CHANGECOLOR]);
1712        if (i<60+areaw && j<50+areah)
1713           XDrawRectangle(dpy, Zoom, ZoomCaller->gdata->wingc, i, j, 1, 1);
1714 
1715        XSetForeground(dpy, ZoomCaller->gdata->wingc, ZoomCaller->gdata->pixel[MENUFGCOLOR]);
1716        XDrawRectangle(dpy, Zoom, ZoomCaller->gdata->wingc, 60, 50, areaw+1, areah+1);
1717     }
1718 
1719     XSetWindowBackground(dpy, Zoom, ZoomCaller->gdata->pixel[MENUBGCOLOR]);
1720     XSetBackground(dpy, ZoomCaller->gdata->wingc, ZoomCaller->gdata->pixel[MENUBGCOLOR]);
1721     XSetForeground(dpy, ZoomCaller->gdata->wingc, ZoomCaller->gdata->pixel[MENUFGCOLOR]);
1722 
1723     if (mode & 1) {
1724        XClearArea(dpy, Zoom,  33, 23, 17, 17, False);
1725        s[0] = '0'+ZoomCaller->newzoom.mode;
1726        s[1] = '\0';
1727        XDrawImageString(dpy, Zoom, ZoomCaller->gdata->wingc,
1728 	  39, 25+ZoomCaller->gdata->font[MENUFONT]->max_bounds.ascent, s, 1);
1729     }
1730 
1731     if (mode & 16) {
1732        drawButton(Zoom, N_ZOOM+1, 0);
1733     }
1734 
1735     XSetForeground(dpy, ZoomCaller->gdata->wingc, ZoomCaller->gdata->pixel[MENUFGCOLOR]);
1736 
1737     if (mode == -1) {
1738        XDrawRectangle(dpy, Zoom, ZoomCaller->gdata->wingc, 60, 30, areah+1, 10);
1739        XDrawRectangle(dpy, Zoom, ZoomCaller->gdata->wingc, 40, 50, 10, areah+1);
1740        XDrawRectangle(dpy, Zoom, ZoomCaller->gdata->wingc, 32, 22, 18, 18);
1741        drawButton(Zoom, N_ZOOM+1, 0);
1742     }
1743 
1744     zoom_lasthint = '\0';
1745     showZoomHint(-1);
1746 }
1747 
1748 void
PopZoom(Context)1749 PopZoom(Context)
1750 struct Sundata * Context;
1751 {
1752         int a, b, w, h, x=0, y=0;
1753 
1754 	if (do_zoom) {
1755 	    XDestroyWindow(dpy, Zoom);
1756 	    Zoom = 0;
1757 	    ZoomCaller = NULL;
1758 	    zoom_active = 1;
1759             do_zoom = 0;
1760 	    if (zoompix) {
1761 	       XFreePixmap(dpy, zoompix);
1762 	       zoompix = 0;
1763 	    }
1764 	    return;
1765 	} else
1766 	    do_zoom = 1;
1767 
1768         if (!Zoom)
1769            Zoom = newWindow(NULL, &ZoomGeom, 4);
1770 
1771         Context->newzoom = Context->zoom;
1772         XSelectInput(dpy, Zoom, 0);
1773 
1774 	ZoomCaller = Context;
1775 	zoom_shift = 0;
1776 	zoom_mode = 0;
1777 	zoom_active = do_zoomsync;
1778 	zoom_newhint = ' ';
1779 
1780 	if (!getPlacement(Context->win, &Context->geom.x, &Context->geom.y, &w, &h)) {
1781 	   x = Context->geom.x + ZoomGeom.x - horiz_drift - 5;
1782 	   a = Context->geom.y + h + 6;
1783            if (do_menu && Context == MenuCaller)
1784                a += MenuGeom.height + MenuGeom.y + vert_drift + 2;
1785            b = Context->geom.y - ZoomGeom.height - ZoomGeom.y - 2*vert_drift - 28;
1786            if (b < TOPTITLEBARHEIGHT ) b = TOPTITLEBARHEIGHT;
1787            if (a > (int) DisplayHeight(dpy,scr)
1788                    - ZoomGeom.height - vert_drift -20)
1789               a = b;
1790 	   y = (placement<=NE)? a : b;
1791 	}
1792 
1793         setSizeHints(NULL, 4);
1794         XMoveWindow(dpy, Zoom, x, y);
1795         XMapRaised(dpy, Zoom);
1796         XMoveWindow(dpy, Zoom, x, y);
1797         XFlush(dpy);
1798 	usleep(2*TIMESTEP);
1799 	option_lasthint = '\0';
1800         setupZoom(-1);
1801         setProtocols(NULL, 4);
1802 }
1803 
1804 void
activateZoom(Context,rebuild)1805 activateZoom(Context, rebuild)
1806 Sundata *Context;
1807 int rebuild;
1808 {
1809     setZoomDimension(Context);
1810     zoom_newhint = ' ';
1811     setupZoom(zoom_mode);
1812     if (rebuild) {
1813        if (Context->zoom.width!=Context->newzoom.width ||
1814 	   Context->zoom.height!=Context->newzoom.height ||
1815 	   Context->zoom.dx!=Context->newzoom.dx ||
1816 	   Context->zoom.dy!=Context->newzoom.dy) {
1817           Context->zoom = Context->newzoom;
1818           shutDown(Context, 0);
1819           buildMap(Context, Context->wintype, 0);
1820 	  zoom_newhint = ' ';
1821           setupZoom(zoom_mode);
1822        } else
1823           Context->zoom = Context->newzoom;
1824     }
1825     if (rebuild>0)
1826        zoom_mode = 0;
1827 }
1828 
processZoomAction(Context,x,y,button,evtype)1829 void processZoomAction(Context, x, y, button, evtype)
1830 Sundata * Context;
1831 int x;
1832 int y;
1833 int button;
1834 int evtype;
1835 {
1836            static int click_pos=-1;
1837            double v1, v2;
1838 	   int but_pos;
1839 
1840            if (evtype == ButtonRelease && click_pos>=0) {
1841 	       drawButton(Zoom, click_pos, 0);
1842                click_pos = -1;
1843   	   }
1844 
1845 	   if (y>areah+63 && y<areah+63+Context->gdata->menustrip) {
1846 	      but_pos = getButton(Zoom, x, y);
1847 	      if (but_pos>=N_ZOOM)
1848 		 zoom_newhint = '\033';
1849 	      else
1850 	      if (but_pos>=0)
1851 		 zoom_newhint = ZoomKey[2*but_pos];
1852 	      if (evtype==MotionNotify) {
1853 	         if (but_pos>=0)
1854 		    showZoomHint(but_pos);
1855 	      }
1856               if (evtype==ButtonPress && but_pos>=0) {
1857 	         drawButton(Zoom, but_pos, 1);
1858 		 click_pos  = but_pos;
1859 	      }
1860 	      if (evtype==ButtonRelease) {
1861 		 if (zoom_newhint == '\033') {
1862 		    PopZoom(Context);
1863 		    return;
1864 		 }
1865 		 if (zoom_newhint == 'W') do_zoom = -1;
1866 		 processKey(Context->win, tolower(zoom_newhint));
1867 		 showZoomHint(but_pos);
1868 		 zoom_mode = 0;
1869 	      }
1870 	      return;
1871 	   }
1872 	   if (button_pressed>=2) return;
1873 
1874            if (evtype==ButtonRelease)
1875 	       drawButton(Zoom, N_ZOOM+1, 0);
1876            if (evtype==MotionNotify)
1877 	       drawButton(Zoom, N_ZOOM+1, -2);
1878 
1879 	   if (x>areah+170 && x<areah+170+Context->gdata->charspace &&
1880                y>21 && y<39) {
1881                if (evtype==MotionNotify) {
1882 		  drawButton(Zoom, N_ZOOM+1, -1);
1883 		  zoom_newhint = '"';
1884 		  showZoomHint(getNumCmd(zoom_newhint));
1885 	       }
1886                if (evtype==ButtonPress) {
1887 		  drawButton(Zoom, N_ZOOM+1, 1);
1888 	       }
1889                if (evtype==ButtonRelease) {
1890 	          zoom_active = 1 -zoom_active;
1891 	          zoom_mode |= 16;
1892 	       }
1893 	   } else
1894 	   if (x>=60 && x<=areah+60 && y>=30 && y<=40 && button_pressed) {
1895               v1 = exp((double)(x-66)*log(100.00)/(double)(areah-6));
1896 	      if (v1 != Context->newzoom.fx) {
1897 		 Context->newzoom.fx = v1;
1898 		 zoom_mode |= 10;
1899                  (void) setZoomAspect(Context, 2);
1900 	      }
1901 	   } else
1902 	   if (x>=40 && x<=50 && y>=50 && y<=areah+50 && button_pressed) {
1903               v2  = exp((double)(y-53)*log(100.00)/(double)(areah-6));
1904 	      if (v2 != Context->newzoom.fy) {
1905 		 Context->newzoom.fy = v2;
1906 		 zoom_mode |= 12;
1907                  (void) setZoomAspect(Context, 1);
1908 	      }
1909 	   }
1910            else
1911 	   if (x>=60 && y>=50 && x<=60+areaw && y<=50+areah && button_pressed){
1912               v1 = ((double)(x-60))/((double)areaw);
1913               v2 = ((double)(y-50))/((double)areah);
1914               if (v1!=Context->newzoom.fdx || v2!=Context->newzoom.fdy) {
1915 		 Context->newzoom.fdx = v1;
1916 		 Context->newzoom.fdy = v2;
1917 	         zoom_mode |= 8;
1918 	      }
1919 	      (void) setZoomAspect(Context, 3);
1920 	   }
1921 	   else {
1922 	     zoom_newhint = ' ';
1923 	     showZoomHint(-1);
1924 	     if (button_pressed) return;
1925 	   }
1926 
1927 	   if (zoom_mode>0) {
1928               setZoomDimension(Context);
1929 	      if (zoom_active && !button_pressed &&
1930                   memcmp(&Context->newzoom,
1931                          &Context->zoom, sizeof(ZoomSettings))) {
1932 		 button_pressed = 0;
1933 		 activateZoom(Context, -1);
1934 	      } else
1935 	         setupZoom(zoom_mode);
1936  	      if (!button_pressed) zoom_mode = 0;
1937 	   }
1938 }
1939 
1940 void
showOptionHint(but_pos)1941 showOptionHint(but_pos)
1942 int but_pos;
1943 {
1944         static int move_pos = -1;
1945 	char hint[128];
1946 	int l, v;
1947 
1948         if (move_pos>=0) drawButton(Option, move_pos, -2);
1949         if (but_pos>=0) drawButton(Option, but_pos, -1);
1950 	move_pos = but_pos;
1951 
1952 	if (!do_option || option_lasthint==option_newhint) return;
1953 
1954 	*hint = '\0';
1955 
1956 	option_lasthint = option_newhint;
1957 	v = OptionGeom.height - OptionCaller->gdata->menustrip;
1958 
1959 	if (option_newhint=='\033')
1960            strcpy(hint, Label[L_ESCMENU]);
1961 	else
1962 	if (option_newhint==' ')
1963 	   strcpy(hint, Label[L_OPTIONINTRO]);
1964 	else
1965 	if (option_newhint=='\n')
1966            strcpy(hint, Label[L_ACTIVATE]);
1967 	else
1968 	if (option_newhint=='?')
1969            strcpy(hint, Label[L_INCORRECT]);
1970 	else {
1971 	   if (option_newhint == 'G' || option_newhint == 'J')
1972               option_lasthint = ' ';
1973 	   helpHint(OptionCaller, option_newhint, hint);
1974 	}
1975 
1976 	l = strlen(hint);
1977 
1978 	BasicSettings(OptionCaller);
1979         XSetWindowBackground(dpy, Option, OptionCaller->gdata->pixel[MENUBGCOLOR]);
1980 
1981 	XClearArea(dpy, Option, 0, v+1, OptionGeom.width,
1982              OptionCaller->gdata->menustrip-1, False);
1983 
1984         XDrawImageString(dpy, Option, OptionCaller->gdata->wingc,
1985               4, v + OptionCaller->gdata->font[MENUFONT]->max_bounds.ascent + 3,
1986               hint, l);
1987 }
1988 
1989 void
showCaret(Context,win,entry,x,y,mode)1990 showCaret(Context, win, entry, x, y, mode)
1991 Sundata * Context;
1992 Window win;
1993 TextEntry *entry;
1994 int x, y, mode;
1995 {
1996 int i, j;
1997 
1998     i = XTextWidth(Context->gdata->font[MENUFONT], entry->string, entry->caret);
1999     j = XTextWidth(Context->gdata->font[MENUFONT], "_", 1);
2000 
2001     XSetWindowBackground(dpy, win,
2002           Context->gdata->pixel[(mode)? CARETCOLOR : OPTIONBGCOLOR]);
2003     XClearArea(dpy, win,
2004           x+i, y+Context->gdata->font[MENUFONT]->max_bounds.ascent+
2005           Context->gdata->menustrip/3 - 1, j, 2, False);
2006 }
2007 
2008 void
setupOption(mode)2009 setupOption(mode)
2010 int mode;
2011 {
2012     int i, vskip;
2013     char s[80];
2014 
2015     if (!do_option) return;
2016 
2017     BasicSettings(OptionCaller);
2018     XSetWindowColormap(dpy, Option, OptionCaller->gdata->cmap);
2019     XSetWindowBackground(dpy, Option, OptionCaller->gdata->pixel[MENUBGCOLOR]);
2020 
2021     vskip = 3*OptionCaller->gdata->menustrip/8;
2022     option_lasthint = '\0';
2023 
2024     if (mode == -1) {
2025        XClearArea(dpy, Option, 0,0, OptionGeom.width,
2026                                     OptionGeom.height, False);
2027        for (i=0; i<=N_OPTION; i++)
2028 	   drawButton(Option, i, 0);
2029 
2030        XSetBackground(dpy, OptionCaller->gdata->wingc,
2031                       OptionCaller->gdata->pixel[MENUBGCOLOR]);
2032        XSetForeground(dpy, OptionCaller->gdata->wingc,
2033                       OptionCaller->gdata->pixel[MENUFGCOLOR]);
2034        strcpy(s, Label[L_OPTION]);
2035        XDrawImageString(dpy, Option, OptionCaller->gdata->wingc,
2036 	       8, OptionCaller->gdata->font[MENUFONT]->max_bounds.ascent + vskip + 3,
2037                s, strlen(s));
2038        XDrawRectangle(dpy, Option, OptionCaller->gdata->wingc,
2039                            70, vskip, OptionGeom.width-85,
2040                            OptionCaller->gdata->menustrip);
2041     }
2042 
2043     XSetWindowBackground(dpy, Option,
2044        OptionCaller->gdata->pixel[OPTIONBGCOLOR]);
2045     XClearArea(dpy, Option, 71,vskip+1, OptionGeom.width-86,
2046            OptionCaller->gdata->menustrip-1, False);
2047     XSetBackground(dpy, OptionCaller->gdata->wingc,
2048                         OptionCaller->gdata->pixel[OPTIONBGCOLOR]);
2049     XSetForeground(dpy, OptionCaller->gdata->wingc,
2050                         OptionCaller->gdata->pixel[OPTIONFGCOLOR]);
2051 
2052     XDrawImageString(dpy, Option, OptionCaller->gdata->wingc, 76,
2053        OptionCaller->gdata->font[MENUFONT]->max_bounds.ascent + vskip + 3,
2054        option_entry.string, strlen(option_entry.string));
2055     if (text_input == OPTION_INPUT)
2056        showCaret(OptionCaller, Option, &option_entry, 76, vskip, 1);
2057     showOptionHint(-1);
2058 }
2059 
2060 void
resetStringLength(max,entry)2061 resetStringLength(max, entry)
2062 int max;
2063 TextEntry * entry;
2064 {
2065         int a;
2066 
2067 	entry->maxlength = max;
2068 
2069 	a = (entry->string == NULL);
2070        	entry->string = (char *)
2071            realloc(entry->string, (max+2)*sizeof(char));
2072         if (a) {
2073 	   *entry->string = '\0';
2074 	   entry->caret = 0;
2075 	}
2076 	if (entry->caret > max) {
2077 	   entry->string[max] = '\0';
2078 	   entry->caret = max;
2079 	}
2080 }
2081 
2082 void
PopOption(Context)2083 PopOption(Context)
2084 struct Sundata * Context;
2085 {
2086 	int    w, h, a, b, x=0, y=0;
2087 
2088 	do_option = 1 - do_option;
2089 
2090         if (!do_option)
2091 	  {
2092 	  XDestroyWindow(dpy, Option);
2093 	  Option = 0;
2094 	  OptionCaller = NULL;
2095 	  return;
2096 	  }
2097 
2098         if (!Option)
2099            Option = newWindow(NULL, &OptionGeom, 5);
2100 
2101         XSelectInput(dpy, Option, 0);
2102         OptionCaller = Context;
2103 
2104 	w = ((OptionGeom.width-86) /
2105               XTextWidth(OptionCaller->gdata->font[MENUFONT], "_", 1)) - 2;
2106 	resetStringLength(w, &option_entry);
2107 
2108 	if (runlevel>=RUNTIMEOPTION)
2109            option_newhint = '\n';
2110 	else
2111 	   option_newhint = ' ';
2112 
2113 	OptionGeom.height = OptionGeom.h_mini
2114                           = (15 * Context->gdata->menustrip)/4;
2115 
2116 	if (!getPlacement(Context->win, &Context->geom.x, &Context->geom.y, &w, &h)) {
2117 	   x = Context->geom.x + OptionGeom.x - horiz_drift - 5;
2118 	   a = Context->geom.y + h + 6;
2119            if (do_menu && Context == MenuCaller)
2120                a += MenuGeom.height + MenuGeom.y + vert_drift + 2;
2121            b = Context->geom.y - OptionGeom.height - OptionGeom.y - 2*vert_drift - 28;
2122            if (b < TOPTITLEBARHEIGHT ) b = TOPTITLEBARHEIGHT;
2123            if (a > (int) DisplayHeight(dpy,scr)
2124                    - 2*OptionGeom.height -vert_drift -20)
2125               a = b;
2126 	   y = (placement<=NE)? a : b;
2127 	}
2128         setSizeHints(NULL, 5);
2129         XMoveWindow(dpy, Option, x, y);
2130         XMapRaised(dpy, Option);
2131         XMoveWindow(dpy, Option, x, y);
2132         XFlush(dpy);
2133 	usleep(2*TIMESTEP);
2134 	option_lasthint = '\0';
2135 	option_newhint = ' ';
2136 	setupOption(-1);
2137         setProtocols(NULL, 5);
2138 }
2139 
2140 void
activateOption()2141 activateOption()
2142 {
2143         Sundata *Context;
2144 	Flags oldflags;
2145         ZoomSettings oldzoom;
2146         int i, size;
2147 	short *ptr, *oldptr, *newptr;
2148 	double *zptr, *zoldptr, *znewptr;
2149 
2150 	Context = OptionCaller;
2151 
2152 	if (!do_option || !Context) return;
2153 
2154 	oldflags = gflags;
2155 	gflags.animate += 2;
2156 	oldzoom = gzoom;
2157 	runlevel = RUNTIMEOPTION;
2158 	i = parseCmdLine(option_entry.string);
2159 	correctValues();
2160         if (i>0 || runlevel == FAILEDOPTION) {
2161 	   option_newhint = '?';
2162 	   showOptionHint(-1);
2163 	   return;
2164 	} else
2165 	   option_newhint = '\n';
2166 
2167         showOptionHint(-1);
2168      /* Set runlevel=IMAGERECYCLE if previous image/pixmap can be recycled */
2169 	if (option_changes<4 && gflags.colorlevel==oldflags.colorlevel &&
2170             gflags.fillmode==oldflags.fillmode) {
2171            runlevel = IMAGERECYCLE;
2172 	   tmp_cmap = Context->gdata->cmap;
2173 	   if (gflags.colorlevel<FULLCOLORS) {
2174               clearNightArea(Context);
2175 	      if (gflags.colorlevel==MONOCHROME) {
2176 	         drawCities(Context);
2177 	         drawSunAndMoon(Context);
2178 	      }
2179 	   } else {
2180   	      size = Context->xim->bytes_per_line*Context->xim->height;
2181               memcpy(Context->xim->data, Context->ximdata, size);
2182 	   }
2183 	}
2184 	shutDown(Context, 0);
2185 	memcpy(Context->spotsizes, city_spotsizes, city_cat*sizeof(int));
2186 	memcpy(Context->sizelimits, city_sizelimits, city_cat*sizeof(int));
2187 
2188 	if (gflags.animate<2)
2189 	   Context->flags.animate = gflags.animate;
2190 	else
2191            gflags.animate -= 2;
2192 	ptr = (short *) &gflags;
2193 	oldptr = (short *) &oldflags;
2194 	newptr = (short *) &Context->flags;
2195         for (i=0; i<sizeof(Flags)/sizeof(short); i++)
2196             if (ptr[i]!=oldptr[i]) newptr[i] = ptr[i];
2197 
2198 	zptr = (double *) &gzoom;
2199 	zoldptr = (double *) &oldzoom;
2200 	znewptr = (double *) &Context->zoom;
2201         for (i=0; i<6; i++)
2202             if (zptr[i]!=zoldptr[i]) znewptr[i] = zptr[i];
2203 
2204 	if (option_changes & 8)
2205 	    Context->geom = ClockGeom;
2206 	if (option_changes & 16)
2207 	    Context->geom = MapGeom;
2208 	if (option_changes & 32)
2209             StringReAlloc(&Context->clock_img_file, Clock_img_file);
2210 	if (option_changes & 64)
2211             StringReAlloc(&Context->map_img_file, Map_img_file);
2212 	buildMap(Context, Context->wintype, 0);
2213 }
2214 
2215 void
processOptionAction(Context,x,y,button,evtype)2216 processOptionAction(Context, x, y, button, evtype)
2217 Sundata * Context;
2218 int x, y, button, evtype;
2219 {
2220         static int click_pos = -1;
2221         int i, opth, vskip, but_pos;
2222 	KeySym key;
2223 
2224         opth = OptionGeom.height - 2 * Context->gdata->menustrip - 1;
2225 	vskip = 3*Context->gdata->menustrip/8;
2226 
2227         if (evtype == ButtonRelease && click_pos>=0) {
2228 	   drawButton(Option, click_pos, 0);
2229            click_pos = -1;
2230         }
2231 
2232 	if (evtype==ButtonRelease && x>=70 &&
2233             x<=OptionGeom.width-15 &&
2234             y>=vskip && y<=Context->gdata->menustrip+vskip) {
2235 	   text_input = OPTION_INPUT;
2236 	   but_pos = (x-76)/XTextWidth(OptionCaller->gdata->font[MENUFONT], "_", 1);
2237 	   i = strlen(option_entry.string);
2238 	   if (but_pos<0) but_pos = 0;
2239 	   if (but_pos>i) but_pos = i;
2240 	   option_entry.caret = but_pos;
2241 	   setupOption(0);
2242 	   return;
2243 	}
2244 
2245         if (evtype==ButtonRelease && text_input==OPTION_INPUT) {
2246 	   text_input = NULL_INPUT;
2247 	   setupOption(0);
2248 	}
2249 
2250 	if (y>opth && y<opth+Context->gdata->menustrip) {
2251            but_pos = getButton(Option, x, y);
2252 	   if (but_pos>=N_OPTION)
2253 	      option_newhint = '\033';
2254 	   else
2255 	   if (but_pos>=0)
2256 	      option_newhint = OptionKey[2*but_pos];
2257 	   if (evtype==MotionNotify) {
2258 	      if (but_pos>=0)
2259                  showOptionHint(but_pos);
2260 	   }
2261 	   if (evtype==ButtonPress && but_pos>=0) {
2262 	      drawButton(Option, but_pos, 1);
2263 	      click_pos = but_pos;
2264 	   }
2265 	   if (evtype==ButtonRelease) {
2266 	      if (but_pos<0) return;
2267 	      if (but_pos<N_OPTION) {
2268 		 key = (KeySym)tolower(option_newhint);
2269 	         processKey(Option, key);
2270 		 showOptionHint(but_pos);
2271 	      } else
2272 	         PopOption(Context);
2273 	   }
2274 	}
2275 }
2276 
2277 void
showUrbanHint(str)2278 showUrbanHint(str)
2279 char * str;
2280 {
2281 	char hint[128];
2282 	int l, v;
2283 
2284 	if (!do_urban || urban_lasthint==urban_newhint) return;
2285 
2286 	*hint = '\0';
2287 
2288 	urban_lasthint = urban_newhint;
2289 	v = UrbanGeom.height - UrbanCaller->gdata->menustrip;
2290 
2291 	if (urban_newhint=='\033')
2292            strcpy(hint, Label[L_ESCMENU]);
2293 	else
2294 	if (urban_newhint==' ')
2295            strcpy(hint, Help[getNumCmd('U')]);
2296 	else {
2297 	   if (str)
2298               strncpy(hint, str, 125);
2299 	   else {
2300 	      l = getNumCmd(urban_newhint);
2301 	      if (l>=0 && l<N_HELP) {
2302 		 strcpy(hint, Help[l]);
2303 	      }
2304 	   }
2305 	}
2306 
2307 	l = strlen(hint);
2308 
2309         BasicSettings(UrbanCaller);
2310         XSetWindowBackground(dpy, Urban, UrbanCaller->gdata->pixel[MENUBGCOLOR]);
2311 
2312 	XClearArea(dpy, Urban, 0, v+1, UrbanGeom.width,
2313              UrbanCaller->gdata->menustrip-1, False);
2314         XDrawImageString(dpy, Urban, UrbanCaller->gdata->wingc,
2315               4, v + UrbanCaller->gdata->font[MENUFONT]->max_bounds.ascent + 3,
2316               hint, l);
2317 }
2318 
2319 void
updateUrbanEntries(Context,city)2320 updateUrbanEntries(Context, city)
2321 Sundata * Context;
2322 City * city;
2323 {
2324 char *ptr;
2325 int w;
2326 
2327     text_input = NULL_INPUT;
2328     if (city == NULL) return;
2329     if (city->name && city!=&Context->pos1) {
2330        w = (urban_w[0]/
2331            XTextWidth(UrbanCaller->gdata->font[MENUFONT],"_",1))-2;
2332        if (strlen(city->name)<w) w = strlen(city->name);
2333        strncpy(urban_entry[0].string, city->name, w);
2334        urban_entry[0].string[w] = '\0';
2335        urban_entry[0].caret = strlen(urban_entry[0].string);
2336     } else {
2337        urban_entry[0].string[0]='\0';
2338        urban_entry[0].caret = 0;
2339     }
2340     if (city->tz) {
2341        w = (urban_w[1]/
2342            XTextWidth(UrbanCaller->gdata->font[MENUFONT],"_",1))-2;
2343        if (strlen(city->tz)<w) w = strlen(city->tz);
2344        strncpy(urban_entry[1].string, city->tz, w);
2345        urban_entry[1].string[w] = '\0';
2346        urban_entry[1].caret = strlen(urban_entry[1].string);
2347     }
2348     ptr = num2str(city->lat, urban_entry[2].string, Context->flags.dms);
2349     urban_entry[2].caret = strlen(ptr);
2350     ptr = num2str(city->lon, urban_entry[3].string, Context->flags.dms);
2351     urban_entry[3].caret = strlen(ptr);
2352     sprintf(urban_entry[4].string, "%d", city->size);
2353     urban_entry[4].caret = 1;
2354 }
2355 
2356 void
setupUrban(mode)2357 setupUrban(mode)
2358 int mode;
2359 {
2360     int i, vskip;
2361     char s[80];
2362 
2363     if (!do_urban) return;
2364 
2365     if (mode == -2) {
2366        urban_t[0] = urban_t[2] = 7;
2367        urban_t[1] = 15 + UrbanGeom.width/2;
2368        urban_t[3] = (UrbanGeom.width - 120)/2;
2369        urban_t[4] = UrbanGeom.width - 100;
2370        urban_x[0] = 100;
2371        urban_x[1] = 100 + (UrbanGeom.width+1)/2;
2372        urban_x[2] = 80;
2373        urban_x[3] = 20 + UrbanGeom.width/2;
2374        urban_x[4] = UrbanGeom.width-40;
2375        for (i=0; i<=1; i++)
2376            urban_y[i] = UrbanCaller->gdata->mapstrip/2 - 1;
2377        for (i=2; i<=4; i++)
2378            urban_y[i] = 2*UrbanCaller->gdata->mapstrip - 1;
2379        urban_w[0] = urban_w[1] = (UrbanGeom.width-210)/2;
2380        urban_w[2] = urban_w[3] = (UrbanGeom.width-320)/2;
2381        urban_w[4] = 35;
2382        return;
2383     }
2384 
2385     BasicSettings(UrbanCaller);
2386     XSetWindowColormap(dpy, Urban, UrbanCaller->gdata->cmap);
2387     XSetWindowBackground(dpy, Urban, UrbanCaller->gdata->pixel[MENUBGCOLOR]);
2388 
2389     urbanh = UrbanGeom.height-2*UrbanCaller->gdata->menustrip - 1;
2390     vskip = 3*UrbanCaller->gdata->menustrip/8;
2391     urban_lasthint = '\0';
2392 
2393     if (mode == -1) {
2394        XClearArea(dpy, Urban, 0,0, UrbanGeom.width,
2395                                     UrbanGeom.height, False);
2396        for (i=0; i<=N_URBAN; i++)
2397 	  drawButton(Urban, i, 0);
2398 
2399        BasicSettings(UrbanCaller);
2400        for (i=0; i<=4; i++) {
2401            strcpy(s, Label[L_CITYNAME+i]);
2402            XDrawImageString(dpy, Urban, UrbanCaller->gdata->wingc,
2403 	       urban_t[i],
2404                urban_y[i]+
2405                   UrbanCaller->gdata->font[MENUFONT]->max_bounds.ascent+4,
2406                s, strlen(s));
2407            XDrawRectangle(dpy, Urban, UrbanCaller->gdata->wingc,
2408                            urban_x[i], urban_y[i], urban_w[i],
2409                            UrbanCaller->gdata->menustrip);
2410        }
2411     }
2412 
2413     for (i=0; i<=4; i++)
2414        if (text_input != URBAN_INPUT+i)
2415           showCaret(UrbanCaller, Urban, &urban_entry[i],
2416                                  urban_x[i]+6, urban_y[i], 0);
2417     for (i=0; i<=4; i++)
2418        if (text_input == URBAN_INPUT+i || text_input < URBAN_INPUT) {
2419        XSetWindowBackground(dpy, Urban, UrbanCaller->gdata->pixel[OPTIONBGCOLOR]);
2420        XClearArea(dpy, Urban, urban_x[i]+1, urban_y[i]+1,
2421               urban_w[i]-1,
2422               UrbanCaller->gdata->menustrip-1, False);
2423        XSetBackground(dpy, UrbanCaller->gdata->wingc,
2424                       UrbanCaller->gdata->pixel[OPTIONBGCOLOR]);
2425        XSetForeground(dpy, UrbanCaller->gdata->wingc,
2426                       UrbanCaller->gdata->pixel[OPTIONFGCOLOR]);
2427        XDrawImageString(dpy, Urban, UrbanCaller->gdata->wingc,
2428           urban_x[i]+6,
2429           urban_y[i]+ UrbanCaller->gdata->font[MENUFONT]->max_bounds.ascent +4,
2430           urban_entry[i].string, strlen(urban_entry[i].string));
2431        if (text_input == URBAN_INPUT+i)
2432           showCaret(UrbanCaller, Urban, &urban_entry[i],
2433                                  urban_x[i]+6, urban_y[i], 1);
2434     }
2435     if (urban_newhint == '?')
2436        urban_newhint = urban_lasthint = '(';
2437     else
2438        showUrbanHint(NULL);
2439 }
2440 
2441 void
PopUrban(Context)2442 PopUrban(Context)
2443 struct Sundata * Context;
2444 {
2445 	int    i, w, h, a, b, x=0, y=0;
2446 
2447 	do_urban = 1 - do_urban;
2448 
2449         if (!do_urban) {
2450 	  XDestroyWindow(dpy, Urban);
2451 	  Urban = 0;
2452 	  UrbanCaller = NULL;
2453 	  return;
2454 	}
2455 
2456         if (!Urban)
2457            Urban = newWindow(NULL, &UrbanGeom, 6);
2458 
2459         XSelectInput(dpy, Urban, 0);
2460         UrbanCaller = Context;
2461 
2462 	UrbanGeom.height = UrbanGeom.h_mini
2463                           = (22 * Context->gdata->menustrip)/4;
2464 	setupUrban(-2);
2465         for (i=0; i<=4; i++) {
2466 	   w = (urban_w[i]/
2467               XTextWidth(UrbanCaller->gdata->font[MENUFONT], "_", 1)) - 2;
2468 	   resetStringLength(w, &urban_entry[i]);
2469 	}
2470 
2471 	if (!getPlacement(Context->win, &Context->geom.x, &Context->geom.y, &w, &h)) {
2472 	   x = Context->geom.x + UrbanGeom.x - horiz_drift - 5;
2473 	   a = Context->geom.y + h + 6;
2474            if (do_menu && Context == MenuCaller)
2475                a += MenuGeom.height + MenuGeom.y + vert_drift + 2;
2476            b = Context->geom.y - UrbanGeom.height - UrbanGeom.y - 2*vert_drift -28;
2477            if (b < TOPTITLEBARHEIGHT ) b = TOPTITLEBARHEIGHT;
2478            if (a > (int) DisplayHeight(dpy,scr)
2479                    - 2*UrbanGeom.height -vert_drift -20)
2480               a = b;
2481 	   y = (placement<=NE)? a : b;
2482 	}
2483         setSizeHints(NULL, 6);
2484         XMoveWindow(dpy, Urban, x, y);
2485         XResizeWindow(dpy, Urban, UrbanGeom.width, UrbanGeom.height);
2486         XMapRaised(dpy, Urban);
2487         XMoveWindow(dpy, Urban, x, y);
2488         XFlush(dpy);
2489 	usleep(2*TIMESTEP);
2490 	setupUrban(-1);
2491         setProtocols(NULL, 6);
2492 }
2493 
2494 void
activateUrban()2495 activateUrban()
2496 {
2497 }
2498 
2499 void
processUrbanAction(Context,x,y,button,evtype)2500 processUrbanAction(Context, x, y, button, evtype)
2501 Sundata * Context;
2502 int x, y, button, evtype;
2503 {
2504         static int move_pos;
2505         static int click_pos;
2506         int i, j, vskip, but_pos;
2507 	KeySym key;
2508 
2509         if (evtype == MotionNotify && move_pos>=0) {
2510 	       drawButton(Urban, move_pos, -2);
2511 	       move_pos = -1;
2512 	}
2513 
2514         if (evtype == ButtonRelease && click_pos>=0) {
2515 	       drawButton(Urban, click_pos, 0);
2516                click_pos = -1;
2517 	}
2518 
2519 	vskip = 3*Context->gdata->menustrip/8;
2520 
2521 	for (i=0; i<=4; i++)
2522 	if (evtype==ButtonRelease && x>=urban_x[i]+1 &&
2523             x<=urban_x[i]+urban_w[i] &&
2524             y>=urban_y[i] && y<=urban_y[i]+Context->gdata->menustrip) {
2525 	   text_input = URBAN_INPUT+i;
2526 	   but_pos =
2527              (x-urban_x[i]-6)/XTextWidth(UrbanCaller->gdata->font[MENUFONT], "_", 1);
2528 	   j = strlen(urban_entry[i].string);
2529 	   if (but_pos<0) but_pos = 0;
2530 	   if (but_pos>j) but_pos = j;
2531 	   urban_entry[i].caret = but_pos;
2532 	   setupUrban(0);
2533 	   return;
2534 	}
2535 
2536         if (evtype==ButtonRelease) {
2537 	   text_input = NULL_INPUT;
2538 	   setupUrban(0);
2539 	}
2540 
2541 	if (y>urbanh && y<urbanh+Context->gdata->menustrip) {
2542            but_pos = getButton(Urban, x, y);
2543 	   if (but_pos>=N_URBAN)
2544 	      urban_newhint = '\033';
2545 	   else
2546 	   if (but_pos>=0)
2547 	      urban_newhint = UrbanKey[2*but_pos];
2548 	   if (evtype==MotionNotify) {
2549 	      if (but_pos>=0 && but_pos<=N_URBAN) {
2550                  showUrbanHint(NULL);
2551 		 drawButton(Urban, but_pos, -1);
2552                  move_pos = but_pos;
2553 	      }
2554 	   }
2555 	   if (evtype==ButtonPress && but_pos>=0) {
2556 	      drawButton(Urban, but_pos, 1);
2557 	      click_pos = but_pos;
2558 	   }
2559 	   if (evtype==ButtonRelease) {
2560 	      if (but_pos<0) return;
2561 	      if (but_pos<N_URBAN) {
2562 		 if (button<=2)
2563 		    key = (KeySym)tolower(urban_newhint);
2564 		 else
2565 		    key = (KeySym)urban_newhint;
2566 		 showUrbanHint(NULL);
2567 	         processKey(Urban, key);
2568 	      } else
2569 	         PopUrban(Context);
2570 	   }
2571 	}
2572 	if (evtype==MotionNotify &&
2573             (y<=urbanh || y>=urbanh+Context->gdata->menustrip)) {
2574 	   urban_newhint = 'U';
2575            showUrbanHint(NULL);
2576 	}
2577 }
2578 
2579 struct Sundata *
parentContext(Context)2580 parentContext(Context)
2581 struct Sundata * Context;
2582 {
2583    struct Sundata * ParentContext;
2584 
2585    ParentContext = Seed;
2586    while (ParentContext && ParentContext->next != Context)
2587      ParentContext = ParentContext->next;
2588    return ParentContext;
2589 }
2590 
2591 void
setAuxilWins(Context,mode)2592 setAuxilWins(Context, mode)
2593 Sundata * Context;
2594 int mode;
2595 {
2596 #define NUMWIDGETS 5
2597 
2598 
2599 int * bool_state[NUMWIDGETS] = {
2600   &do_menu, &do_filesel, &do_zoom, &do_option, &do_urban };
2601 
2602 Window * window[NUMWIDGETS] = { &Menu, &Filesel, &Zoom, &Option, &Urban };
2603 
2604 struct Sundata ** caller[NUMWIDGETS] = {
2605   &MenuCaller, &FileselCaller, &ZoomCaller, &OptionCaller, &UrbanCaller };
2606 
2607 char * char_newhint[NUMWIDGETS] = {
2608    (char *)&menu_newhint, NULL,
2609    &zoom_newhint, &option_newhint, &urban_newhint };
2610 
2611 setupCB setup_proc[NUMWIDGETS] = {
2612    &setupMenu, &setupFilesel, &setupZoom, &setupOption, &setupUrban };
2613 
2614 popCB pop_proc[NUMWIDGETS] = {
2615    &PopMenu, &PopFilesel, &PopZoom, &PopOption, &PopUrban };
2616 
2617 int i, announce=1;
2618 
2619       if (option_changes && mode==RESET) mode = REMAP;
2620 
2621       for (i=0; i<NUMWIDGETS; i++) if (*bool_state[i]) {
2622 	 if (announce && verbose && mode!=RESET) {
2623 	   fprintf(stderr,
2624               "Resetting auxiliary widgets in mode %d...\n", mode);
2625 	   announce = 0;
2626 	 }
2627 
2628 	 switch(mode) {
2629 
2630          case RESET:
2631 	    if (Context == *caller[i]) {
2632   	       *bool_state[i] = 1;
2633 	       *caller[i] = Context;
2634 	       setup_proc[i](-1);
2635 	    }
2636 	    break;
2637 
2638          case REMAP:
2639 	    if (Context != *caller[i]) break;
2640 	 case REATTRIB:
2641 	    *bool_state[i] = 0;
2642 	    if (*window[i])
2643                XUnmapWindow(dpy, *window[i]);
2644 	    if (char_newhint[i])
2645 	       *char_newhint[i] = ' ';
2646 	    pop_proc[i](Context);
2647 	    break;
2648 
2649          case DESTROY:
2650 	    if (Context == *caller[i]) {
2651 	       if (*window[i]) {
2652 		  XDestroyWindow(dpy, *window[i]);
2653                   *window[i] = 0;
2654 	       }
2655 	       *bool_state[i] = 0;
2656 	       *caller[i] = NULL;
2657 	    }
2658 	    break;
2659 
2660          case ICONIFY:
2661 	    if (Context == *caller[i])
2662 	       XIconifyWindow(dpy, *window[i], scr);
2663 	    break;
2664 
2665          case DEICONIFY:
2666 	    if (Context == *caller[i]) {
2667 	       XMapWindow(dpy, *window[i]);
2668 	       setup_proc[i](-1);
2669 	    }
2670 	    break;
2671 
2672 	 }
2673       }
2674 
2675       XFlush(dpy);
2676       if (mode == REMAP) usleep(2*TIMESTEP);
2677 }
2678 
RaiseAndFocus(win)2679 void RaiseAndFocus(win)
2680 Window win;
2681 {
2682      XFlush(dpy);
2683      XRaiseWindow(dpy, win);
2684      XSetInputFocus(dpy, win, RevertToPointerRoot, CurrentTime);
2685 }
2686 
2687 /*
2688  * Free GC's.
2689  */
2690 
2691 void
destroyGCs(Context)2692 destroyGCs(Context)
2693 Sundata * Context;
2694 {
2695 int i;
2696 	 if (Context->gdata->links>0) {
2697             --Context->gdata->links;
2698 	    return;
2699 	 }
2700 
2701          XFreeGC(dpy, Context->gdata->wingc);
2702 	 Context->gdata->wingc = 0;
2703          XFreeGC(dpy, Context->gdata->pixgc);
2704          Context->gdata->pixgc = 0;
2705 
2706          if (runlevel!=IMAGERECYCLE && Context->gdata->cmap!=cmap0)
2707 	    XFreeColormap(dpy, Context->gdata->cmap);
2708 
2709 	 for (i=0; i<NUMFONTS; i++)
2710              XFreeFont(dpy, Context->gdata->font[i]);
2711 
2712 	 free(Context->gdata);
2713 }
2714 
2715 /*
2716  * Free resources.
2717  */
2718 
2719 void
shutDown(Context,all)2720 shutDown(Context, all)
2721 struct Sundata * Context;
2722 int all;
2723 {
2724         struct Sundata * ParentContext, *NextContext;
2725 
2726 	if (all<0)
2727 	   Context = Seed;
2728 
2729       repeat:
2730 	fflush(stderr);
2731 
2732         NextContext = Context->next;
2733         ParentContext = parentContext(Context);
2734 
2735 	if (runlevel!=IMAGERECYCLE) {
2736   	   if (Context->xim) {
2737               XDestroyImage(Context->xim);
2738               Context->xim = NULL;
2739 	   }
2740 	   if (Context->ximdata) {
2741 	      free(Context->ximdata);
2742 	      Context->ximdata = NULL;
2743 	   }
2744 	   while (Context->label) {
2745 	      struct TextLabel *ptr;
2746 	      ptr = (Context->label)->next;
2747               free(Context->label->text);
2748               free(Context->label);
2749 	      Context->label = ptr;
2750 	   }
2751 	   if (Context->mappix) {
2752               XFreePixmap(dpy, Context->mappix);
2753 	      Context->mappix = 0;
2754  	   }
2755            if (Context->daypixel) {
2756 	      free(Context->daypixel);
2757 	      Context->daypixel = NULL;
2758            }
2759            if (Context->nightpixel) {
2760 	      free(Context->nightpixel);
2761 	      Context->nightpixel = NULL;
2762            }
2763            if (Context->vmfpixels) {
2764 	      free(Context->vmfpixels);
2765               Context->vmfpixels = NULL;
2766 	   }
2767            if (Context->tr1) {
2768               free(Context->tr1);
2769               Context->tr1 = NULL;
2770            }
2771            if (Context->tr2) {
2772               free(Context->tr2);
2773               Context->tr2 = NULL;
2774            }
2775            if (Context->daywave) {
2776               free(Context->daywave);
2777               Context->daywave = NULL;
2778 	   }
2779 	}
2780         destroyGCs(Context);
2781 
2782 	Context->flags.hours_shown = 0;
2783 
2784         if (all) {
2785 	   last_time = 0;
2786 
2787            if (Context->win) {
2788 	      setAuxilWins(Context, DESTROY);
2789 	      if (Context == RootCaller) {
2790                  RootCaller = NULL;
2791 		 do_root = 0;
2792 	      }
2793 	      XDestroyWindow(dpy, Context->win);
2794   	      Context->win = 0;
2795 	   }
2796            if (Context->clock_img_file) {
2797               free(Context->clock_img_file);
2798 	      Context->clock_img_file = NULL;
2799 	   }
2800            if (Context->map_img_file) {
2801               free(Context->map_img_file);
2802 	      Context->map_img_file = NULL;
2803 	   }
2804 
2805            free(Context->spotsizes);
2806            free(Context->sizelimits);
2807 
2808 	   if (all<0) {
2809 	      free(Context);
2810 	      if (NextContext) {
2811 	         Context = NextContext;
2812 	         goto repeat;
2813 	      }
2814 	      else {
2815 	        endup:
2816                  if (zoompix) XFreePixmap(dpy, zoompix);
2817                  if (textpix) XFreePixmap(dpy, textpix);
2818                  if (rootpix) XFreePixmap(dpy, rootpix);
2819                  XCloseDisplay(dpy);
2820          	 if (dirtable) free(dirtable);
2821          	 exit(0);
2822  	      }
2823  	   }
2824 	   if (ParentContext)
2825 	      ParentContext->next = Context->next;
2826 	   else
2827               Seed = Context->next;
2828 	   free(Context);
2829            if (Seed == NULL) goto endup;
2830 	}
2831 }
2832