1 /*
2  * FIG : Facility for Interactive Generation of figures
3  * Copyright (c) 1985-1988 by Supoj Sutanthavibul
4  * Parts Copyright (c) 1989-2007 by Brian V. Smith
5  * Parts Copyright (c) 1991 by Paul King
6  *
7  * Any party obtaining a copy of these files is granted, free of charge, a
8  * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
9  * nonexclusive right and license to deal in this software and documentation
10  * files (the "Software"), including without limitation the rights to use,
11  * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
12  * the Software, and to permit persons who receive copies from any such
13  * party to do so, with the only requirement being that the above copyright
14  * and this permission notice remain intact.
15  *
16  */
17 
18 #include "fig.h"
19 #include "figx.h"
20 #include "resources.h"
21 #include "main.h"
22 #include "mode.h"
23 #include "object.h"
24 #include "f_util.h"
25 #include "u_redraw.h"
26 #include "w_canvas.h"
27 #include "w_cmdpanel.h"
28 #include "w_drawprim.h"
29 #include "w_export.h"
30 #include "w_indpanel.h"
31 #include "w_color.h"
32 #include "w_layers.h"
33 #include "w_msgpanel.h"
34 #include "w_print.h"
35 #include "w_util.h"
36 #include "w_setup.h"
37 
38 #include <X11/IntrinsicP.h> /* XtResizeWidget() */
39 
40 #ifdef I18N
41 #include "d_text.h"
42 #endif /* I18N */
43 
44 /* EXPORTS */
45 
46 char	 *grid_inch_choices[] = { "None", "1/16", "1/8", "1/4", "1/2", "1", "2", "5", "10" };
47 int	  num_grid_inch_choices = sizeof(grid_inch_choices) / sizeof(char *);
48 
49 char	 *grid_tenth_inch_choices[] = { "None", "1/10", "1/5", "1/2", "1", "2", "5", "10" };
50 int	  num_grid_tenth_inch_choices = sizeof(grid_tenth_inch_choices) / sizeof(char *);
51 
52 char	 *grid_cm_choices[] = { "None", "1 mm", "2 mm", "5 mm", "10 mm",
53 					"20 mm", "50 mm", "100 mm" };
54 int	  num_grid_cm_choices = sizeof(grid_cm_choices) / sizeof(char *);
55 
56 char	**grid_choices;
57 int	  n_grid_choices, grid_minor, grid_major;
58 Pixmap	  check_pm, null_check_pm;
59 Pixmap	  sm_check_pm, sm_null_check_pm;
60 Pixmap	  balloons_on_bitmap=(Pixmap) 0;
61 Pixmap	  balloons_off_bitmap=(Pixmap) 0;
62 Pixmap	  menu_arrow, menu_cascade_arrow;
63 Pixmap	  arrow_pixmaps[NUM_ARROW_TYPES+1];
64 Pixmap	  diamond_pixmap;
65 Pixmap	  linestyle_pixmaps[NUM_LINESTYLE_TYPES];
66 Pixmap	  mouse_l=(Pixmap) 0,		/* mouse indicator bitmaps for the balloons */
67 	  mouse_r=(Pixmap) 0;
68 
69 /* LOCALS */
70 
71 DeclareStaticArgs(14);
72 static void _installscroll(Widget parent, Widget widget);
73 
74 static Pixmap	spinup_bm=0;	/* pixmaps for spinners */
75 static Pixmap	spindown_bm=0;
76 static void	validate_int(Widget w, XtPointer info, XtPointer dum);	/* validation for spinners */
77 static void	convert_gridstr(Widget widget, float mult);
78 
79 /* for internal consumption only (use MakeIntSpinnerEntry or MakeFloatSpinnerEntry) */
80 static Widget	MakeSpinnerEntry(Widget parent, Widget *text, char *name, Widget below, Widget beside, XtCallbackProc callback, char *string, int type, float min, float max, float inc, int width);
81 
82 /* bitmap for checkmark */
83 static unsigned char check_bits[] = {
84    0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x7f,
85    0x8c, 0x1f, 0xde, 0x07, 0xfe, 0x03, 0xfc, 0x01, 0xf8, 0x00, 0xf0, 0x00,
86    0x70, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00};
87 
88 /* smaller checkmark */
89 /* sm_check_width and _height are defined in w_util.c so w_layers.c can use them */
90 static unsigned char sm_check_bits[] = {
91    0x00, 0x00, 0x80, 0x01, 0xc0, 0x01, 0xe0, 0x01, 0x76, 0x00, 0x3e, 0x00,
92    0x1c, 0x00, 0x18, 0x00, 0x08, 0x00, 0x00, 0x00};
93 
94 #define balloons_on_width 16
95 #define balloons_on_height 15
96 static unsigned char balloons_on_bits[] = {
97    0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x67, 0xfe, 0x63, 0xfe, 0x71, 0xfe, 0x79,
98    0xfe, 0x7c, 0xe2, 0x7c, 0x46, 0x7e, 0x0e, 0x7e, 0x0e, 0x7f, 0x1e, 0x7f,
99    0x9e, 0x7f, 0xfe, 0x7f, 0x00, 0x00};
100 
101 #define balloons_off_width 16
102 #define balloons_off_height 15
103 static unsigned char balloons_off_bits[] = {
104    0xff, 0xff, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
105    0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
106    0x01, 0x80, 0x01, 0x80, 0xff, 0xff};
107 
108 /* spinner up/down icons */
109 
110 #define spinup_width 9
111 #define spinup_height 6
112 static unsigned char spinup_bits[] = {
113    0x10, 0x00, 0x38, 0x00, 0x7c, 0x00, 0xfe, 0x00, 0xff, 0x01, 0x00, 0x00};
114 #define spindown_width 9
115 #define spindown_height 6
116 static unsigned char spindown_bits[] = {
117    0x00, 0x00, 0xff, 0x01, 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00};
118 
119 #define mouse_r_width 19
120 #define mouse_r_height 10
121 static unsigned char mouse_r_bits[] = {
122    0xff, 0xff, 0x07, 0x41, 0xf0, 0x07, 0x41, 0xf0, 0x07, 0x41, 0xf0, 0x07,
123    0x41, 0xf0, 0x07, 0x41, 0xf0, 0x07, 0x41, 0xf0, 0x07, 0x41, 0xf0, 0x07,
124    0x41, 0xf0, 0x07, 0xff, 0xff, 0x07};
125 
126 #define mouse_l_width 19
127 #define mouse_l_height 10
128 static unsigned char mouse_l_bits[] = {
129    0xff, 0xff, 0x07, 0x7f, 0x10, 0x04, 0x7f, 0x10, 0x04, 0x7f, 0x10, 0x04,
130    0x7f, 0x10, 0x04, 0x7f, 0x10, 0x04, 0x7f, 0x10, 0x04, 0x7f, 0x10, 0x04,
131    0x7f, 0x10, 0x04, 0xff, 0xff, 0x07};
132 
133 /* arrow for pull-down menus */
134 
135 #define menu_arrow_width 11
136 #define menu_arrow_height 13
137 static unsigned char menu_arrow_bits[] = {
138   0xf8,0x00,0xd8,0x00,0xa8,0x00,0xd8,0x00,0xa8,0x00,0xd8,0x00,
139   0xaf,0x07,0x55,0x05,0xaa,0x02,0x54,0x01,0xa8,0x00,0x50,0x00,
140   0x20,0x00};
141 
142 /* arrow for cascade menu entries */
143 
144 #define menu_cascade_arrow_width 10
145 #define menu_cascade_arrow_height 12
146 static unsigned char menu_cascade_arrow_bits[] = {
147    0x00, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x3e, 0x00, 0xfe, 0x00, 0xfe, 0x01,
148    0xfe, 0x01, 0xfe, 0x00, 0x3e, 0x00, 0x0e, 0x00, 0x02, 0x00, 0x00, 0x00};
149 
150 /* diamond for library menu to indicate toplevel files.
151  * For the XAW3D1_5E version, there is a single point on the left edge and the
152  * diamond is moved to the right because the menu's 3D edge obscures it otherwise */
153 
154 #ifdef XAW3D1_5E
155 #define diamond_width 12
156 #define diamond_height 7
157 static unsigned char diamond_bits[] = {
158    0x00, 0x01, 0x80, 0x03, 0xc0, 0x07, 0xe1,
159    0x0f, 0xc0, 0x07, 0x80, 0x03, 0x00, 0x01};
160 #else
161 #define diamond_width 7
162 #define diamond_height 7
163 static unsigned char diamond_bits[] = {
164    0x08, 0x1c, 0x3e, 0x7f, 0x3e, 0x1c, 0x08};
165 #endif /* XAW3D1_5E */
166 
167 /* popup a confirmation window */
168 
169 static int	query_result, query_done;
170 static String   query_translations =
171         "<Message>WM_PROTOCOLS: DismissQuery()\n";
172 static void     accept_cancel(Widget widget, XtPointer closure, XtPointer call_data);
173 static XtActionsRec     query_actions[] =
174 {
175     {"DismissQuery", (XtActionProc) accept_cancel},
176 };
177 
178 /* synchronize so that (e.g.) new cursor appears etc. */
179 
180 
181 
app_flush(void)182 void app_flush(void)
183 {
184 	/* this method prevents "ghost" rubberbanding when the user
185 	   moves the mouse after creating/resizing object */
186 	XSync(tool_d, False);
187 }
188 
189 static void
accept_yes(Widget widget,XtPointer closure,XtPointer call_data)190 accept_yes(Widget widget, XtPointer closure, XtPointer call_data)
191 {
192     query_done = 1;
193     query_result = RESULT_YES;
194 }
195 
196 static void
accept_no(Widget widget,XtPointer closure,XtPointer call_data)197 accept_no(Widget widget, XtPointer closure, XtPointer call_data)
198 {
199     query_done = 1;
200     query_result = RESULT_NO;
201 }
202 
203 static void
accept_cancel(Widget widget,XtPointer closure,XtPointer call_data)204 accept_cancel(Widget widget, XtPointer closure, XtPointer call_data)
205 {
206     query_done = 1;
207     query_result = RESULT_CANCEL;
208 }
209 
210 static void
accept_part(Widget widget,XtPointer closure,XtPointer call_data)211 accept_part(Widget widget, XtPointer closure, XtPointer call_data)
212 {
213     query_done = 1;
214     query_result = RESULT_PART;
215 }
216 
217 static void
accept_all(Widget widget,XtPointer closure,XtPointer call_data)218 accept_all(Widget widget, XtPointer closure, XtPointer call_data)
219 {
220     query_done = 1;
221     query_result = RESULT_ALL;
222 }
223 
224 int
popup_query(int query_type,char * message)225 popup_query(int query_type, char *message)
226 {
227     Widget	    query_popup, query_form, query_message;
228     Widget	    query_yes, query_no, query_cancel;
229     Widget	    query_part, query_all;
230     int		    xposn, yposn;
231     Window	    win;
232     XEvent	    event;
233     static Boolean  actions_added=False;
234 
235     XTranslateCoordinates(tool_d, main_canvas, XDefaultRootWindow(tool_d),
236 			  150, 200, &xposn, &yposn, &win);
237     FirstArg(XtNallowShellResize, True);
238     NextArg(XtNx, xposn);
239     NextArg(XtNy, yposn);
240     NextArg(XtNborderWidth, POPUP_BW);
241     NextArg(XtNtitle, "Xfig: Query");
242     NextArg(XtNcolormap, tool_cm);
243     query_popup = XtCreatePopupShell("query_popup", transientShellWidgetClass,
244 				     tool, Args, ArgCount);
245     XtOverrideTranslations(query_popup,
246                        XtParseTranslationTable(query_translations));
247     if (!actions_added) {
248         XtAppAddActions(tool_app, query_actions, XtNumber(query_actions));
249 	actions_added = True;
250     }
251 
252     FirstArg(XtNdefaultDistance, 10);
253     query_form = XtCreateManagedWidget("query_form", formWidgetClass,
254 				       query_popup, Args, ArgCount);
255 
256     FirstArg(XtNfont, bold_font);
257     NextArg(XtNborderWidth, 0);
258     NextArg(XtNlabel, message);
259     query_message = XtCreateManagedWidget("message", labelWidgetClass,
260 					  query_form, Args, ArgCount);
261 
262     FirstArg(XtNheight, 25);
263     NextArg(XtNvertDistance, 15);
264     NextArg(XtNfromVert, query_message);
265     NextArg(XtNborderWidth, INTERNAL_BW);
266     NextArg(XtNhorizDistance, 55);
267 
268     if (query_type == QUERY_ALLPARTCAN) {
269 	NextArg(XtNlabel, "Save All ");
270 	query_all = XtCreateManagedWidget("all", commandWidgetClass,
271 				      query_form, Args, ArgCount);
272 	XtAddCallback(query_all, XtNcallback, accept_all, (XtPointer) NULL);
273 	ArgCount = 4;
274 	NextArg(XtNhorizDistance, 25);
275 	NextArg(XtNlabel, "Save Part");
276 	NextArg(XtNfromHoriz, query_all);
277 	query_part = XtCreateManagedWidget("part", commandWidgetClass,
278 					 query_form, Args, ArgCount);
279 	XtAddCallback(query_part, XtNcallback, accept_part, (XtPointer) NULL);
280 	/* setup for the cancel button */
281 	ArgCount = 5;
282 	NextArg(XtNfromHoriz, query_part);
283 
284     } else if (query_type == QUERY_OK) {
285 	/* just OK button */
286 	NextArg(XtNlabel, " Ok  ");
287 	query_yes = XtCreateManagedWidget("ok", commandWidgetClass,
288 				query_form, Args, ArgCount);
289 	/* just use the accept_yes callback because the caller only gets one result anyway */
290 	XtAddCallback(query_yes, XtNcallback, accept_yes, (XtPointer) NULL);
291 
292     } else {
293 	/* yes/no or yes/no/cancel */
294 
295 	NextArg(XtNlabel, " Yes  ");
296 	query_yes = XtCreateManagedWidget("yes", commandWidgetClass,
297 				query_form, Args, ArgCount);
298 	XtAddCallback(query_yes, XtNcallback, accept_yes, (XtPointer) NULL);
299 
300 	if (query_type == QUERY_YESNO || query_type == QUERY_YESNOCAN) {
301 	    ArgCount = 4;
302 	    NextArg(XtNhorizDistance, 25);
303 	    NextArg(XtNlabel, "  No  ");
304 	    NextArg(XtNfromHoriz, query_yes);
305 	    query_no = XtCreateManagedWidget("no", commandWidgetClass,
306 				query_form, Args, ArgCount);
307 	    XtAddCallback(query_no, XtNcallback, accept_no, (XtPointer) NULL);
308 
309 	    /* setup for the cancel button */
310 	    ArgCount = 5;
311 	    NextArg(XtNfromHoriz, query_no);
312 	} else {
313 	    /* setup for the cancel button */
314 	    ArgCount = 4;
315 	    NextArg(XtNhorizDistance, 25);
316 	    NextArg(XtNfromHoriz, query_yes);
317 	}
318     }
319 
320     if (query_type == QUERY_YESCAN || query_type == QUERY_YESNOCAN ||
321 	query_type == QUERY_ALLPARTCAN) {
322 	    NextArg(XtNlabel, "Cancel");
323 	    query_cancel = XtCreateManagedWidget("cancel", commandWidgetClass,
324 						query_form, Args, ArgCount);
325 	    XtAddCallback(query_cancel, XtNcallback, accept_cancel, (XtPointer) NULL);
326     }
327 
328     XtPopup(query_popup, XtGrabExclusive);
329     /* insure that the most recent colormap is installed */
330     set_cmap(XtWindow(query_popup));
331     (void) XSetWMProtocols(tool_d, XtWindow(query_popup), &wm_delete_window, 1);
332     XDefineCursor(tool_d, XtWindow(query_popup), arrow_cursor);
333 
334     query_done = 0;
335     while (!query_done) {
336 	/* pass events */
337 	XNextEvent(tool_d, &event);
338 	XtDispatchEvent(&event);
339     }
340 
341     XtPopdown(query_popup);
342     XtDestroyWidget(query_popup);
343 
344     return (query_result);
345 }
346 
347 /*
348  * make a pulldown menu with "nent" button entries (labels) that call "callback"
349  * when pressed.
350  * Additionally, make a dividing line in the menu at the position "divide_line"
351  * (use -1 if no line desired)
352  */
353 
354 #ifndef XAW3D1_5E
355 #include "SmeCascade.h"
356 #endif /* XAW3D1_5E */
357 
358 #include "d_text.h"
359 #include "e_placelib.h"
360 #include "w_rulers.h"
361 
362 Widget
make_pulldown_menu(char ** entries,Cardinal nent,int divide_line,char * divide_message,Widget parent,XtCallbackProc callback)363 make_pulldown_menu(char **entries, Cardinal nent, int divide_line, char *divide_message, Widget parent, XtCallbackProc callback)
364 {
365     Widget	    pulldown_menu, entry;
366     intptr_t	    i;
367 
368     pulldown_menu = XtCreatePopupShell("menu", simpleMenuWidgetClass, parent,
369 				  NULL, ZERO);
370 
371     for (i = 0; i < nent; i++) {
372 	if (i == divide_line) {
373 	    (void) XtCreateManagedWidget(entries[i], smeLineObjectClass, pulldown_menu,
374 					NULL, ZERO);
375 	    FirstArg(XtNlabel, divide_message);
376 	    (void) XtCreateManagedWidget("menu_divider", smeBSBObjectClass, pulldown_menu,
377 					Args, ArgCount);
378 	    (void) XtCreateManagedWidget(entries[i], smeLineObjectClass, pulldown_menu,
379 					NULL, ZERO);
380 	}
381 	entry = XtCreateManagedWidget(entries[i], smeBSBObjectClass, pulldown_menu,
382 					NULL, ZERO);
383 	XtAddCallback(entry, XtNcallback, callback, (XtPointer) i);
384     }
385     return pulldown_menu;
386 }
387 
388 static void
CvtStringToFloat(XrmValuePtr args,Cardinal * num_args,XrmValuePtr fromVal,XrmValuePtr toVal)389 CvtStringToFloat(XrmValuePtr args, Cardinal *num_args, XrmValuePtr fromVal, XrmValuePtr toVal)
390 {
391     static float    f;
392 
393     if (*num_args != 0)
394 	XtWarning("String to Float conversion needs no extra arguments");
395     if (sscanf((char *) fromVal->addr, "%f", &f) == 1) {
396 	(*toVal).size = sizeof(float);
397 	(*toVal).addr = (caddr_t) & f;
398     } else
399 	XtStringConversionWarning((char *) fromVal->addr, "Float");
400 }
401 
402 static void
CvtIntToFloat(XrmValuePtr args,Cardinal * num_args,XrmValuePtr fromVal,XrmValuePtr toVal)403 CvtIntToFloat(XrmValuePtr args, Cardinal *num_args, XrmValuePtr fromVal, XrmValuePtr toVal)
404 {
405     static float    f;
406 
407     if (*num_args != 0)
408 	XtWarning("Int to Float conversion needs no extra arguments");
409     f = *(int *) fromVal->addr;
410     (*toVal).size = sizeof(float);
411     (*toVal).addr = (caddr_t) & f;
412 }
413 
fix_converters(void)414 void fix_converters(void)
415 {
416     XtAppAddConverter(tool_app, "String", "Float", CvtStringToFloat, NULL, 0);
417     XtAppAddConverter(tool_app, "Int", "Float", CvtIntToFloat, NULL, 0);
418 }
419 
420 
421 static void
cancel_color(Widget w,XtPointer widget,XtPointer dum1)422 cancel_color(Widget w, XtPointer widget, XtPointer dum1)
423 {
424     XtPopdown((Widget)widget);
425 }
426 
427 Widget
make_color_popup_menu(Widget parent,char * name,XtCallbackProc callback,Boolean include_transp,Boolean include_backg)428 make_color_popup_menu(Widget parent, char *name, XtCallbackProc callback, Boolean include_transp, Boolean include_backg)
429 {
430     Widget	    pop_menu, pop_form, color_box;
431     Widget	    viewp, entry, label;
432     intptr_t	    i;
433     char	    buf[30];
434     Position	    x_val, y_val;
435     Dimension	    height;
436     int		    wd, ht;
437     Pixel	    bgcolor;
438     Boolean	    first;
439 
440     /* position selection panel just below button */
441     FirstArg(XtNheight, &height);
442     GetValues(parent);
443     XtTranslateCoords(parent, (Position) 0, (Position) height,
444 		      &x_val, &y_val);
445 
446     /* by using the name "menu", the menuButton that is using this
447      * shell will know to pop it up, since that is the default menu name */
448     FirstArg(XtNx, x_val);
449     NextArg(XtNy, y_val+4);
450     pop_menu = XtCreatePopupShell("menu",
451 			         overrideShellWidgetClass, parent,
452 				 Args, ArgCount);
453 
454     /* outer box containing label, color box, and cancel button */
455     FirstArg(XtNorientation, XtorientVertical);
456     pop_form = XtCreateManagedWidget("color_menu_form", formWidgetClass,
457 				    pop_menu, Args, ArgCount);
458 
459     /* get the background color of the form for the "Background" button */
460     FirstArg(XtNbackground, &bgcolor);
461     GetValues(pop_form);
462 
463     FirstArg(XtNlabel, name);
464     label = XtCreateManagedWidget("color_menu_label", labelWidgetClass,
465 				    pop_form, Args, ArgCount);
466 
467     /* put the Background, None and Default outside the box/viewport */
468     first = True;
469     for (i=(include_transp? TRANSP_BACKGROUND:
470 			    include_backg? COLOR_NONE: DEFAULT); i<0; i++) {
471 	set_color_name(i,buf);
472 	FirstArg(XtNwidth, COLOR_BUT_WID);
473 	NextArg(XtNborderWidth, COLOR_BUT_BD_WID);
474 	if (!first) {
475 	    NextArg(XtNfromHoriz, entry);
476 	}
477 	first = False;
478 	NextArg(XtNfromVert, label);
479 	if (i==TRANSP_BACKGROUND) {
480 	    /* make its background transparent by using color of parent */
481 	    NextArg(XtNforeground, black_color.pixel);
482 	    NextArg(XtNbackground, bgcolor);
483 	    /* and it is a little wider due to the longer name */
484 	    NextArg(XtNwidth, COLOR_BUT_WID+14);
485 	} else if (i==TRANSP_NONE) {
486 	    NextArg(XtNforeground, black_color.pixel);
487 	    NextArg(XtNbackground, white_color.pixel);
488 	} else {
489 	    NextArg(XtNforeground, white_color.pixel);
490 	    NextArg(XtNbackground, black_color.pixel);
491 	}
492 	entry = XtCreateManagedWidget(buf, commandWidgetClass, pop_form, Args, ArgCount);
493 	XtAddCallback(entry, XtNcallback, callback, (XtPointer) i);
494     }
495 
496     /* make a scrollable viewport in case all the buttons don't fit */
497 
498     FirstArg(XtNallowVert, True);
499     NextArg(XtNallowHoriz, False);
500     NextArg(XtNfromVert, entry);
501     if (num_usr_cols > 8) {
502 	/* allow for width of scrollbar */
503 	wd = 4*(COLOR_BUT_WID+2*COLOR_BUT_BD_WID)+25;
504     } else {
505 	wd = 4*(COLOR_BUT_WID+2*COLOR_BUT_BD_WID);
506     }
507     if (num_usr_cols == 0) {
508 	ht = 8*(COLOR_BUT_HT+2*COLOR_BUT_BD_WID+3);
509     } else {
510 	ht = 12*(COLOR_BUT_HT+2*COLOR_BUT_BD_WID+3);
511     }
512     NextArg(XtNwidth, wd);
513     NextArg(XtNheight, ht);
514     NextArg(XtNborderWidth, 1);
515     viewp = XtCreateManagedWidget("color_viewp", viewportWidgetClass,
516 				  pop_form, Args, ArgCount);
517 
518     FirstArg(XtNorientation, XtorientVertical);
519     NextArg(XtNhSpace, 0);	/* small space between entries */
520     NextArg(XtNvSpace, 0);
521     color_box = XtCreateManagedWidget("color_box", boxWidgetClass,
522 				    viewp, Args, ArgCount);
523 
524     /* now make the buttons in the box */
525     for (i = 0; i < NUM_STD_COLS+num_usr_cols; i++) {
526 	XColor		xcolor;
527 	Pixel		col;
528 
529 	/* only those user-defined colors that are defined */
530 	if (i >= NUM_STD_COLS && colorFree[i-NUM_STD_COLS])
531 	    continue;
532 	set_color_name(i,buf);
533 	FirstArg(XtNwidth, COLOR_BUT_WID);
534 	NextArg(XtNborderWidth, COLOR_BUT_BD_WID);
535 	if (all_colors_available) {
536 	    xcolor.pixel = colors[i];
537 	    /* get RGB of the color to check intensity */
538 	    XQueryColor(tool_d, tool_cm, &xcolor);
539 	    /* make contrasting label */
540 	    if ((0.3 * xcolor.red + 0.59 * xcolor.green + 0.11 * xcolor.blue) <
541 			0.55 * (255 << 8))
542 		col = colors[WHITE];
543 	    else
544 		col = colors[BLACK];
545 	    NextArg(XtNforeground, col);
546 	    NextArg(XtNbackground, colors[i]);
547 	}
548 	entry = XtCreateManagedWidget(buf, commandWidgetClass, color_box,
549 				      Args, ArgCount);
550 	XtAddCallback(entry, XtNcallback, callback, (XtPointer) i);
551     }
552 
553     /* make the cancel button */
554     FirstArg(XtNlabel, "Cancel");
555     NextArg(XtNfromVert, viewp);
556     entry = XtCreateManagedWidget(buf, commandWidgetClass, pop_form,
557 				      Args, ArgCount);
558     XtAddCallback(entry, XtNcallback, (XtCallbackProc) cancel_color,
559 		  (XtPointer) pop_menu);
560 
561     return pop_menu;
562 }
563 
564 void
set_color_name(int color,char * buf)565 set_color_name(int color, char *buf)
566 {
567     if (color == TRANSP_NONE)
568 	sprintf(buf,"None");
569     else if (color == TRANSP_BACKGROUND)
570 	sprintf(buf,"Background");
571     else if (color == DEFAULT || (color >= 0 && color < NUM_STD_COLS))
572 	sprintf(buf, "%s", colorNames[color + 1].name);
573     else
574 	sprintf(buf, "User %d", color);
575 }
576 
577 /*
578  * Set the color name in the label of widget, set its foreground to
579  * that color, and set its background to a contrasting color
580  */
581 
582 void
set_but_col(Widget widget,Pixel color)583 set_but_col(Widget widget, Pixel color)
584 {
585 	XColor		 xcolor;
586 	Pixel		 but_col;
587 	char		 buf[50];
588 
589 	/* put the color name in the label and the color itself as the background */
590 	set_color_name(color, buf);
591 	but_col = x_color(color);
592 	FirstArg(XtNlabel, buf);
593 	NextArg(XtNbackground, but_col);  /* set color of button */
594 	SetValues(widget);
595 
596 	/* now set foreground to contrasting color */
597 	xcolor.pixel = but_col;
598 	XQueryColor(tool_d, tool_cm, &xcolor);
599 	pick_contrast(xcolor, widget);
600 }
601 
602 static void
inc_flt_spinner(Widget widget,XtPointer info,XtPointer dum)603 inc_flt_spinner(Widget widget, XtPointer info, XtPointer dum)
604 {
605     float val;
606     char *sval,str[40];
607     spin_struct *spins = (spin_struct*) info;
608     Widget textwidg = (Widget) spins->widget;
609 
610     FirstArg(XtNstring, &sval);
611     GetValues(textwidg);
612     val = (float) atof(sval);
613     val += spins->inc;
614     if (val < spins->min)
615 	val = spins->min;
616     if (val > spins->max)
617 	val = spins->max;
618     sprintf(str,"%0.2f",val);
619     FirstArg(XtNstring, str);
620     SetValues(textwidg);
621     FirstArg(XtNinsertPosition, strlen(str));
622     SetValues(textwidg);
623 }
624 
625 static void
dec_flt_spinner(Widget widget,XtPointer info,XtPointer dum)626 dec_flt_spinner(Widget widget, XtPointer info, XtPointer dum)
627 {
628     float val;
629     char *sval,str[40];
630     spin_struct *spins = (spin_struct*) info;
631     Widget textwidg = (Widget) spins->widget;
632 
633 
634     FirstArg(XtNstring, &sval);
635     GetValues(textwidg);
636     val = (float) atof(sval);
637     val -= spins->inc;
638     if (val < spins->min)
639 	val = spins->min;
640     if (val > spins->max)
641 	val = spins->max;
642     sprintf(str,"%0.2f",val);
643     FirstArg(XtNstring, str);
644     SetValues(textwidg);
645     FirstArg(XtNinsertPosition, strlen(str));
646     SetValues(textwidg);
647 }
648 
649 static void
inc_int_spinner(Widget widget,XtPointer info,XtPointer dum)650 inc_int_spinner(Widget widget, XtPointer info, XtPointer dum)
651 {
652     int     val;
653     char   *sval,str[40];
654     spin_struct *spins = (spin_struct*) info;
655     Widget textwidg = (Widget) spins->widget;
656 
657     FirstArg(XtNstring, &sval);
658     GetValues(textwidg);
659     val = (int) atof(sval);
660     val += (int) spins->inc;
661     if (val < (int) spins->min)
662 	val = (int) spins->min;
663     if (val > (int) spins->max)
664 	val = (int) spins->max;
665     sprintf(str,"%d",val);
666     FirstArg(XtNstring, str);
667     SetValues(textwidg);
668     FirstArg(XtNinsertPosition, strlen(str));
669     SetValues(textwidg);
670 }
671 
672 static void
dec_int_spinner(Widget widget,XtPointer info,XtPointer dum)673 dec_int_spinner(Widget widget, XtPointer info, XtPointer dum)
674 {
675     int     val;
676     char   *sval,str[40];
677     spin_struct *spins = (spin_struct*) info;
678     Widget textwidg = (Widget) spins->widget;
679 
680     FirstArg(XtNstring, &sval);
681     GetValues(textwidg);
682     val = (int) atof(sval);
683     val -= (int) spins->inc;
684     if (val < (int) spins->min)
685 	val = (int) spins->min;
686     if (val > (int) spins->max)
687 	val = (int) spins->max;
688     sprintf(str,"%d",val);
689     FirstArg(XtNstring, str);
690     SetValues(textwidg);
691     FirstArg(XtNinsertPosition, strlen(str));
692     SetValues(textwidg);
693 }
694 
695 /***********************************************************************/
696 /* Code to handle automatic spinning when user holds down mouse button */
697 /***********************************************************************/
698 
699 static void /* XtTimerCallbackProc */ auto_spin(XtPointer client_data, XtIntervalId *id);
700 static XtIntervalId	   auto_spinid;
701 static void /* XtEventHandler */   stop_spin_timer(int widget, int data, int event);
702 static Widget		   cur_spin = (Widget) 0;
703 
704 static void /* XtEventHandler */
start_spin_timer(Widget widget,XtPointer data,XEvent event)705 start_spin_timer(Widget widget, XtPointer data, XEvent event)
706 {
707     auto_spinid = XtAppAddTimeOut(tool_app, appres.spinner_delay,
708 				(XtTimerCallbackProc) auto_spin, (XtPointer) NULL);
709     /* add event to cancel timer when user releases button */
710     XtAddEventHandler(widget, ButtonReleaseMask, False,
711 			  (XtEventHandler) stop_spin_timer, (XtPointer) NULL);
712     /* keep track of which one the user is pressing */
713     cur_spin = widget;
714 
715     return;
716 }
717 
718 static void /* XtEventHandler */
stop_spin_timer(int widget,int data,int event)719 stop_spin_timer(int widget, int data, int event)
720 {
721     XtRemoveTimeOut(auto_spinid);
722 
723     return;
724 }
725 
726 static void /* XtTimerCallbackProc */
auto_spin(XtPointer client_data,XtIntervalId * id)727 auto_spin(XtPointer client_data, XtIntervalId *id)
728 {
729     auto_spinid = XtAppAddTimeOut(tool_app, appres.spinner_rate,
730 				(XtTimerCallbackProc) auto_spin, (XtPointer) NULL);
731     /* call the proper spinup/down routine */
732     XtCallCallbacks(cur_spin, XtNcallback, 0);
733 
734     return;
735 }
736 
737 /***************************/
738 /* make an integer spinner */
739 /***************************/
740 
741 Widget
MakeIntSpinnerEntry(Widget parent,Widget * text,char * name,Widget below,Widget beside,XtCallbackProc callback,char * string,int min,int max,int inc,int width)742 MakeIntSpinnerEntry(Widget parent, Widget *text, char *name, Widget below, Widget beside, XtCallbackProc callback, char *string, int min, int max, int inc, int width)
743 {
744     return MakeSpinnerEntry(parent, text, name, below, beside, callback,
745 			string, I_IVAL, (float) min, (float) max, (float) inc, width);
746 }
747 
748 /*********************************/
749 /* make a floating point spinner */
750 /*********************************/
751 
752 Widget
MakeFloatSpinnerEntry(Widget parent,Widget * text,char * name,Widget below,Widget beside,XtCallbackProc callback,char * string,float min,float max,float inc,int width)753 MakeFloatSpinnerEntry(Widget parent, Widget *text, char *name, Widget below, Widget beside, XtCallbackProc callback, char *string, float min, float max, float inc, int width)
754 {
755     return MakeSpinnerEntry(parent, text, name, below, beside, callback,
756 			string, I_FVAL, min, max, inc, width);
757 }
758 
759 /*****************************************************************************************
760    Make a "spinner" entry widget - a widget with an asciiTextWidget and
761    two spinners, an up and down spinner to increase and decrease the value.
762    Call with:	parent	- (Widget)  parent widget
763 		text	- (Widget*) text widget ID is stored here (RETURN)
764 		name    - (char*)   name for text widget
765 		below	- (Widget)  widget below which this one goes
766 		beside	- (Widget)  widget beside which this one goes
767 		callback - (XtCallbackProc) callback for the text widget and
768 						spinners (0 if none)
769 						Callback is passed spin_struct which
770 						has min, max
771 		string	- (char*)   initial string for text widget
772 		type	- (int)     entry type (I_FVAL = float, I_IVAL = int)
773 		min	- (float)   minimum value it is allowed to have
774 		max	- (float)   maximum value it is allowed to have
775 		inc	- (float)   increment/decrement value for each press of arrow
776 		width	- (int)	    width (pixels) of the text widget part
777 
778    Return value is outer box widget which may be used for positioning subsequent widgets.
779 *****************************************************************************************/
780 
781 static Widget
MakeSpinnerEntry(Widget parent,Widget * text,char * name,Widget below,Widget beside,XtCallbackProc callback,char * string,int type,float min,float max,float inc,int width)782 MakeSpinnerEntry(Widget parent, Widget *text, char *name, Widget below, Widget beside, XtCallbackProc callback, char *string, int type, float min, float max, float inc, int width)
783 {
784     Widget	 inform, outform, spinup, spindown, source;
785     spin_struct *spinstruct;
786     Pixel	 bgcolor;
787 
788     if ((spinstruct = (spin_struct *) malloc(sizeof(spin_struct))) == NULL) {
789 	file_msg("Can't allocate space for spinner");
790 	return 0;
791     }
792 
793     FirstArg(XtNfromVert, below);
794     NextArg(XtNfromHoriz, beside);
795     NextArg(XtNdefaultDistance, 0);
796     NextArg(XtNtop, XtChainTop);
797     NextArg(XtNbottom, XtChainTop);
798     NextArg(XtNleft, XtChainLeft);
799     NextArg(XtNright, XtChainLeft);
800     outform = XtCreateManagedWidget("spinner_form", formWidgetClass, parent, Args, ArgCount);
801 
802     /* first the ascii widget to the left of the spinner controls */
803     FirstArg(XtNstring, string);
804     NextArg(XtNwidth, width);
805     NextArg(XtNleftMargin, 4);
806     NextArg(XtNeditType, XawtextEdit);
807     NextArg(XtNinsertPosition, strlen(string));
808     NextArg(XtNhorizDistance, 1);
809     NextArg(XtNvertDistance, 1);
810     NextArg(XtNtop, XtChainTop);
811     NextArg(XtNbottom, XtChainTop);
812     NextArg(XtNleft, XtChainLeft);
813     NextArg(XtNright, XtChainLeft);
814     NextArg(XtNborderWidth, 0);
815     *text = XtCreateManagedWidget(name, asciiTextWidgetClass,
816                                               outform, Args, ArgCount);
817     /* get id of text source widget */
818     FirstArg(XtNtextSource, &source);
819     GetValues(*text);
820 
821     /* install callback to validate data that user types in */
822     if (type == I_IVAL)
823 	XtAddCallback(source, XtNcallback, validate_int, (XtPointer) spinstruct);
824 
825     /* add any callback the user wants */
826     if (callback) {
827 	XtAddCallback(source, XtNcallback, callback, (XtPointer) spinstruct);
828     }
829 
830     XtOverrideTranslations(*text, XtParseTranslationTable(text_translations));
831 
832     /* setup the data structure that gets passed to the callback */
833     spinstruct->widget = *text;
834     spinstruct->min = min;
835     spinstruct->max = max;
836     spinstruct->inc = inc;
837 
838     /* now the spinners */
839 
840     /* get the background color of the text widget and make that the
841        background of the spinners */
842     FirstArg(XtNbackground, &bgcolor);
843     GetValues(*text);
844 
845     /* give the main frame the same background so the little part under
846        the bottom spinner will blend with it */
847     FirstArg(XtNbackground, bgcolor);
848     SetValues(outform);
849 
850     /* make the spinner bitmaps if we haven't already */
851     if (spinup_bm == 0 || spindown_bm == 0) {
852 	spinup_bm = XCreatePixmapFromBitmapData(tool_d, XtWindow(ind_panel),
853 		    (char *) spinup_bits, spinup_width, spinup_height,
854 		    x_color(BLACK), bgcolor, tool_dpth);
855 	spindown_bm = XCreatePixmapFromBitmapData(tool_d, XtWindow(ind_panel),
856 		    (char *) spindown_bits, spindown_width, spindown_height,
857 		    x_color(BLACK), bgcolor, tool_dpth);
858     }
859 
860     /* a form to put them in */
861 
862     FirstArg(XtNfromHoriz, *text);
863     NextArg(XtNinternalWidth, 0);
864     NextArg(XtNinternalHeight, 0);
865     NextArg(XtNhorizDistance, 0);
866     NextArg(XtNvertDistance, 0);
867     NextArg(XtNtop, XtChainTop);
868     NextArg(XtNbottom, XtChainTop);
869     NextArg(XtNleft, XtChainRight);
870     NextArg(XtNright, XtChainRight);
871     NextArg(XtNdefaultDistance, 0);
872     NextArg(XtNborderWidth, 0);
873     NextArg(XtNbackground, bgcolor);
874     inform = XtCreateManagedWidget("spinner_frame", formWidgetClass, outform, Args, ArgCount);
875 
876     /* then the up spinner */
877 
878     FirstArg(XtNbitmap, spinup_bm);
879     NextArg(XtNbackground, bgcolor);
880     NextArg(XtNborderWidth, 0);
881     NextArg(XtNinternalWidth, 0);
882     NextArg(XtNinternalHeight, 0);
883     NextArg(XtNhorizDistance, 0);
884     NextArg(XtNvertDistance, 0);
885     NextArg(XtNtop, XtChainBottom);
886     NextArg(XtNbottom, XtRubber);
887     NextArg(XtNdefaultDistance, 0);
888     spinup = XtCreateManagedWidget("spinup", commandWidgetClass, inform, Args, ArgCount);
889     XtAddCallback(spinup, XtNcallback,
890 		(XtCallbackProc) (type==I_IVAL? inc_int_spinner: inc_flt_spinner),
891 		(XtPointer) spinstruct);
892     /* add event to start timer when user presses spinner */
893     /* if he keeps pressing, it will count automatically */
894     XtAddEventHandler(spinup, ButtonPressMask, False,
895 			  (XtEventHandler) start_spin_timer, (XtPointer) NULL);
896     /* add any user validation callback */
897     if (callback) {
898 	XtAddCallback(spinup, XtNcallback, callback, (XtPointer) spinstruct);
899     }
900 
901     /* finally the down spinner */
902 
903     FirstArg(XtNbitmap, spindown_bm);
904     NextArg(XtNbackground, bgcolor);
905     NextArg(XtNborderWidth, 0);
906     NextArg(XtNinternalWidth, 0);
907     NextArg(XtNinternalHeight, 0);
908     NextArg(XtNfromVert, spinup);
909     NextArg(XtNhorizDistance, 0);
910     NextArg(XtNvertDistance, 0);
911     NextArg(XtNtop, XtRubber);
912     NextArg(XtNbottom, XtChainBottom);
913     NextArg(XtNdefaultDistance, 0);
914     spindown = XtCreateManagedWidget("spindown", commandWidgetClass, inform, Args, ArgCount);
915     XtAddCallback(spindown, XtNcallback,
916 		(XtCallbackProc) (type==I_IVAL? dec_int_spinner: dec_flt_spinner),
917 		(XtPointer) spinstruct);
918     /* add event to start timer when user presses spinner */
919     /* if he keeps pressing, it will count automatically */
920     XtAddEventHandler(spindown, ButtonPressMask, False,
921 			  (XtEventHandler) start_spin_timer, (XtPointer) NULL);
922     /* add any user validation callback */
923     if (callback) {
924 	XtAddCallback(spindown, XtNcallback, callback, (XtPointer) spinstruct);
925     }
926     return outform;
927 }
928 
929 /* validate the integer spinner as user types */
930 
931 void
validate_int(Widget w,XtPointer info,XtPointer dum)932 validate_int(Widget w, XtPointer info, XtPointer dum)
933 {
934     DeclareArgs(4);
935     spin_struct *spins = (spin_struct*) info;
936     char	buf[200];
937     int		val, i, modified = 0;
938     XawTextPosition pos;
939 
940     /* save cursor position */
941     FirstArg(XtNinsertPosition, &pos);
942     GetValues(spins->widget);
943 
944     snprintf(buf, sizeof(buf), "%s", panel_get_value(spins->widget));
945 
946     for (i=0; i<strlen(buf); )
947 	/* delete any non-digits (including leading "-" when min >= 0 */
948 	if ((spins->min >= 0.0 && buf[i] == '-') || ((buf[i] < '0' || buf[i] > '9') && buf[i] != '-') ||
949 			(i != 0 && buf[i] == '-')) {
950 	    memmove(&buf[i], &buf[i+1], strlen(&buf[i]));
951 	    /* adjust cursor for char we just removed */
952 	    pos--;
953 	    modified = 1;
954 	} else {
955 	    i++;
956 	}
957 
958     if (strlen(buf) > 0 && !(strlen(buf)==1 && buf[0] == '-')) {
959 	val = atoi(buf);
960 	/* only check max.  If min is, say 3 and user wants to type 10, the 1 is too small */
961 	if (val > (int) spins->max) {
962 	    val = (int) spins->max;
963 	    sprintf(buf,"%d", val);
964 	    modified = 1;
965         }
966     }
967 
968     if (modified) {
969         panel_set_value(spins->widget, buf);
970 
971 	/* put cursor back */
972 	if (pos < strlen(buf)) {
973 	    FirstArg(XtNinsertPosition, (pos+1));
974 	    SetValues(spins->widget);
975 	}
976     }
977 }
978 
979 /* handle the wheelmouse wheel */
980 
981 void
spinner_up_down(Widget w,XButtonEvent * ev,String * params,Cardinal * num_params)982 spinner_up_down(Widget w, XButtonEvent *ev, String *params, Cardinal *num_params)
983 {
984   w = XtParent(w);
985   if (params[0][0] == '+') w = XtNameToWidget(w, "*spinup");
986   else w = XtNameToWidget(w, "*spindown");
987   if (w == None) {
988     fprintf(stderr, "spinner_up_down() is called for wrong widget\n");
989   } else {
990     ev->window = XtWindow(w);
991     XSendEvent(ev->display, ev->window, TRUE, ButtonPressMask, (XEvent *)ev);
992   }
993 }
994 
995 
996 /* if the file message window is up add it to the grab */
997 /* in this way a user may dismiss the file_msg panel if another panel is up */
998 
file_msg_add_grab(void)999 void file_msg_add_grab(void)
1000 {
1001     if (file_msg_popup)
1002 	XtAddGrab(file_msg_popup, False, False);
1003 }
1004 
1005 /* get the pointer relative to the window it is in */
1006 
1007 void
get_pointer_win_xy(int * xposn,int * yposn)1008 get_pointer_win_xy(int *xposn, int *yposn)
1009 {
1010     Window	   win;
1011     int		   cx, cy;
1012 
1013     get_pointer_root_xy(&cx, &cy);
1014     XTranslateCoordinates(tool_d, XDefaultRootWindow(tool_d), main_canvas,
1015 			  cx, cy, xposn, yposn, &win);
1016 }
1017 
1018 /* get the pointer relative to the root */
1019 
1020 void
get_pointer_root_xy(int * xposn,int * yposn)1021 get_pointer_root_xy(int *xposn, int *yposn)
1022 {
1023     Window	   rw, cw;
1024     int		   cx, cy;
1025     unsigned int   mask;
1026 
1027     XQueryPointer(tool_d, XtWindow(tool),
1028 	&rw, &cw, xposn, yposn, &cx, &cy, &mask);
1029 }
1030 
1031 /* give error message if action_on is true.  This means we are trying
1032    to switch modes in the middle of some drawing/editing operation */
1033 
1034 Boolean
check_action_on(void)1035 check_action_on(void)
1036 {
1037     /* zooming is ok */
1038     if (action_on && cur_mode != F_ZOOM) {
1039 	if (cur_mode == F_TEXT)
1040 	    finish_text_input(0,0,0);/* finish up any text input */
1041 	else {
1042 	    if (cur_mode == F_PLACE_LIB_OBJ)
1043 		cancel_place_lib_obj(0, 0, 0);
1044 	    else {
1045 		put_msg("Finish (or cancel) the current operation before changing modes");
1046 		beep();
1047 		return True;
1048 	    }
1049 	}
1050     }
1051     return False;
1052 }
1053 
1054 /* process any (single) outstanding Xt event to allow things like button pushes */
1055 
process_pending(void)1056 void process_pending(void)
1057 {
1058     while (XtAppPending(tool_app))
1059 	XtAppProcessEvent(tool_app, XtIMAll);
1060     app_flush();
1061 }
1062 
1063 Boolean	user_colors_saved = False;
1064 XColor	saved_user_colors[MAX_USR_COLS];
1065 Boolean	saved_userFree[MAX_USR_COLS];
1066 int	saved_user_num;
1067 
1068 Boolean	nuser_colors_saved = False;
1069 XColor	saved_nuser_colors[MAX_USR_COLS];
1070 Boolean	saved_nuserFree[MAX_USR_COLS];
1071 int	saved_nuser_num;
1072 
1073 /* save user colors into temp vars */
1074 
save_user_colors(void)1075 void save_user_colors(void)
1076 {
1077     int		i;
1078 
1079     if (appres.DEBUG)
1080 	fprintf(stderr,"** Saving user colors. Before: user_colors_saved = %d\n",
1081 		user_colors_saved);
1082 
1083     user_colors_saved = True;
1084 
1085     /* first save the current colors because del_color_cell destroys them */
1086     for (i=0; i<num_usr_cols; i++)
1087 	saved_user_colors[i] = user_colors[i];
1088     /* and save Free entries */
1089     for (i=0; i<num_usr_cols; i++)
1090 	saved_userFree[i] = colorFree[i];
1091     /* now free any previously defined user colors */
1092     for (i=0; i<num_usr_cols; i++) {
1093 	    del_color_cell(i);		/* remove widget and colormap entry */
1094     }
1095     saved_user_num = num_usr_cols;
1096 }
1097 
1098 /* save n_user colors into temp vars */
1099 
save_nuser_colors(void)1100 void save_nuser_colors(void)
1101 {
1102     int		i;
1103 
1104     if (appres.DEBUG)
1105 	fprintf(stderr,"** Saving n_user colors. Before: nuser_colors_saved = %d\n",
1106 		user_colors_saved);
1107 
1108     nuser_colors_saved = True;
1109 
1110     /* first save the current colors because del_color_cell destroys them */
1111     for (i=0; i<n_num_usr_cols; i++)
1112 	saved_nuser_colors[i] = n_user_colors[i];
1113     /* and save Free entries */
1114     for (i=0; i<n_num_usr_cols; i++)
1115 	saved_nuserFree[i] = n_colorFree[i];
1116     saved_nuser_num = n_num_usr_cols;
1117 }
1118 
1119 /* restore user colors from temp vars */
1120 
restore_user_colors(void)1121 void restore_user_colors(void)
1122 {
1123     int		i,num;
1124 
1125     if (!user_colors_saved)
1126 	return;
1127 
1128     if (appres.DEBUG)
1129 	fprintf(stderr,"** Restoring user colors. Before: user_colors_saved = %d\n",
1130 		user_colors_saved);
1131 
1132     user_colors_saved = False;
1133 
1134     /* first free any previously defined user colors */
1135     for (i=0; i<num_usr_cols; i++) {
1136 	    del_color_cell(i);		/* remove widget and colormap entry */
1137     }
1138 
1139     num_usr_cols = saved_user_num;
1140 
1141     /* now restore the orig user colors */
1142     for (i=0; i<num_usr_cols; i++)
1143 	 user_colors[i] = saved_user_colors[i];
1144     /* and Free entries */
1145     for (i=0; i<num_usr_cols; i++)
1146 	colorFree[i] = saved_userFree[i];
1147 
1148     /* now try to allocate those colors */
1149     if (num_usr_cols > 0) {
1150 	num = num_usr_cols;
1151 	num_usr_cols = 0;
1152 	/* fill the colormap and the color memories */
1153 	for (i=0; i<num; i++) {
1154 	    if (colorFree[i]) {
1155 		colorUsed[i] = False;
1156 	    } else {
1157 		/* and add a widget and colormap entry */
1158 		if (add_color_cell(USE_EXISTING_COLOR, i, user_colors[i].red/256,
1159 			user_colors[i].green/256,
1160 			user_colors[i].blue/256) == -1) {
1161 			    file_msg("Can't allocate more than %d user colors, not enough colormap entries",
1162 					num_usr_cols);
1163 			    return;
1164 			}
1165 	        colorUsed[i] = True;
1166 	    }
1167 	}
1168     }
1169 }
1170 
1171 /* Restore user colors from temp vars into n_user_...  */
1172 
restore_nuser_colors(void)1173 void restore_nuser_colors(void)
1174 {
1175     int		i;
1176 
1177     if (!nuser_colors_saved)
1178 	return;
1179 
1180     if (appres.DEBUG)
1181 	fprintf(stderr,"** Restoring user colors into n_...\n");
1182 
1183     nuser_colors_saved = False;
1184 
1185     n_num_usr_cols = saved_nuser_num;
1186 
1187     /* now restore the orig user colors */
1188     for (i=0; i<n_num_usr_cols; i++)
1189 	 n_user_colors[i] = saved_nuser_colors[i];
1190     /* and Free entries */
1191     for (i=0; i<n_num_usr_cols; i++)
1192 	n_colorFree[i] = saved_nuserFree[i];
1193 }
1194 
1195 /* create some global bitmaps like menu arrows, checkmarks, etc. */
1196 
create_bitmaps(void)1197 void create_bitmaps(void)
1198 {
1199 	int	       i;
1200 
1201 	/* make a down-arrow for any pull-down menu buttons */
1202 	menu_arrow = XCreateBitmapFromData(tool_d, tool_w,
1203 			(char *) menu_arrow_bits, menu_arrow_width, menu_arrow_height);
1204 	/* make a right-arrow for any cascade menu entries */
1205 	menu_cascade_arrow = XCreateBitmapFromData(tool_d, tool_w,
1206 			(char *) menu_cascade_arrow_bits,
1207 			menu_cascade_arrow_width, menu_cascade_arrow_height);
1208 	/* make pixmap for red checkmark */
1209 	check_pm = XCreatePixmapFromBitmapData(tool_d, tool_w,
1210 		    (char *) check_bits, check_width, check_height,
1211 		    colors[RED], colors[WHITE], tool_dpth);
1212 	/* and make one same size but all white */
1213 	null_check_pm = XCreatePixmapFromBitmapData(tool_d, tool_w,
1214 		    (char *) check_bits, check_width, check_height,
1215 		    colors[WHITE], colors[WHITE], tool_dpth);
1216 	/* and one for a smaller checkmark */
1217 	sm_check_pm = XCreatePixmapFromBitmapData(tool_d, tool_w,
1218 		    (char *) sm_check_bits, sm_check_width, sm_check_height,
1219 		    colors[RED], colors[WHITE], tool_dpth);
1220 	/* and make one same size but all white */
1221 	sm_null_check_pm = XCreatePixmapFromBitmapData(tool_d, tool_w,
1222 		    (char *) sm_check_bits, sm_check_width, sm_check_height,
1223 		    colors[WHITE], colors[WHITE], tool_dpth);
1224 	/* create two bitmaps to show on/off state */
1225 	balloons_on_bitmap = XCreateBitmapFromData(tool_d, tool_w,
1226 				 (char *) balloons_on_bits,
1227 				 balloons_on_width, balloons_on_height);
1228 	balloons_off_bitmap = XCreateBitmapFromData(tool_d, tool_w,
1229 				 (char *) balloons_off_bits,
1230 				 balloons_off_width, balloons_off_height);
1231 	/* create the 1-plane bitmaps of the arrow images */
1232 	/* these will go in the "left bitmap" part of the menu */
1233 	/* they are used in e_edit.c and w_indpanel.c */
1234 	for (i = -1; i < NUM_ARROW_TYPES-1; i++) {
1235 	    arrow_pixmaps[i+1] = XCreateBitmapFromData(tool_d,canvas_win,
1236 				(i==-1? (char*) no_arrow_bits: (char*) arrowtype_choices[i].icon->bits),
1237 				32, 32);
1238 	}
1239 	diamond_pixmap = XCreateBitmapFromData(tool_d, canvas_win,
1240 			(char*) diamond_bits, diamond_width, diamond_height);
1241 	if (linestyle_pixmaps[0] == 0) {
1242 	    for (i=0; i<NUM_LINESTYLE_TYPES; i++)
1243 		linestyle_pixmaps[i] = XCreateBitmapFromData(tool_d,canvas_win,
1244 			(char*) linestyle_choices[i].icon->bits,
1245 			linestyle_choices[i].icon->width, linestyle_choices[i].icon->height);
1246 	}
1247 
1248 	/* create the bitmaps that look like mouse buttons pressed */
1249 	mouse_l = XCreateBitmapFromData(tool_d, tool_w,
1250 		(char *) mouse_l_bits, mouse_l_width, mouse_l_height);
1251 	mouse_r = XCreateBitmapFromData(tool_d, tool_w,
1252 		(char *) mouse_r_bits, mouse_r_width, mouse_r_height);
1253 }
1254 
1255 /* put a string into an ASCII text widget */
1256 
1257 void
panel_set_value(Widget widg,char * val)1258 panel_set_value(Widget widg, char *val)
1259 {
1260     FirstArg(XtNstring, val);
1261     SetValues(widg);
1262     /* this must be done separately from setting the string */
1263     FirstArg(XtNinsertPosition, strlen(val));
1264     SetValues(widg);
1265 }
1266 
1267 /* get a string from an ASCII text widget */
1268 
1269 char *
panel_get_value(Widget widg)1270 panel_get_value(Widget widg)
1271 {
1272     char	   *val;
1273 
1274     FirstArg(XtNstring, &val);
1275     GetValues(widg);
1276     return val;
1277 }
1278 
1279 /* put an int into an ASCII text widget */
1280 
1281 void
panel_set_int(Widget widg,int intval)1282 panel_set_int(Widget widg, int intval)
1283 {
1284     char	    buf[80];
1285     sprintf(buf, "%d", intval);
1286     panel_set_value(widg, buf);
1287 }
1288 
1289 /* put a float into an ASCII text widget */
1290 
1291 void
panel_set_float(Widget widg,float floatval,char * format)1292 panel_set_float(Widget widg, float floatval, char *format)
1293 {
1294     char	    buf[80];
1295     sprintf(buf, format, floatval);
1296     panel_set_value(widg, buf);
1297 }
1298 
1299 /* create a checkbutton with a labelled area to the right */
1300 
1301 Widget
CreateCheckbutton(char * label,char * widget_name,Widget parent,Widget below,Widget beside,Boolean manage,Boolean large,Boolean * value,XtCallbackProc user_callback,Widget * togwidg)1302 CreateCheckbutton(char *label, char *widget_name, Widget parent, Widget below, Widget beside, Boolean manage, Boolean large, Boolean *value, XtCallbackProc user_callback, Widget *togwidg)
1303 {
1304 	DeclareArgs(20);
1305 	Widget   form, toggle, labelw;
1306 	unsigned int check_ht, udum;
1307 	int	 idum;
1308 	Window	 root;
1309 
1310 	FirstArg(XtNdefaultDistance, 1);
1311 	NextArg(XtNresizable, True);
1312 	if (below != NULL)
1313 	    NextArg(XtNfromVert, below);
1314 	if (beside != NULL)
1315 	    NextArg(XtNfromHoriz, beside);
1316 	NextArg(XtNtop, XtChainTop);
1317 	NextArg(XtNbottom, XtChainTop);
1318 	NextArg(XtNleft, XtChainLeft);
1319 	NextArg(XtNright, XtChainLeft);
1320 	NextArg(XtNborderWidth, 0);
1321 	form = XtCreateWidget(widget_name, formWidgetClass, parent, Args, ArgCount);
1322 
1323 	FirstArg(XtNradioData, 1);
1324 	if (*value) {
1325 	    if (large) {
1326 		NextArg(XtNbitmap, check_pm);
1327 	    } else {
1328 		NextArg(XtNbitmap, sm_check_pm);
1329 	    }
1330 	} else {
1331 	    if (large) {
1332 		NextArg(XtNbitmap, null_check_pm);
1333 	    } else {
1334 		NextArg(XtNbitmap, sm_null_check_pm);
1335 	    }
1336 	}
1337 	NextArg(XtNinternalWidth, 1);
1338 	NextArg(XtNinternalHeight, 1);
1339 	NextArg(XtNleft, XtChainLeft);
1340 	NextArg(XtNright, XtChainLeft);
1341 	NextArg(XtNtop, XtChainTop);
1342 	NextArg(XtNbottom, XtChainTop);
1343 	NextArg(XtNleft, XtChainLeft);
1344 	NextArg(XtNright, XtChainLeft);
1345 	toggle = XtCreateManagedWidget("toggle", toggleWidgetClass,
1346 					form, Args, ArgCount);
1347 	/* user wants widget ID */
1348 	if (togwidg)
1349 	    *togwidg = toggle;
1350 	XtAddCallback(toggle, XtNcallback, (XtCallbackProc) toggle_checkbutton,
1351 				(XtPointer) value);
1352 	if (user_callback)
1353 	    XtAddCallback(toggle, XtNcallback, (XtCallbackProc) user_callback,
1354 			(XtPointer) value);
1355 
1356 	/* get the height of the checkmark pixmap */
1357 	(void) XGetGeometry(tool_d, (large?check_pm:sm_check_pm), &root,
1358 			&idum, &idum, &udum, &check_ht, &udum, &udum);
1359 
1360 	FirstArg(XtNlabel, label);
1361 	NextArg(XtNheight, check_ht+4);	/* make label as tall as the check mark */
1362 	NextArg(XtNjustify, XtJustifyLeft);
1363 	NextArg(XtNborderWidth, 0);
1364 	NextArg(XtNfromHoriz, toggle);
1365 	NextArg(XtNtop, XtChainTop);
1366 	NextArg(XtNbottom, XtChainTop);
1367 	NextArg(XtNleft, XtChainLeft);
1368 	NextArg(XtNright, XtChainLeft);
1369 	/* for small checkmarks, leave less room around label */
1370 	if (!large) {
1371 	    NextArg(XtNinternalWidth, 2);
1372 	    NextArg(XtNinternalHeight, 1);
1373 	}
1374 	labelw = XtCreateManagedWidget("label", labelWidgetClass,
1375 					form, Args, ArgCount);
1376 	if (manage)
1377 	    XtManageChild(form);
1378 	return form;
1379 }
1380 
1381 void /* XtCallbackProc */
toggle_checkbutton(Widget w,XtPointer data,XtPointer garbage)1382 toggle_checkbutton(Widget w, XtPointer data, XtPointer garbage)
1383 {
1384     DeclareArgs(5);
1385     Pixmap	   pm;
1386     Boolean	  *what = (Boolean *) data;
1387     Boolean	   large;
1388 
1389     *what = !*what;
1390     /* first find out what size pixmap we are using */
1391     FirstArg(XtNbitmap, &pm);
1392     GetValues(w);
1393 
1394     large = False;
1395     if (pm == check_pm || pm == null_check_pm)
1396 	large = True;
1397 
1398     if (*what) {
1399 	if (large) {
1400 	    FirstArg(XtNbitmap, check_pm);
1401 	} else {
1402 	    FirstArg(XtNbitmap, sm_check_pm);
1403 	}
1404 	/* make button depressed */
1405 	NextArg(XtNstate, False);
1406     } else {
1407 	if (large) {
1408 	    FirstArg(XtNbitmap, null_check_pm);
1409 	} else {
1410 	    FirstArg(XtNbitmap, sm_null_check_pm);
1411 	}
1412 	/* make button raised */
1413 	NextArg(XtNstate, True);
1414     }
1415     SetValues(w);
1416 
1417     return;
1418 }
1419 
1420 /* assemble main window title bar with xfig title and (base) file name */
1421 
1422 void
update_wm_title(char * name)1423 update_wm_title(char *name)
1424 {
1425     char  wm_title[200];
1426     DeclareArgs(2);
1427 
1428     if (strlen(name)==0) sprintf(wm_title, "%s - No file", tool_name);
1429     else sprintf(wm_title, "Xfig - %s", xf_basename(name));
1430     FirstArg(XtNtitle, wm_title);
1431     SetValues(tool);
1432 }
1433 
1434 void
check_for_resize(Widget tool,XButtonEvent * event,String * params,Cardinal * nparams)1435 check_for_resize(Widget tool, XButtonEvent *event, String *params, Cardinal *nparams)
1436 {
1437     int		    dx, dy;
1438     XConfigureEvent *xc = (XConfigureEvent *) event;
1439 
1440     if (xc->width == TOOL_WD && xc->height == TOOL_HT)
1441 	return;		/* no size change */
1442     dx = xc->width - TOOL_WD;
1443     dy = xc->height - TOOL_HT;
1444     TOOL_WD = xc->width;
1445     TOOL_HT = xc->height;
1446     resize_all(CANVAS_WD + dx, CANVAS_HT + dy);
1447 #ifdef I18N
1448     if (xim_ic != NULL)
1449       xim_set_ic_geometry(xim_ic, CANVAS_WD, CANVAS_HT);
1450 #endif /* I18N */
1451 }
1452 
1453 /* resize whole shebang given new canvas size (width,height) */
1454 
resize_all(int width,int height)1455 void resize_all(int width, int height)
1456 {
1457     Dimension	b, b2, b3, w, h, h2;
1458     int		min_sw_per_row;
1459     DeclareArgs(10);
1460 
1461     setup_sizes(width, height);
1462 
1463     FirstArg(XtNheight, &h);
1464     NextArg(XtNborderWidth, &b);
1465     GetValues(mode_panel);
1466     FirstArg(XtNheight, &h2);
1467     NextArg(XtNborderWidth, &b2);
1468     GetValues(topruler_sw);
1469     FirstArg(XtNborderWidth, &b3);
1470     GetValues(canvas_sw);
1471     h = h+2*b;
1472     h2 = CANVAS_HT+2*b3+h2+2*b2 + 2;
1473     /* if mode panel is taller than the canvas + topruler height, increase
1474        but_per_row and decrease canvas width */
1475     /* but don't change it if user explicitely used -but_per_row */
1476     if (appres.but_per_row == 0) {
1477 	if (h > h2) {
1478 	    min_sw_per_row = (mode_sw_ht + INTERNAL_BW*2) * (NUM_MODE_SW+2) / h2 + 1;
1479 	    if (min_sw_per_row != SW_PER_ROW)
1480 		width -= (min_sw_per_row - SW_PER_ROW)*(mode_sw_wd+2*INTERNAL_BW);
1481 	    SW_PER_ROW = max2(min_sw_per_row, SW_PER_ROW);
1482 	    setup_sizes(width, height);
1483 	    XtUnmanageChild(mode_panel);
1484 	    FirstArg(XtNwidth, MODEPANEL_WD);
1485 	    NextArg(XtNresizable, True);
1486 	    SetValues(mode_panel);
1487 	    /* now resize width of labels "Drawing" and "Editing" */
1488 	    FirstArg(XtNwidth, mode_sw_wd * SW_PER_ROW + INTERNAL_BW * (SW_PER_ROW - 1));
1489 	    SetValues(d_label);
1490 	    SetValues(e_label);
1491 	    FirstArg(XtNwidth, MODEPANEL_WD);
1492 	    NextArg(XtNresizable, False);
1493 	    SetValues(mode_panel);
1494 	    XtManageChild(mode_panel);
1495 	} else {
1496 	    /* check if we can decrease number of buttons per row (user made tool taller) */
1497 	    /* do this by checking the height of the "Drawing" label */
1498 	    FirstArg(XtNheight, &h);
1499 	    GetValues(d_label);
1500 	    min_sw_per_row = (mode_sw_ht + INTERNAL_BW*2) * (NUM_MODE_SW+2) / h2 + 1;
1501 	    if (min_sw_per_row < SW_PER_ROW) {
1502 		width -= (min_sw_per_row - SW_PER_ROW)*(mode_sw_wd+2*INTERNAL_BW);
1503 		SW_PER_ROW = min_sw_per_row;
1504 		setup_sizes(width, height);
1505 		XtUnmanageChild(mode_panel);
1506 		FirstArg(XtNwidth, MODEPANEL_WD);
1507 		NextArg(XtNresizable, True);
1508 		SetValues(mode_panel);
1509 		/* now resize width of labels "Drawing" and "Editing" */
1510 		FirstArg(XtNwidth, mode_sw_wd * SW_PER_ROW + INTERNAL_BW * (SW_PER_ROW - 1));
1511 		SetValues(d_label);
1512 		SetValues(e_label);
1513 		FirstArg(XtNwidth, MODEPANEL_WD);
1514 		NextArg(XtNresizable, False);
1515 		SetValues(mode_panel);
1516 		XtManageChild(mode_panel);
1517 	    }
1518 	} /* if (h > h2) */
1519     } /* appres.but_per_row == 0 */
1520 
1521     XawFormDoLayout(tool_form, False);
1522     ignore_exp_cnt++;		/* canvas is resized twice - redraw only once */
1523 
1524     /* first redo the top panels */
1525     FirstArg(XtNborderWidth, &b);
1526     GetValues(name_panel);
1527     XtResizeWidget(name_panel, NAMEPANEL_WD, CMD_BUT_HT, b);
1528     GetValues(msg_panel);
1529     XtUnmanageChild(mousefun);	/* unmanage the mouse function... */
1530     XtUnmanageChild(layer_form);/* and the layer form */
1531     XtResizeWidget(msg_panel, MSGPANEL_WD, MSGPANEL_HT, b);
1532     XtManageChild(mousefun);	/* so that they shift with msg_panel */
1533 
1534     /* now redo the center area */
1535     XtUnmanageChild(mode_panel);
1536     FirstArg(XtNheight, (MODEPANEL_SPACE + 1) / 2);
1537     SetValues(d_label);
1538     FirstArg(XtNheight, (MODEPANEL_SPACE) / 2);
1539     SetValues(e_label);
1540     XtManageChild(mode_panel);	/* so that it adjusts properly */
1541 
1542     FirstArg(XtNborderWidth, &b);
1543     GetValues(canvas_sw);
1544     XtResizeWidget(canvas_sw, CANVAS_WD, CANVAS_HT, b);
1545     GetValues(topruler_sw);
1546     XtResizeWidget(topruler_sw, TOPRULER_WD, TOPRULER_HT, b);
1547     resize_topruler();
1548     GetValues(sideruler_sw);
1549     XtResizeWidget(sideruler_sw, SIDERULER_WD, SIDERULER_HT, b);
1550     resize_sideruler();
1551     XtUnmanageChild(sideruler_sw);
1552     XtManageChild(sideruler_sw);/* so that it shifts with canvas */
1553     XtUnmanageChild(unitbox_sw);
1554     XtManageChild(unitbox_sw);	/* so that it shifts with canvas */
1555 
1556     /* and the bottom */
1557     XtUnmanageChild(ind_panel);
1558     FirstArg(XtNborderWidth, &b);
1559     NextArg(XtNheight, &h);
1560     GetValues(ind_panel);
1561     w = INDPANEL_WD;
1562     /* account for update control */
1563     if (XtIsManaged(upd_ctrl))
1564 	w -= UPD_CTRL_WD-2*INTERNAL_BW;
1565     XtResizeWidget(ind_panel, w, h, b);
1566     XtManageChild(ind_panel);
1567     XtUnmanageChild(ind_box);
1568     XtManageChild(ind_box);
1569     XtManageChild(layer_form);
1570 
1571     XawFormDoLayout(tool_form, True);
1572 }
1573 
1574 void
check_colors(void)1575 check_colors(void)
1576 {
1577     int		    i;
1578     XColor	    dum,color;
1579 
1580     /* no need to allocate black and white specially */
1581     colors[BLACK] = black_color.pixel;
1582     colors[WHITE] = white_color.pixel;
1583     /* fill the colors array with black (except for white) */
1584     for (i=0; i<NUM_STD_COLS; i++)
1585 	if (i != BLACK && i != WHITE)
1586 		colors[i] = colors[BLACK];
1587 
1588     /* initialize user color cells */
1589     for (i=0; i<MAX_USR_COLS; i++) {
1590 	    colorFree[i] = True;
1591 	    n_colorFree[i] = True;
1592 	    num_usr_cols = 0;
1593     }
1594 
1595     /* if monochrome resource is set, do not even check for colors */
1596     if (!all_colors_available || appres.monochrome) {
1597 	return;
1598     }
1599 
1600     for (i=0; i<NUM_STD_COLS; i++) {
1601 	/* try to allocate another named color */
1602 	/* first try by #xxxxx form if exists, then by name from rgb.txt file */
1603 	if (!xallncol(colorNames[i+1].rgb,&color,&dum)) {
1604 	     /* can't allocate it, switch colormaps try again */
1605 	     if (!switch_colormap() ||
1606 	        (!xallncol(colorNames[i+1].rgb,&color,&dum))) {
1607 		    fprintf(stderr, "Not enough colormap entries available for basic colors\n");
1608 		    fprintf(stderr, "using monochrome mode.\n");
1609 		    all_colors_available = False;
1610 		    return;
1611 	    }
1612 	}
1613 	/* put the colorcell number in the color array */
1614 	colors[i] = color.pixel;
1615     }
1616 
1617     /* get two grays for insensitive spinners */
1618     if (tool_cells == 2 || appres.monochrome) {
1619 	/* use black to gray out an insensitive spinner */
1620 	dark_gray_color = colors[BLACK];
1621 	med_gray_color = colors[BLACK];
1622 	lt_gray_color = colors[BLACK];
1623 	/* color of page border */
1624 	pageborder_color = colors[BLACK];
1625     } else {
1626 	XColor x_color;
1627 	/* get a dark gray for making certain spinners insensitive */
1628 	XParseColor(tool_d, tool_cm, "gray65", &x_color);
1629 	if (XAllocColor(tool_d, tool_cm, &x_color)) {
1630 	    dark_gray_color = x_color.pixel;
1631 	} else {
1632 	    dark_gray_color = colors[WHITE];
1633 	}
1634 	/* get a medium one too */
1635 	XParseColor(tool_d, tool_cm, "gray80", &x_color);
1636 	if (XAllocColor(tool_d, tool_cm, &x_color)) {
1637 	    med_gray_color = x_color.pixel;
1638 	} else {
1639 	    med_gray_color = colors[WHITE];
1640 	}
1641 	/* get a lighter one too */
1642 	XParseColor(tool_d, tool_cm, "gray90", &x_color);
1643 	if (XAllocColor(tool_d, tool_cm, &x_color)) {
1644 	    lt_gray_color = x_color.pixel;
1645 	} else {
1646 	    lt_gray_color = colors[WHITE];
1647 	}
1648 
1649 	/* get page border color */
1650 	XParseColor(tool_d, tool_cm, appres.pageborder, &x_color);
1651 	if (XAllocColor(tool_d, tool_cm, &x_color)) {
1652 	    pageborder_color = x_color.pixel;
1653 	} else {
1654 	    pageborder_color = colors[BLACK];
1655 	}
1656 	/* get axis lines color */
1657 	XParseColor(tool_d, tool_cm, appres.axislines, &x_color);
1658 	if (XAllocColor(tool_d, tool_cm, &x_color)) {
1659 	    axis_lines_color = x_color.pixel;
1660 	} else {
1661 	    axis_lines_color = colors[BLACK];
1662 	}
1663     }
1664 
1665 }
1666 
1667 /* useful when using ups */
XSyncOn(void)1668 void XSyncOn(void)
1669 {
1670 	XSynchronize(tool_d, True);
1671 	XFlush(tool_d);
1672 }
1673 
XSyncOff(void)1674 void XSyncOff(void)
1675 {
1676 	XSynchronize(tool_d, False);
1677 	XFlush(tool_d);
1678 }
1679 
1680 /*
1681  * This will parse the hexadecimal form of the named colors in the standard color
1682  * names.  Some servers can't parse the hex form for XAllocNamedColor()
1683  */
1684 
1685 int
xallncol(char * name,XColor * color,XColor * exact)1686 xallncol(char *name, XColor *color, XColor *exact)
1687 {
1688     unsigned	short r,g,b;
1689     char	nam[30];
1690 
1691     if (*name != '#')
1692 	return XAllocNamedColor(tool_d,tool_cm,name,color,exact);
1693 
1694     /* gcc doesn't allow writing on constant strings without the -fwritable_strings
1695        option, and apparently some versions of sscanf need to write a char back */
1696     strcpy(nam,name);
1697     if (sscanf(nam,"#%2hx%2hx%2hx",&r,&g,&b) != 3 || nam[7] != '\0') {
1698 	fprintf(stderr,
1699 	  "Malformed color specification %s in resources.c must be 6 hex digits",nam);
1700 	exit(1);
1701     }
1702 
1703     color->red   = r<<8;
1704     color->green = g<<8;
1705     color->blue  = b<<8;
1706     color->flags = DoRed|DoGreen|DoBlue;
1707     *exact = *color;
1708     return XAllocColor(tool_d,tool_cm,color);
1709 }
1710 
1711 Widget
make_grid_options(Widget parent,Widget put_below,Widget put_beside,char * minor_grid_value,char * major_grid_value,Widget * grid_minor_menu_button,Widget * grid_major_menu_button,Widget * grid_minor_menu,Widget * grid_major_menu,Widget * print_grid_minor_text,Widget * print_grid_major_text,Widget * grid_unit_label,void (* grid_major_select)(),void (* grid_minor_select)())1712 make_grid_options(Widget parent, Widget put_below, Widget put_beside, char *minor_grid_value, char *major_grid_value,
1713 		Widget *grid_minor_menu_button, Widget *grid_major_menu_button, Widget *grid_minor_menu,
1714 		Widget *grid_major_menu, Widget *print_grid_minor_text, Widget *print_grid_major_text,
1715 		Widget *grid_unit_label, void (*grid_major_select) (/* ??? */), void (*grid_minor_select) (/* ??? */))
1716 {
1717 	Widget	below, beside;
1718 
1719 	if (appres.INCHES) {
1720 	    if (cur_gridunit == FRACT_UNIT) {
1721 		grid_choices = grid_inch_choices;
1722 		n_grid_choices = sizeof(grid_inch_choices) / sizeof(char *);
1723 	    } else {
1724 		grid_choices = grid_tenth_inch_choices;
1725 		n_grid_choices = sizeof(grid_tenth_inch_choices) / sizeof(char *);
1726 	    }
1727 	} else {
1728 	    grid_choices = grid_cm_choices;
1729 	    n_grid_choices = sizeof(grid_cm_choices) / sizeof(char *);
1730 	}
1731 
1732 	FirstArg(XtNlabel, "Minor");
1733 	NextArg(XtNfromVert, put_below);
1734 	NextArg(XtNfromHoriz, put_beside);
1735 	NextArg(XtNhorizDistance, 4);
1736 	NextArg(XtNborderWidth, INTERNAL_BW);
1737 	NextArg(XtNleftBitmap, menu_arrow);	/* use menu arrow for pull-down */
1738 	NextArg(XtNtop, XtChainTop);
1739 	NextArg(XtNbottom, XtChainTop);
1740 	NextArg(XtNleft, XtChainLeft);
1741 	NextArg(XtNright, XtChainLeft);
1742 	beside = *grid_minor_menu_button = XtCreateManagedWidget("minor_grid_menu_button",
1743 						menuButtonWidgetClass,
1744 						parent, Args, ArgCount);
1745 	*grid_minor_menu = make_pulldown_menu(grid_choices, n_grid_choices, -1, NULL,
1746 				*grid_minor_menu_button, grid_minor_select);
1747 
1748 	/* text widget for user to type in minor grid spacing */
1749 	FirstArg(XtNstring, minor_grid_value);
1750 	NextArg(XtNwidth, 50);
1751 	NextArg(XtNleftMargin, 4);
1752 	NextArg(XtNfromVert, put_below);
1753 	NextArg(XtNfromHoriz, beside);
1754 	NextArg(XtNhorizDistance, 1);
1755 	NextArg(XtNeditType, XawtextEdit);
1756 	NextArg(XtNinsertPosition, 0);
1757 	NextArg(XtNborderWidth, INTERNAL_BW);
1758 	NextArg(XtNtop, XtChainTop);
1759 	NextArg(XtNbottom, XtChainTop);
1760 	NextArg(XtNleft, XtChainLeft);
1761 	NextArg(XtNright, XtChainLeft);
1762 	*print_grid_minor_text = XtCreateManagedWidget("minor_grid_text",
1763 						asciiTextWidgetClass,
1764 						parent, Args, ArgCount);
1765         XtOverrideTranslations(*print_grid_minor_text,
1766                            XtParseTranslationTable(text_translations));
1767 
1768 	FirstArg(XtNlabel, "Major");
1769 	NextArg(XtNfromVert, put_below);
1770 	NextArg(XtNfromHoriz, *print_grid_minor_text);
1771 	NextArg(XtNhorizDistance, 8);
1772 	NextArg(XtNborderWidth, INTERNAL_BW);
1773 	NextArg(XtNleftBitmap, menu_arrow);	/* use menu arrow for pull-down */
1774 	NextArg(XtNtop, XtChainTop);
1775 	NextArg(XtNbottom, XtChainTop);
1776 	NextArg(XtNleft, XtChainLeft);
1777 	NextArg(XtNright, XtChainLeft);
1778 	below = *grid_major_menu_button = XtCreateManagedWidget("major_grid_menu_button",
1779 						menuButtonWidgetClass,
1780 						parent, Args, ArgCount);
1781 	*grid_major_menu = make_pulldown_menu(grid_choices, n_grid_choices, -1, NULL,
1782 				*grid_major_menu_button, grid_major_select);
1783 
1784 	/* text widget for user to type in major grid spacing */
1785 
1786 	FirstArg(XtNstring, major_grid_value);
1787 	NextArg(XtNwidth, 50);
1788 	NextArg(XtNleftMargin, 4);
1789 	NextArg(XtNfromVert, put_below);
1790 	NextArg(XtNfromHoriz, below);
1791 	NextArg(XtNhorizDistance, 1);
1792 	NextArg(XtNeditType, XawtextEdit);
1793 	NextArg(XtNinsertPosition, 0);
1794 	NextArg(XtNborderWidth, INTERNAL_BW);
1795 	NextArg(XtNtop, XtChainTop);
1796 	NextArg(XtNbottom, XtChainTop);
1797 	NextArg(XtNleft, XtChainLeft);
1798 	NextArg(XtNright, XtChainLeft);
1799 	*print_grid_major_text = XtCreateManagedWidget("major_grid_text",
1800 						asciiTextWidgetClass,
1801 						parent, Args, ArgCount);
1802         XtOverrideTranslations(*print_grid_major_text,
1803                            XtParseTranslationTable(text_translations));
1804 
1805 	FirstArg(XtNlabel, appres.INCHES? "inches" : "mm");
1806 	NextArg(XtNwidth, 60);
1807 	NextArg(XtNfromVert, put_below);
1808 	NextArg(XtNfromHoriz, *print_grid_major_text);
1809 	NextArg(XtNborderWidth, 0);
1810 	NextArg(XtNtop, XtChainTop);
1811 	NextArg(XtNbottom, XtChainTop);
1812 	NextArg(XtNleft, XtChainLeft);
1813 	NextArg(XtNright, XtChainLeft);
1814 	*grid_unit_label = XtCreateManagedWidget("grid_unit_label", labelWidgetClass,
1815 					    parent, Args, ArgCount);
1816 
1817 	return *grid_minor_menu_button;
1818 }
1819 
1820 /* force the the grid choice menu to be consistent with current IP/metric setting */
1821 /* do this in both the print and export panels */
1822 
1823 void
reset_grid_menus(Boolean inches)1824 reset_grid_menus(Boolean inches)
1825 {
1826 	float	convert;
1827 
1828 	if (inches) {
1829 	    convert = 1.0;
1830 	    /* if was metric and is now inches, convert grid values */
1831 	    if (old_gridunit == MM_UNIT)
1832 		convert = 0.03937;
1833 	    /* if fraction<->decimal find nearest equiv */
1834 	    if (print_panel) {
1835 		/* convert major to metric */
1836 		convert_gridstr(print_grid_major_text, convert);
1837 		/* convert minor to metric */
1838 		convert_gridstr(print_grid_minor_text, convert);
1839 	    }
1840 	    if (export_panel) {
1841 		/* convert major to metric */
1842 		convert_gridstr(export_grid_major_text, convert);
1843 		/* convert minor to metric */
1844 		convert_gridstr(export_grid_minor_text, convert);
1845 	    }
1846 	    if (cur_gridunit == FRACT_UNIT) {
1847 		grid_choices = grid_inch_choices;
1848 		n_grid_choices = num_grid_inch_choices;
1849 	    } else {
1850 		grid_choices = grid_tenth_inch_choices;
1851 		n_grid_choices = num_grid_tenth_inch_choices;
1852 	    }
1853 	    FirstArg(XtNlabel, "inches");
1854 	    if (print_panel)
1855 		SetValues(print_grid_unit_label);
1856 	    if (export_panel)
1857 		SetValues(export_grid_unit_label);
1858 	} else {
1859 	    grid_choices = grid_cm_choices;
1860 	    n_grid_choices = num_grid_cm_choices;
1861 	    FirstArg(XtNlabel, "mm");
1862 	    if (print_panel)
1863 		SetValues(print_grid_unit_label);
1864 	    if (export_panel)
1865 		SetValues(export_grid_unit_label);
1866 	    /* if there are any fractions in the values, change them to 10mm */
1867 	    if (print_panel) {
1868 		/* convert major to metric */
1869 		convert_gridstr(print_grid_major_text, 25.4);
1870 		/* convert minor to metric */
1871 		convert_gridstr(print_grid_minor_text, 25.4);
1872 	    }
1873 	    if (export_panel) {
1874 		/* convert major to metric */
1875 		convert_gridstr(export_grid_major_text, 25.4);
1876 		/* convert minor to metric */
1877 		convert_gridstr(export_grid_minor_text, 25.4);
1878 	    }
1879 	}
1880 	if (old_gridunit != cur_gridunit) {
1881 	    if (print_panel) {
1882 		XtDestroyWidget(print_grid_minor_menu);
1883 		XtDestroyWidget(print_grid_major_menu);
1884 		print_grid_minor_menu = make_pulldown_menu(grid_choices, n_grid_choices, -1, NULL,
1885 					print_grid_minor_menu_button, print_grid_minor_select);
1886 		print_grid_major_menu = make_pulldown_menu(grid_choices, n_grid_choices, -1, NULL,
1887 					print_grid_major_menu_button, print_grid_major_select);
1888 	    }
1889 	    if (export_panel) {
1890 		XtDestroyWidget(export_grid_minor_menu);
1891 		XtDestroyWidget(export_grid_major_menu);
1892 		export_grid_minor_menu = make_pulldown_menu(grid_choices,n_grid_choices,-1, NULL,
1893 					export_grid_minor_menu_button,export_grid_minor_select);
1894 		export_grid_major_menu = make_pulldown_menu(grid_choices,n_grid_choices,-1, NULL,
1895 					export_grid_major_menu_button, export_grid_major_select);
1896 	    }
1897 	}
1898 	old_gridunit = cur_gridunit;
1899 }
1900 
1901 static void
convert_gridstr(Widget widget,float mult)1902 convert_gridstr(Widget widget, float mult)
1903 {
1904 	double	 value, numer, denom, diff;
1905 	char	*sval, fraction[20];
1906 	double	 fracts[] = { 2, 4, 8, 16, 32 };
1907 	double	 tol[]    = { 0.05, 0.1, 0.2, 0.3, 0.6};
1908 #define NUM_FRACTS sizeof(fracts)/sizeof(double)
1909 	int	 i;
1910 
1911 	FirstArg(XtNstring, &sval);
1912 	GetValues(widget);
1913 	/* don't convert anything if "none" */
1914 	if (strcasecmp(sval, "none") == 0)
1915 	    return;
1916 	if (sscanf(sval,"%lf/%lf", &numer, &denom) == 2) {
1917 	    value = numer/denom*mult;
1918 	} else {
1919 	    /* not fraction, just convert to metric */
1920 	    value = numer * mult;
1921 	}
1922 	/* if user wants fractions, give him fractions */
1923 	if (cur_gridunit == FRACT_UNIT) {
1924 	    for (i=0; i<NUM_FRACTS; i++) {
1925 		numer = round(value*fracts[i]);
1926 		diff = fabs(value*fracts[i] - numer);
1927 		if (diff < tol[i] && numer > 0.0)
1928 		    break;
1929 	    }
1930 	    if (i < NUM_FRACTS) {
1931 		sprintf(fraction, "%d/%d", (int) numer, (int) fracts[i]);
1932 		panel_set_value(widget, fraction);
1933 		return;
1934 	    }
1935 	} else if (cur_gridunit == MM_UNIT) {
1936 	    /* for metric, round to nearest integer mm */
1937 	    value = round(value);
1938 	}
1939 	panel_set_float(widget, value, "%.3lf");
1940 }
1941 
1942 /************************/
1943 /*     Splash Screen    */
1944 /************************/
1945 
1946 #define SPLASH_LOGO_XOFFSET	 25	/* offset from the corner of the splash to the logo */
1947 #define SPLASH_LOGO_YOFFSET	 35
1948 #define SPLASH_LOGO_XTEXT	325	/* offset from the corner to version text */
1949 #define SPLASH_LOGO_YTEXT	 30
1950 #define	STEPSIZE		  4	/* stepsize in pixel columns */
1951 
1952 #define	COLSTEPS		   20	/* number of steps in fading splash */
1953 #define DRAW_PAUSE		    1	/* pause between each scan column when drawing the verison number */
1954 #define FADE_PAUSE		50000	/* pause between each shade change in fade */
1955 
1956 /****************************************************************************
1957  * Do a splash screen (actually on the canvas itself)
1958  * We draw the xfig logo followed by "3.X.X", STEPSIZE pixels at a time.
1959  * Then we fade the letters to the background color and remove the icon.
1960  ****************************************************************************/
1961 
splash_screen(void)1962 void splash_screen(void)
1963 {
1964 	GC		splash_gc;
1965 	XColor		col, colbg;
1966 	Boolean		fade;
1967 	int		red_step, green_step, blue_step;
1968 	Pixmap		letters_pm;
1969 	int		splash_x, splash_y;
1970 	int		i, x, y, width;
1971 	unsigned long	plane_mask;
1972 	XGCValues	gcv;
1973 #ifdef USE_SPLASH
1974 	Pixmap		spl_bckgnd, dum;
1975 	Boolean		use_bitmap = False;
1976 #ifdef USE_XPM
1977 	XpmAttributes	spl_bckgnd_attr;
1978 #endif
1979 #endif /* USE_SPLASH */
1980 
1981 	/* center the splash on the canvas */
1982 	splash_x = (CANVAS_WD - spl_bckgnd_ic.width)/2;
1983 	splash_y = (CANVAS_HT - spl_bckgnd_ic.height)/2;
1984 
1985 #ifdef USE_SPLASH
1986 	/* read the background for the splash screen */
1987 #ifdef USE_XPM
1988 	if (all_colors_available) {
1989 	    /* use the color XPM bitmap */
1990 	    spl_bckgnd_attr.valuemask = XpmReturnPixels;
1991 	    spl_bckgnd_attr.colormap = tool_cm;
1992 	    if (XpmCreatePixmapFromData(tool_d, tool_w,
1993 				     spl_bckgnd_xpm, &spl_bckgnd,
1994 				     &dum, &spl_bckgnd_attr) == XpmSuccess)
1995 	        /* use color pixmap */
1996 		use_bitmap = False;
1997 	    else
1998 		/* use mono */
1999 		use_bitmap = True;
2000 	}
2001 #else
2002 	use_bitmap = True;
2003 #endif /* USE_XPM */
2004 
2005 	if (!all_colors_available || use_bitmap) {
2006 	    /* use the xbm bitmap */
2007 	    spl_bckgnd = XCreateBitmapFromData(tool_d, tool_w,
2008 				(char *) spl_bckgnd_ic.bits,
2009 				spl_bckgnd_ic.width, spl_bckgnd_ic.height);
2010 	    /* one-bit deep pixmap */
2011 	    use_bitmap = True;
2012 	}
2013 #endif /* USE_SPLASH */
2014 
2015 	/* if we have a color visual, fade the letters from dark color
2016 	 * up to the background
2017 	 * we'll start the text at sort of a bluish-purple
2018 	 */
2019 	col.flags = DoRed|DoGreen|DoBlue;
2020 	col.red   = 0x66<<8;
2021 	col.green = 0x55<<8;
2022 	col.blue  = 0xaa<<8;
2023 
2024 	/* with a lighter background (must match color in the background pixmap) */
2025 	colbg.flags = DoRed|DoGreen|DoBlue;
2026 	colbg.red   = 0xa7<<8;
2027 	colbg.green = 0xa7<<8;
2028 	colbg.blue  = 0xd6<<8;
2029 
2030 	fade = False;
2031 	if (all_colors_available) {
2032 	    if (tool_vclass == GrayScale || tool_vclass == PseudoColor) {
2033 		/* allocate a color for the background of the text pixmap */
2034 		if (XAllocColorCells(tool_d, tool_cm, 0, &plane_mask, 0,
2035 					&colbg.pixel, 1)==0) {
2036 		    colbg = x_bg_color;
2037 		} else {
2038 		    XStoreColor(tool_d, tool_cm, &colbg);
2039 		}
2040 		/* allocate a colorcell that we can change to fade the text image */
2041 		if (XAllocColorCells(tool_d, tool_cm, 0, &plane_mask, 0,
2042 					&col.pixel, 1)==0) {
2043 		    /* can't get a color, no fading */
2044 		    fade = False;
2045 		} else {
2046 		    XStoreColor(tool_d, tool_cm, &col);
2047 		    fade = True;
2048 		}
2049 	    } else if (tool_vclass == TrueColor) {
2050 		/* for TrueColor, just allocate two colors for the text part */
2051 		XAllocColor(tool_d, tool_cm, &col);
2052 		XAllocColor(tool_d, tool_cm, &colbg);
2053 	    }
2054 	} else {
2055 	    /* monochrome or no colors availble, draw text in fg, bg */
2056 	    col = x_fg_color;
2057 	    colbg = x_bg_color;
2058 	}
2059 	/* make our own gc */
2060 	splash_gc = makegc(PAINT, col.pixel, colbg.pixel);
2061 
2062 	XSetForeground(tool_d, splash_gc, col.pixel);
2063 	/* step size to go from the starting color to the background color */
2064 	red_step   = (x_bg_color.red-col.red)/COLSTEPS;
2065 	green_step = (x_bg_color.green-col.green)/COLSTEPS;
2066 	blue_step  = (x_bg_color.blue-col.blue)/COLSTEPS;
2067 
2068 #ifdef USE_SPLASH
2069 	/* write the background on the canvas */
2070 	if (use_bitmap) {
2071 	    /* this is the monochrome background */
2072 	    XCopyPlane(tool_d, spl_bckgnd, canvas_win, gccache[PAINT],
2073 			0, 0, spl_bckgnd_ic.width, spl_bckgnd_ic.height,
2074 			splash_x, splash_y, 1);
2075 	} else {
2076 	    /* this is the color background */
2077 	    XCopyArea(tool_d, spl_bckgnd, canvas_win, splash_gc,
2078 			0, 0, spl_bckgnd_ic.width, spl_bckgnd_ic.height,
2079 			splash_x, splash_y);
2080 	}
2081 
2082 	app_flush();
2083 #endif
2084 
2085 	/* make the 1-plane bitmap for the version letters "3.X.X" */
2086 	letters_pm = XCreateBitmapFromData(tool_d, tool_w,
2087 				(char *) letters_ic.bits,
2088 				letters_ic.width, letters_ic.height);
2089 
2090 	/* now write the letters STEPSIZE pixel columns at a time */
2091 	x = splash_x + SPLASH_LOGO_XTEXT;
2092 	y = splash_y + SPLASH_LOGO_YTEXT;
2093 	width = STEPSIZE;
2094 
2095 	/* clip to letters to their shape so we don't write on the background */
2096 	gcv.clip_mask = letters_pm;
2097 	gcv.clip_x_origin = x;
2098 	gcv.clip_y_origin = y;
2099 	XChangeGC(tool_d, splash_gc, GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcv);
2100 
2101 	/* write bit-by-bit */
2102 	for (i=0; i<letters_ic.width; i+=width) {
2103 	    if (i+width > letters_ic.width)
2104 		width = letters_ic.width - i;
2105 	    XCopyPlane(tool_d, letters_pm, canvas_win, splash_gc,
2106 		    i, 0, width, letters_ic.height, x+i, y, 1);
2107 	    app_flush();
2108 	    /* even though we're pausing only 1 microsecond (!), it seems to be enough,
2109 	     * probably because of the system call. */
2110 	    usleep(DRAW_PAUSE);
2111 	}
2112 
2113 	/* now ramp it up to the background color to fade it */
2114 	if (fade) {
2115 	    for (i=0; i<COLSTEPS-1; ++i) {
2116 		XStoreColor(tool_d, tool_cm, &col);
2117 		/* change the color in the colormap */
2118 		XSetForeground(tool_d, splash_gc, col.pixel);
2119 		app_flush();
2120 		usleep(FADE_PAUSE);
2121 		col.red   += red_step;
2122 		col.green += green_step;
2123 		col.blue  += blue_step;
2124 	    }
2125 	} else {
2126 	    /* for fading the text in TrueColor, re-write
2127 	     * the pixmap in a changing color */
2128 	    for (i=0; i<COLSTEPS-1; ++i) {
2129 		XAllocColor(tool_d, tool_cm, &col);
2130 		gcv.foreground = col.pixel;
2131 		XChangeGC(tool_d, splash_gc, GCForeground, &gcv);
2132 		XCopyPlane(tool_d, letters_pm, canvas_win, splash_gc,
2133 		    0, 0, letters_ic.width, letters_ic.height, x, y, 1);
2134 		app_flush();
2135 		usleep(FADE_PAUSE);
2136 		col.red   += red_step;
2137 		col.green += green_step;
2138 		col.blue  += blue_step;
2139 	    }
2140 	}
2141 
2142 	/* free up the pixmaps */
2143 	XFreePixmap(tool_d, letters_pm);
2144 #ifdef USE_SPLASH
2145 	XFreePixmap(tool_d, spl_bckgnd);
2146 #endif
2147 
2148 	/* and the GC */
2149 	XFreeGC(tool_d, splash_gc);
2150 
2151 	/* free up the color(s) we allocated */
2152 	XFreeColors(tool_d, tool_cm, &colbg.pixel, 1, 0);
2153 	if (fade)
2154 	    XFreeColors(tool_d, tool_cm, &col.pixel, 1, 0);
2155 
2156 	/* and the colors in the xpm image */
2157 #if defined(USE_XPM) && defined(USE_SPLASH)
2158 	if (!use_bitmap)
2159 	    XFreeColors(tool_d, tool_cm,
2160 		spl_bckgnd_attr.pixels, spl_bckgnd_attr.npixels, 0);
2161 #endif
2162 
2163 	/* finally, set flag saying splash is on the screen so it will be cleared later */
2164 	splash_onscreen = True;
2165 }
2166 
2167 /* clear the splash graphic if it hasn't already been cleared */
2168 /* clear_canvas() zeroes splash_onscreen */
2169 
2170 void
clear_splash(void)2171 clear_splash(void)
2172 {
2173     if (splash_onscreen)
2174 	clear_canvas();
2175 }
2176 
2177 /*
2178  * Install wheel mouse scrolling for the scrollbar of the parent of the passed widget
2179  *
2180  *      viewport
2181  *        / \
2182  *   widget  scrollbar
2183  */
2184 
2185 static char scroll_accel[] =
2186 	"<Btn4Down>:	StartScroll(Backward)\n\
2187 	<Btn5Down>:	StartScroll(Forward)\n\
2188 	<BtnUp>:	NotifyScroll(FullLength) EndScroll()\n";
2189 
2190 static XtAccelerators scroll_acceltable = 0;
2191 
2192 void
InstallScroll(Widget widget)2193 InstallScroll(Widget widget)
2194 {
2195 	_installscroll(XtParent(widget), widget);
2196 }
2197 
2198 /*
2199  * Install wheel mouse scrolling for the scrollbar of the parent of the parent of the passed widget
2200  *
2201  * We look to the parent of the parent for the scrollbar because we have:
2202  *      viewport
2203  *        / \
2204  *     box   scrollbar
2205  *      |
2206  *    widget
2207  */
2208 
2209 void
InstallScrollParent(Widget widget)2210 InstallScrollParent(Widget widget)
2211 {
2212 	_installscroll(XtParent(XtParent(widget)), widget);
2213 }
2214 
2215 static void
_installscroll(Widget parent,Widget widget)2216 _installscroll(Widget parent, Widget widget)
2217 {
2218 	Widget		scroll;
2219 	XtActionList	action_list;
2220 	int		num_actions, i;
2221 	Boolean		found_scroll_action;
2222 
2223 	/* only parse the acceleration table once */
2224 	if (scroll_acceltable == 0)
2225 	    scroll_acceltable = XtParseAcceleratorTable(scroll_accel);
2226 
2227 	/* install the wheel scrolling for the scrollbar of the parent onto the widget */
2228 	scroll = XtNameToWidget(parent, "vertical");
2229 	if (scroll) {
2230 	    /* first see if the scrollbar supports the StartScroll action (when Xaw
2231 	     * is compiled with ARROW_SCROLLBAR, it does not have this action */
2232 	    found_scroll_action = False;
2233 	    XtGetActionList(scrollbarWidgetClass, &action_list,
2234 				(unsigned int *)&num_actions);
2235 	    for (i=0; i<num_actions; i++)
2236 		if (strcasecmp(action_list[i].string, "startscroll") == 0) {
2237 		    found_scroll_action = True;
2238 		    break;
2239 		}
2240 	    if (found_scroll_action) {
2241 		XtOverrideTranslations(scroll, scroll_acceltable);
2242 		FirstArg(XtNaccelerators, scroll_acceltable);
2243 		SetValues(scroll);
2244 		XtInstallAccelerators(widget, scroll);
2245 	    }
2246 	}
2247 }
2248