1 /*
2  * FIG : Facility for Interactive Generation of figures
3  * Copyright (c) 1989-2007 by Brian V. Smith
4  * Parts Copyright (c) 1991 by Paul King
5  *
6  * Any party obtaining a copy of these files is granted, free of charge, a
7  * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
8  * nonexclusive right and license to deal in this software and documentation
9  * files (the "Software"), including without limitation the rights to use,
10  * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
11  * the Software, and to permit persons who receive copies from any such
12  * party to do so, with the only requirement being that the above copyright
13  * and this permission notice remain intact.
14  *
15  */
16 
17 #include "fig.h"
18 #include "figx.h"
19 #include "resources.h"
20 #include "main.h"
21 #include "mode.h"
22 #include "object.h"
23 #include "d_text.h"
24 #include "f_read.h"
25 #include "f_util.h"
26 #include "u_create.h"
27 #include "u_fonts.h"
28 #include "u_pan.h"
29 #include "u_redraw.h"
30 #include "u_search.h"
31 #include "u_undo.h"
32 #include "w_canvas.h"
33 #include "w_cmdpanel.h"
34 #include "w_digitize.h"
35 #include "w_drawprim.h"
36 #include "w_export.h"
37 #include "w_file.h"
38 #include "w_help.h"
39 #include "w_icons.h"
40 #include "w_indpanel.h"
41 #include "w_layers.h"
42 #include "w_msgpanel.h"
43 #include "w_mousefun.h"
44 #include "w_print.h"
45 #include "w_rulers.h"
46 #include "w_srchrepl.h"
47 #include "w_util.h"
48 #include "w_setup.h"
49 #include "w_style.h"
50 #include "w_zoom.h"
51 #include "w_snap.h"
52 #include "e_delete.h"
53 #include "f_load.h"
54 #include "u_bound.h"
55 #include "u_draw.h"
56 #include "u_free.h"
57 #include "u_list.h"
58 #include "u_translate.h"
59 #include "w_cursor.h"
60 #include "w_modepanel.h"
61 
62 #ifndef XAW3D1_5E
63 #include "w_menuentry.h"
64 #endif
65 #ifdef I18N
66 #include "d_text.h"
67 #endif  /* I18N */
68 
69 #include <X11/IntrinsicP.h> /* XtResizeWidget() */
70 
71 /* internal features and definitions */
72 
73 DeclareStaticArgs(12);
74 
75 #define menu_item_bitmap_width 9
76 #define menu_item_bitmap_height 8
77 static unsigned char menu_item_bitmap_bits[] = {
78    0x00, 0x01, 0x80, 0x01, 0xc0, 0x00, 0x60, 0x00,
79    0x31, 0x00, 0x1b, 0x00, 0x0e, 0x00, 0x04, 0x00
80 };
81 
82 static Pixmap menu_item_bitmap = None;
83 
84 /* Widgets holding the ascii values for the string-based settings */
85 
86 Widget	bal_delay;
87 Widget	n_freehand_resolution;
88 Widget	n_recent_files;
89 Widget	max_colors;
90 Widget	image_ed;
91 Widget	spell_chk;
92 Widget	browser;
93 Widget	pdfview;
94 
95 /* global settings */
96 
97 Widget	global_popup = (Widget) 0;
98 Widget	global_panel;
99 
100 /* prototypes */
101 
102 static void	enter_cmd_but(Widget widget, XtPointer closure, XEvent *event, Boolean *continue_to_dispatch);
103 void		delete_all_cmd(Widget w, int closure, int call_data);
104 static void	init_move_paste_object(int x, int y),move_paste_object(int x, int y);
105 static void	place_object(int x, int y, unsigned int shift),cancel_paste(void);
106 static void	paste_draw(int paint_mode);
107 static void	place_object_orig_posn(int x, int y, unsigned int shift);
108 static void	place_menu(Widget w, XEvent *event, String *params, Cardinal *nparams), popup_menu(Widget w, XEvent *event, String *params, Cardinal *nparams);
109 static void	load_recent_file(Widget w, XtPointer client_data, XtPointer call_data);
110 
111 static void	popup_global_panel(Widget w);
112 static void	global_panel_done(Widget w, XButtonEvent *ev);
113 static void	global_panel_cancel(Widget w, XButtonEvent *ev);
114 static void	character_panel_close(void);
115 static void	paste_char(Widget w, XtPointer client_data, XtPointer call_data);
116 
117 Widget		CreateLabelledAscii(Widget *text_widg, char *label, char *widg_name, Widget parent, Widget below, char *str, int width);
118 static Widget	create_main_menu(int menu_num, Widget beside);
119 
120 static int	off_paste_x,off_paste_y;
121 static int	orig_paste_x,orig_paste_y;
122 
123 static Widget	character_map_popup = (Widget) 0;
124 static Widget	character_map_panel, close_but;
125 
126 #ifdef XAW3D1_5E
127 #else
128 /* popup message over command button when mouse enters it */
129 static void     cmd_balloon_trigger(Widget widget, XtPointer closure, XEvent *event, Boolean *continue_to_dispatch);
130 static void     cmd_unballoon(Widget widget, XtPointer closure, XEvent *event, Boolean *continue_to_dispatch);
131 
132 /* popup message over filename window when mouse enters it */
133 static void     filename_balloon_trigger(Widget widget, XtPointer closure, XEvent *event, Boolean *continue_to_dispatch);
134 static void     filename_unballoon(Widget widget, XtPointer closure, XEvent *event, Boolean *continue_to_dispatch);
135 #endif /* XAW3D1_5E */
136 
137 String  global_translations =
138         "<Message>WM_PROTOCOLS: DismissGlobal()\n";
139 
140 String  charmap_translations =
141         "<Message>WM_PROTOCOLS: DismissCharmap()\n";
142 
143 static XtActionsRec     global_actions[] =
144 		    {
145 			{"DismissGlobal", (XtActionProc) global_panel_cancel},
146 		    };
147 static XtActionsRec     charmap_actions[] =
148 		    {
149 			{"DismissCharmap", (XtActionProc) character_panel_close},
150 		    };
151 
152 static XtActionsRec     menu_actions[] =
153 		    {
154 			{"xMenuPopup", (XtActionProc) popup_menu},
155 			{"PlaceMenu", (XtActionProc) place_menu},
156 		    };
157 
158 menu_def file_menu_items[] = {
159 	{"New         (Meta-N)",	0, new, False},
160 	{"Open...     (Meta-O)",	0, popup_open_panel, False},
161 	{"Merge...    (Meta-M)",	0, popup_merge_panel, False},
162 #ifdef DIGITIZE
163 	{"Digitize... (Meta-Z)",	0, popup_digitize_panel, False},
164 #endif /* DIGITIZE */
165 	{"Save        (Meta-S)",	0, do_save, False},
166 	{"Save As...  (Meta-A)",	5, popup_saveas_panel, False},
167 	{"Export...   (Meta-X) (Quick = Shift-Meta-X)",	0, popup_export_panel, False},
168 	{"Print...    (Meta-P) (Quick = Shift-Meta-P)",	0, popup_print_panel, False},
169 	{"Exit        (Meta-Q)",	1, quit, False},
170 	{(char *) -1,			0, NULL, False}, /* makes a line separator followed by */
171 	{NULL, 0, NULL, False},				/* recently loaded files */
172     };
173 
174 menu_def edit_menu_items[] = {
175 	{"Undo               (Meta-U) ", 0, undo, False},
176 	{"Paste Objects      (Meta-T) ", 0, paste, False},
177 	{"Paste Text         (F18/F20)", 6, paste_primary_selection, False},
178 	{"Search/Replace...  (Meta-I) ", -1, popup_search_panel, False},
179 	{"Spell Check...     (Meta-K) ", 0, spell_check, False},
180 	{"Delete All         (Meta-D) ", 0, delete_all_cmd, False},
181 	{"-",				 0, NULL, False},	 /* divider line */
182 	{"Global settings... (Meta-G) ", 0, show_global_settings, False},
183 	{"Set units...       (Shift-U)", 5, popup_unit_panel, False},
184 	{NULL, 0, NULL, False},
185     };
186 
187 #define PAGE_BRD_MSG	"Show page borders   (Meta-B)"
188 #define DPTH_MGR_MSG	"Show depth manager          "
189 #define INFO_BAL_MSG	"Show info balloons  (Meta-Y)"
190 #define LINE_LEN_MSG	"Show line lengths   (Meta-L)"
191 #define VRTX_NUM_MSG	"Show vertex numbers         "
192 #define AUTO_RFS_MSG	"Autorefresh mode            "
193 
194 menu_def view_menu_items[] = {
195 	{"Manage Styles...      (Ctrl-Y)",  7, popup_manage_style_panel, False},
196 	{"Redraw                (Ctrl-L)",  0, redisplay_canvas, False},
197 	{"Portrait/Landscape    (Meta-C)",  3, change_orient, False},
198 	{"Zoom In               (Shift-Z)", 5, inc_zoom_centered, False},
199 	{"Zoom Out              (z)",	    5, dec_zoom_centered, False},
200 	{"Zoom to Fit canvas    (Ctrl-Z)",  8, fit_zoom, False},
201 	{"Unzoom",			    0, unzoom, False},
202 	{"Pan to origin",		    0, pan_origin, False},
203 	{"Character map",		    0, popup_character_map, False},
204 	{"-",				    0, NULL, False},	/* divider line */
205 	/* the following menu labels will be refreshed in refresh_view_menu() */
206 	{PAGE_BRD_MSG,			    10, toggle_show_borders, True},
207 	{DPTH_MGR_MSG,			     5, toggle_show_depths, True},
208 	{INFO_BAL_MSG,			     6, toggle_show_balloons, True},
209 	{LINE_LEN_MSG,			    10, toggle_show_lengths, True},
210 	{VRTX_NUM_MSG,			     5, toggle_show_vertexnums, True},
211 	{AUTO_RFS_MSG,			     0, toggle_refresh_mode, True},
212 	{NULL, 0, NULL, False},
213     };
214 
215 menu_def help_menu_items[] = {
216 	{"Xfig Reference (HTML)...",	0, launch_refman, False},
217 #ifdef FIXED_JAPANESE_PDF
218 	{"Xfig Reference (PDF, English)...",	0, launch_refpdf_en, False},
219 	/* Tom Sato said that the Japanese version of the pdf looked ugly so we'll not distribute it now */
220 	{"Xfig Reference (PDF, Japanese)...",	0, launch_refpdf_jp, False},
221 #else
222 	{"Xfig Reference (PDF)...",	0, launch_refpdf_en, False},
223 #endif /* FIXED_JAPANESE_PDF */
224 	{"Xfig Man Pages (HTML)...",	5, launch_man, False},
225 	{"How-To Guide (PDF)...",	0, launch_howto, False},
226 	{"About Xfig...",		0, launch_about, False},
227 	{NULL, 0, NULL, False},
228     };
229 
230 menu_def snap_menu_items[] = {
231   {"Hold",	0, snap_hold},		/* hol snap mode until released					*/
232   {"Release",	0, snap_release},	/* release hold							*/
233   {"-",		0, NULL},		/* make a dividing line						*/
234                                 /* selections that always work						*/
235   {"Endpoint",	0, snap_endpoint},	/* snap to vertices or other endpoints				*/
236   {"Midpoint",	0, snap_midpoint},	/* snap to segment or other midpoints				*/
237   {"Nearest",	0, snap_nearest},	/* snap to nearest object					*/
238   {"Focus",	0, snap_focus},		/* snap to ellipse focus or circle centerpoint			*/
239   {"-",		0, NULL},		/* make a dividing line						*/
240                                 /* selections that only work as a polyline vertex (other stuff?)	*/
241   {"Normal",	0, snap_normal},	/* snap to point that results in a seg normal to snapped-to obj	*/
242   {"Tangent",	0, snap_tangent},	/* snap to point that results in a seg tangent to obj		*/
243   {"Intersection", 0, snap_intersect},	/* snap to intersection of picked objs				*/
244   {"Diameter",	0, snap_diameter},	/* snap to ellipse or circle opposite diameter			*/
245   {"-",		0, NULL},		/* make a dividing line						*/
246   {NULL, 0, NULL},
247 };
248 
249 /* command panel of menus */
250 
251 main_menu_info main_menus[] = {
252     {"File", "filemenu", "File menu", file_menu_items},
253     {"Edit", "editmenu", "Edit menu", edit_menu_items},
254     {"View", "viewmenu", "View menu", view_menu_items},
255     {"Snap", "snapmenu", "Snap menu", snap_menu_items},
256     {"Help", "helpmenu", "Help menu", help_menu_items},
257 };
258 #define		NUM_CMD_MENUS  (sizeof(main_menus) / sizeof(main_menu_info))
259 
260 /* needed by setup_sizes() */
261 
262 
263 void create_global_panel (Widget w);
264 int locate_menu (String *params, Cardinal *nparams);
265 
266 
267 int
num_main_menus(void)268 num_main_menus(void)
269 {
270     return (NUM_CMD_MENUS);
271 }
272 
273 /* command panel */
274 void
init_main_menus(Widget tool,char * filename)275 init_main_menus(Widget tool, char *filename)
276 {
277     register int    i;
278     Widget	    beside = NULL;
279     DeclareArgs(11);
280 
281     FirstArg(XtNborderWidth, 0);
282     NextArg(XtNcolormap, tool_cm);
283     NextArg(XtNdefaultDistance, 0);
284     NextArg(XtNhorizDistance, 0);
285     NextArg(XtNvertDistance, 0);
286     NextArg(XtNleft, XtChainLeft);
287     NextArg(XtNright, XtChainLeft);
288     NextArg(XtNtop, XtChainTop);
289     NextArg(XtNbottom, XtChainTop);
290     cmd_form = XtCreateWidget("commands", formWidgetClass, tool, Args, ArgCount);
291 
292     for (i = 0; i < NUM_CMD_MENUS; ++i) {
293 	beside = create_main_menu(i, beside);
294     }
295 
296     /* now setup the filename label widget to the right of the command menu buttons */
297 
298     FirstArg(XtNlabel, filename);
299     NextArg(XtNfromHoriz, cmd_form);
300     NextArg(XtNhorizDistance, -INTERNAL_BW);
301     NextArg(XtNfont, bold_font);
302     NextArg(XtNjustify, XtJustifyLeft);
303     NextArg(XtNwidth, NAMEPANEL_WD);
304     NextArg(XtNheight, CMD_BUT_HT+INTERNAL_BW);
305     NextArg(XtNtop, XtChainTop);
306     NextArg(XtNbottom, XtChainTop);
307     NextArg(XtNborderWidth, INTERNAL_BW);
308     name_panel = XtCreateManagedWidget("file_name", labelWidgetClass, tool,
309 				      Args, ArgCount);
310 #ifndef XAW3D1_5E
311     /* popup balloon when mouse passes over filename */
312     XtAddEventHandler(name_panel, EnterWindowMask, False,
313 		      filename_balloon_trigger, (XtPointer) name_panel);
314     XtAddEventHandler(name_panel, LeaveWindowMask, False,
315 		      filename_unballoon, (XtPointer) name_panel);
316 #endif
317     /* add actions to position the menus if the user uses an accelerator */
318     refresh_view_menu();
319 }
320 
321 void
add_cmd_actions(void)322 add_cmd_actions(void)
323 {
324     XtAppAddActions(tool_app, menu_actions, XtNumber(menu_actions));
325 }
326 
327 static Widget
create_main_menu(int menu_num,Widget beside)328 create_main_menu(int menu_num, Widget beside)
329 {
330 	register main_menu_info *menu;
331 
332 	menu = &main_menus[menu_num];
333 	FirstArg(XtNborderWidth, INTERNAL_BW);
334 	NextArg(XtNfont, button_font);
335 	NextArg(XtNwidth, CMD_BUT_WD);
336 	NextArg(XtNheight, CMD_BUT_HT);
337 	NextArg(XtNvertDistance, 0);
338 	NextArg(XtNhorizDistance, -INTERNAL_BW);
339 	NextArg(XtNlabel, menu->label);
340 	NextArg(XtNfromHoriz, beside);
341 	NextArg(XtNmenuName, menu->menu_name);
342 	/* make button to popup each menu */
343 	menu->widget = XtCreateManagedWidget(menu->label, menuButtonWidgetClass,
344 					   cmd_form, Args, ArgCount);
345 	XtAddEventHandler(menu->widget, EnterWindowMask, False,
346 			  enter_cmd_but, (XtPointer) menu);
347 
348 	/* now the menu itself */
349 	menu->menuwidget = create_menu_item(menu);
350 
351 #ifndef XAW3D1_5E
352 	/* popup when mouse passes over button */
353 	XtAddEventHandler(menu->widget, EnterWindowMask, False,
354 			  cmd_balloon_trigger, (XtPointer) menu);
355 	XtAddEventHandler(menu->widget, LeaveWindowMask, False,
356 			  cmd_unballoon, (XtPointer) menu);
357 #endif
358 
359 	return menu->widget;
360 }
361 
362 void
rebuild_file_menu(Widget menu)363 rebuild_file_menu(Widget menu)
364 {
365     static Boolean first = TRUE;
366     Widget entry;
367     int j;
368     char id[10];
369 
370     if (menu == None)
371 	menu = main_menus[0].menuwidget;
372 
373     if (first) {
374 	first = FALSE;
375 	for (j = 0; j < MAX_RECENT_FILES; j++) {
376 	    sprintf(id, "%1d", j + 1);
377 	    FirstArg(XtNvertSpace, 10);
378 #ifndef XAW3D1_5E
379 	    NextArg(XtNunderline, 0); /* underline # digit */
380 	    entry = XtCreateWidget(id, figSmeBSBObjectClass, menu, Args, ArgCount);
381 #else
382 	    entry = XtCreateWidget(id, smeBSBObjectClass, menu, Args, ArgCount);
383 #endif
384 	    XtAddCallback(entry, XtNcallback, load_recent_file, (XtPointer) strdup(id));
385 	    if (j < max_recent_files)
386 		XtManageChild(entry);
387 	}
388     }
389     for (j = 0; j < max_recent_files; j++) {
390 	sprintf(id, "%1d", j + 1);
391 	entry = XtNameToWidget(menu, id);
392 	if (entry != None) {
393 	    if (j < num_recent_files) {
394 		FirstArg(XtNlabel, recent_files[j].name);
395 		NextArg(XtNsensitive, True);
396 		SetValues(entry);
397 	    } else {
398 		FirstArg(XtNlabel, id);
399 		NextArg(XtNsensitive, False);
400 		SetValues(entry);
401 	    }
402 	}
403     }
404 }
405 
406 Widget
create_menu_item(main_menu_info * menup)407 create_menu_item(main_menu_info *menup)
408 {
409 	int	i;
410 	Widget	menu, entry;
411 	DeclareArgs(5);
412 
413 	FirstArg(XtNallowShellResize, True);
414 	menu = XtCreatePopupShell(menup->menu_name, simpleMenuWidgetClass,
415 				menup->widget, Args, ArgCount);
416 	/* make the menu items */
417 	for (i = 0; menup->menu[i].name != NULL; i++) {
418 	    if ((intptr_t) menup->menu[i].name == -1) {
419 		/* put in a separator line */
420 		FirstArg(XtNlineWidth, 2);
421 		(void) XtCreateManagedWidget("line", smeLineObjectClass,
422 					menu, Args, ArgCount);
423 		/* and add recently loaded files to the menu */
424 		rebuild_file_menu(menu);
425 	    } else if (strcmp(menup->menu[i].name, "-") == 0) {
426 		/* put in a separator line */
427 		(void) XtCreateManagedWidget("line", smeLineObjectClass, menu, NULL, 0);
428 	    } else {
429 		/* normal menu entry */
430 		FirstArg(XtNvertSpace, 10);
431 		/* leave space for the checkmark bitmap */
432 		if (menup->menu[i].checkmark) {
433 		    NextArg(XtNleftMargin, 12);
434 		}
435 #ifndef XAW3D1_5E
436 		NextArg(XtNunderline, menup->menu[i].u_line); /* any underline */
437 		entry = XtCreateManagedWidget(menup->menu[i].name, figSmeBSBObjectClass,
438 					menu, Args, ArgCount);
439 #else
440 		entry = XtCreateManagedWidget(menup->menu[i].name, smeBSBObjectClass,
441 					menu, Args, ArgCount);
442 #endif
443 		XtAddCallback(entry, XtNcallback, menup->menu[i].func,
444 					(XtPointer) menup->widget);
445 	    }
446 	}
447 	return menu;
448 }
449 
450 void
setup_main_menus(void)451 setup_main_menus(void)
452 {
453     register int    i;
454     register main_menu_info *menu;
455     DeclareArgs(2);
456 
457     XDefineCursor(tool_d, XtWindow(cmd_form), arrow_cursor);
458 
459     for (i = 0; i < NUM_CMD_MENUS; ++i) {
460 	menu = &main_menus[i];
461 	FirstArg(XtNfont, button_font); /* label font */
462 	if ( menu->menu )
463 	    NextArg(XtNleftBitmap, menu_arrow);     /* use menu arrow for pull-down */
464 	SetValues(menu->widget);
465     }
466 }
467 
468 #ifndef XAW3D1_5E
469 /* come here when the mouse passes over a button in the command panel */
470 
471 static	Widget cmd_balloon_popup = (Widget) 0;
472 static	XtIntervalId balloon_id = (XtIntervalId) 0;
473 static	Widget balloon_w;
474 static	XtPointer clos;
475 
476 static void cmd_balloon(Widget w, XtPointer closure, XtPointer call_data);
477 
478 static void
cmd_balloon_trigger(Widget widget,XtPointer closure,XEvent * event,Boolean * continue_to_dispatch)479 cmd_balloon_trigger(Widget widget, XtPointer closure, XEvent *event, Boolean *continue_to_dispatch)
480 {
481 	if (!appres.showballoons)
482 		return;
483 	balloon_w = widget;
484 	clos = closure;
485 	/* if an old balloon is still up, destroy it */
486 	if ((balloon_id != 0) || (cmd_balloon_popup != (Widget) 0)) {
487 		cmd_unballoon((Widget) 0, (XtPointer) 0, (XEvent*) 0, (Boolean*) 0);
488 	}
489 	balloon_id = XtAppAddTimeOut(tool_app, appres.balloon_delay,
490 			(XtTimerCallbackProc) cmd_balloon, (XtPointer) NULL);
491 }
492 
493 static void
cmd_balloon(Widget w,XtPointer closure,XtPointer call_data)494 cmd_balloon(Widget w, XtPointer closure, XtPointer call_data)
495 {
496 	Position  x, y;
497 	Dimension wid, ht;
498 	main_menu_info *menu= (main_menu_info *) clos;
499 	Widget box, balloons_label;
500 
501 	/* get width and height of this button */
502 	FirstArg(XtNwidth, &wid);
503 	NextArg(XtNheight, &ht);
504 	GetValues(balloon_w);
505 	/* find middle and lower edge */
506 	XtTranslateCoords(balloon_w, wid/2, ht+2, &x, &y);
507 	/* put popup there */
508 	FirstArg(XtNx, x);
509 	NextArg(XtNy, y);
510 	cmd_balloon_popup = XtCreatePopupShell("cmd_balloon_popup",overrideShellWidgetClass,
511 				tool, Args, ArgCount);
512 	FirstArg(XtNborderWidth, 0);
513 	NextArg(XtNhSpace, 0);
514 	NextArg(XtNvSpace, 0);
515 	NextArg(XtNorientation, XtorientVertical);
516 	box = XtCreateManagedWidget("box", boxWidgetClass, cmd_balloon_popup, Args, ArgCount);
517 
518 	/* put left/right mouse button labels as message */
519 	FirstArg(XtNborderWidth, 0);
520 	NextArg(XtNlabel, menu->hint);
521 	balloons_label = XtCreateManagedWidget("label", labelWidgetClass,
522 				    box, Args, ArgCount);
523 
524 	XtPopup(cmd_balloon_popup,XtGrabNone);
525 	XtRemoveTimeOut(balloon_id);
526 	balloon_id = (XtIntervalId) 0;
527 }
528 
529 /* come here when the mouse leaves a button in the command panel */
530 
531 static void
cmd_unballoon(Widget widget,XtPointer closure,XEvent * event,Boolean * continue_to_dispatch)532 cmd_unballoon(Widget widget, XtPointer closure, XEvent *event, Boolean *continue_to_dispatch)
533 {
534     if (balloon_id) {
535 	XtRemoveTimeOut(balloon_id);
536     }
537     balloon_id = (XtIntervalId) 0;
538     if (cmd_balloon_popup != (Widget) 0) {
539 	XtDestroyWidget(cmd_balloon_popup);
540 	cmd_balloon_popup = (Widget) 0;
541     }
542 }
543 #endif /* XAW3D1_5E */
544 
545 static void
enter_cmd_but(Widget widget,XtPointer closure,XEvent * event,Boolean * continue_to_dispatch)546 enter_cmd_but(Widget widget, XtPointer closure, XEvent *event, Boolean *continue_to_dispatch)
547 {
548     main_menu_info *menu = (main_menu_info *) closure;
549     draw_mousefun(menu->hint, "", "");
550 }
551 
552 static char	quit_msg[] = "The current figure is modified.\nDo you want to save it before quitting?";
553 
554 void
quit(Widget w,XtPointer closure,XtPointer call_data)555 quit(Widget w, XtPointer closure, XtPointer call_data)
556 {
557     /* turn off Compose key LED */
558     setCompLED(0);
559 
560     /* don't quit if in the middle of drawing/editing */
561     if (check_action_on())
562 	return;
563 
564     XtSetSensitive(w, False);
565     /* if modified (and non-empty) ask to save first */
566     if (!query_save(quit_msg)) {
567 	XtSetSensitive(w, True);
568 	return;		/* cancel, do not quit */
569     }
570     /* if the user hasn't saved changes to the named styles confirm */
571     if (style_dirty_flag)
572 	if (confirm_close_style() == RESULT_CANCEL) {
573 	    XtSetSensitive(w, True);
574 	    return;	/* cancel, don't quit */
575 	}
576 
577     goodbye(False);	/* finish up and exit */
578 }
579 
goodbye(Boolean abortflag)580 void goodbye(Boolean abortflag)
581 {
582 #ifdef I18N
583 #ifdef I18N_USE_PREEDIT
584   kill_preedit();
585 #endif  /* I18N_USE_PREEDIT */
586 #endif  /* I18N */
587     /* delete the cut buffer only if it is in a temporary directory */
588     if (strncmp(cut_buf_name, TMPDIR, strlen(TMPDIR)) == 0)
589 	unlink(cut_buf_name);
590 
591     /* delete any batch print file */
592     if (batch_exists)
593 	unlink(batch_file);
594 
595     XSync(tool_d, False);	/* https://sourceforge.net/p/mcj/tickets/54 */
596 
597     /* free all the GC's */
598     free_GCs();
599     /* free all the loaded X-Fonts*/
600     free_Fonts();
601 
602     XtDestroyWidget(tool);
603 
604     /* generate a fault to cause core dump */
605     if (abortflag) {
606 	/* go to orig_dir, in case core dumps go to the cwd */
607 	(void) change_directory(orig_dir);
608 	abort();
609     }
610     exit(0);
611 }
612 
613 void
paste(Widget w,XtPointer closure,XtPointer call_data)614 paste(Widget w, XtPointer closure, XtPointer call_data)
615 {
616 	fig_settings    settings;
617 	int		x,y;
618 	struct stat	file_status;
619 
620 	/* turn off Compose key LED */
621 	setCompLED(0);
622 
623 	/* don't paste if in the middle of drawing/editing */
624 	if (check_action_on())
625 		return;
626 
627 	/* turn off anypointposn so cur_pointposn is used for pasting (user may
628 	 * be in a mode that doesn't use the point positioning grid */
629 	anypointposn = 0;
630 
631 	set_cursor(wait_cursor);
632 	turn_off_current();
633 	set_mousefun("place object","place at orig posn","cancel paste",
634 			"place object", "place at orig posn", "cancel paste");
635 	/* set to paste mode */
636 	set_action_on();
637 	cur_mode = F_PASTE;
638 
639 	cur_c = create_compound();
640 	cur_c->parent = NULL;
641 	cur_c->GABPtr = NULL;
642 	cur_c->arcs = NULL;
643 	cur_c->compounds = NULL;
644 	cur_c->ellipses = NULL;
645 	cur_c->lines = NULL;
646 	cur_c->splines = NULL;
647 	cur_c->texts = NULL;
648 	cur_c->comments = NULL;
649 	cur_c->next = NULL;
650 
651 	/* read in the cut buf file */
652 	if (read_figc(cut_buf_name,cur_c,MERGE,DONT_REMAP_IMAGES,0,0,&settings)==0) {
653 		compound_bound(cur_c,
654 			     &cur_c->nwcorner.x,
655 			     &cur_c->nwcorner.y,
656 			     &cur_c->secorner.x,
657 			     &cur_c->secorner.y);
658 
659 		/* save orig coords of object */
660 		orig_paste_x = cur_c->nwcorner.x;
661 		orig_paste_y = cur_c->nwcorner.y;
662 
663 		/* make it relative for mouse positioning */
664 		translate_compound(cur_c, -cur_c->nwcorner.x, -cur_c->nwcorner.y);
665 	} else {
666 		/* an error reading the .xfig file */
667 		if (stat(cut_buf_name, &file_status) == 0) {	/* file exists */
668 		    file_msg("Error reading %s",cut_buf_name);
669 		} else if (errno == ENOENT) {
670 		    file_msg("Cut buffer (%s) is empty",cut_buf_name);
671 		}
672 		reset_action_on();
673 		turn_off_current();
674 		set_cursor(arrow_cursor);
675 		free_compound(&cur_c);
676 		return;
677 	}
678 	/* redraw all of the pictures already on the canvas */
679 	canvas_ref_proc = null_proc;
680 	redraw_images(&objects);
681 
682 	put_msg("Reading objects from \"%s\" ...Done", cut_buf_name);
683 	new_c=copy_compound(cur_c);
684 	/* add it to the depths so it is displayed */
685 	add_compound_depth(new_c);
686 	off_paste_x=new_c->secorner.x;
687 	off_paste_y=new_c->secorner.y;
688 	canvas_locmove_proc = init_move_paste_object;
689 	canvas_ref_proc = move_paste_object;
690 	canvas_leftbut_proc = place_object;
691 	canvas_middlebut_proc = place_object_orig_posn;
692 	canvas_rightbut_proc = cancel_paste;
693 
694 	/* set crosshair cursor */
695 	set_cursor(null_cursor);
696 
697 	/* get the pointer position */
698 	get_pointer_win_xy(&x, &y);
699 	/* if pasting from the command button, reset coords so object is fully on canvas */
700 	if (x<0)
701 		x = 20;
702 	if (y<0)
703 		y = 20;
704 	/* draw the first image */
705 	init_move_paste_object(BACKX(x), BACKY(y));
706 }
707 
708 static void
cancel_paste(void)709 cancel_paste(void)
710 {
711     reset_action_on();
712     canvas_leftbut_proc = null_proc;
713     canvas_middlebut_proc = null_proc;
714     canvas_rightbut_proc = null_proc;
715     canvas_locmove_proc = null_proc;
716     canvas_ref_proc = null_proc;
717     clear_mousefun();
718     set_mousefun("","","", "", "", "");
719     turn_off_current();
720     set_cursor(arrow_cursor);
721     cur_mode = F_NULL;
722     paste_draw(ERASE);
723     /* remove it from the depths */
724     remove_compound_depth(new_c);
725 }
726 
727 static void
paste_draw(int paint_mode)728 paste_draw(int paint_mode)
729 {
730    if (paint_mode==ERASE)
731 	redisplay_compound(new_c);
732    else
733 	redisplay_objects(new_c);
734 }
735 
736 static void
move_paste_object(int x,int y)737 move_paste_object(int x, int y)
738 {
739     int dx,dy;
740     void  (*save_canvas_locmove_proc) ();
741     void  (*save_canvas_ref_proc) ();
742 
743     save_canvas_locmove_proc = canvas_locmove_proc;
744     save_canvas_ref_proc = canvas_ref_proc;
745     /* so we don't recurse infinitely */
746     canvas_locmove_proc = null_proc;
747     canvas_ref_proc = null_proc;
748     paste_draw(ERASE);
749     dx=x-cur_x;
750     dy=y-cur_y;
751     translate_compound(new_c,dx,dy);
752     cur_x=x;
753     cur_y=y;
754     paste_draw(PAINT);
755     canvas_locmove_proc = save_canvas_locmove_proc;
756     canvas_ref_proc = save_canvas_ref_proc;
757 }
758 
759 static void
init_move_paste_object(int x,int y)760 init_move_paste_object(int x, int y)
761 {
762     cur_x=x;
763     cur_y=y;
764     translate_compound(new_c,x,y);
765 
766     paste_draw(PAINT);
767     canvas_locmove_proc = move_paste_object;
768     canvas_ref_proc = move_paste_object;
769 }
770 
771 /* button 1: paste object at current position of mouse */
772 
773 static void
place_object(int x,int y,unsigned int shift)774 place_object(int x, int y, unsigned int shift)
775 {
776     clean_up();
777     add_compound(new_c);
778     set_modifiedflag();
779     redisplay_compound(new_c);
780     cancel_paste();
781 }
782 
783 /* button 2: paste object in original location whence it came */
784 
785 static void
place_object_orig_posn(int x,int y,unsigned int shift)786 place_object_orig_posn(int x, int y, unsigned int shift)
787 {
788     int dx,dy;
789 
790     canvas_ref_proc = null_proc;
791     paste_draw(ERASE);
792     clean_up();
793     /* move back to original position */
794     dx = orig_paste_x-x;
795     dy = orig_paste_y-y;
796     translate_compound(new_c,dx,dy);
797     add_compound(new_c);
798     set_modifiedflag();
799     redisplay_compound(new_c);
800     cancel_paste();
801 }
802 
803 void
new(Widget w,XtPointer closure,XtPointer call_data)804 new(Widget w, XtPointer closure, XtPointer call_data)
805 {
806     /* turn off Compose key LED */
807     setCompLED(0);
808 
809     /* don't allow if in the middle of drawing/editing */
810     if (check_action_on())
811 	return;
812     if (!emptyfigure()) {
813 	/* check if user wants to save figure first */
814 	if (query_save("The current figure is modified, do you want to save it first?")) {
815 	    delete_all();
816 	    strcpy(save_filename,cur_filename);
817 	} else {
818 	    /* cancel new */
819 	    return;
820 	}
821     }
822     set_action(F_LOAD);
823     update_cur_filename("");
824     put_msg("Immediate Undo will restore the figure");
825     redisplay_canvas();
826 }
827 
828 void
delete_all_cmd(Widget w,int closure,int call_data)829 delete_all_cmd(Widget w, int closure, int call_data)
830 {
831     /* turn off Compose key LED */
832     setCompLED(0);
833 
834     /* don't allow if in the middle of drawing/editing */
835     if (check_action_on())
836 	return;
837     if (emptyfigure()) {
838 	put_msg("Figure already empty");
839 	return;
840     }
841     delete_all();
842     put_msg("Immediate Undo will restore the figure");
843     redisplay_canvas();
844 }
845 
846 /* Toggle canvas orientation from Portrait to Landscape or vice versa */
847 
848 void
change_orient()849 change_orient()
850 {
851     Dimension	formw, formh;
852     int		dx, dy;
853 
854     /* turn off Compose key LED */
855     setCompLED(0);
856 
857     /* don't change orientation if in the middle of drawing/editing */
858     if (check_action_on())
859 	return;
860 
861     /* don't resize anything if the user specified xfig's geometry */
862     if (!geomspec) {
863 	/* get the current size of the canvas */
864 	FirstArg(XtNwidth, &formw);
865 	NextArg(XtNheight, &formh);
866 	GetValues(canvas_sw);
867 
868 	if (appres.landscape) {
869 	    /* save current size for switching back */
870 	    CANVAS_WD_LAND = CANVAS_WD;
871 	    CANVAS_HT_LAND = CANVAS_HT;
872 	    dx = CANVAS_WD_PORT - formw;
873 	    dy = CANVAS_HT_PORT - formh;
874 	    TOOL_WD += dx;
875 	    TOOL_HT += dy;
876 	    XtResizeWidget(tool, TOOL_WD, TOOL_HT, 0);
877 	    resize_all((int) (CANVAS_WD_PORT), (int) (CANVAS_HT_PORT));
878 	    appres.landscape = False;
879         } else {
880 	    /* save current size for switching back */
881 	    CANVAS_WD_PORT = CANVAS_WD;
882 	    CANVAS_HT_PORT = CANVAS_HT;
883 	    dx = CANVAS_WD_LAND - formw;
884 	    dy = CANVAS_HT_LAND - formh;
885 	    TOOL_WD += dx;
886 	    TOOL_HT += dy;
887 	    XtResizeWidget(tool, TOOL_WD, TOOL_HT, 0);
888 	    resize_all((int) (CANVAS_WD_LAND), (int) (CANVAS_HT_LAND));
889 	    appres.landscape = True;
890 	}
891     } else {
892 	/* just toggle the flag */
893 	appres.landscape = !appres.landscape;
894     }
895     /* change the printer and export orientation labels */
896     FirstArg(XtNlabel, orient_items[appres.landscape]);
897     if (print_orient_panel)
898 	SetValues(print_orient_panel);
899     if (export_orient_panel)
900 	SetValues(export_orient_panel);
901 
902     /* draw the new orientation of the page border */
903     clear_canvas();
904     redisplay_canvas();
905 
906     /* the figure has been modified */
907     set_modifiedflag();
908 #ifdef I18N
909     if (xim_ic != NULL)
910       xim_set_ic_geometry(xim_ic, CANVAS_WD, CANVAS_HT);
911 #endif /* I18N */
912 }
913 
914 /*
915  * Popup a global settings panel with:
916  *
917  * Widget Type     Description
918  * -----------     -----------------------------------------
919  * checkbutton     mouse tracking (ruler arrows)
920  * checkbutton     show page borders in red
921  * checkbutton     show balloons
922  *   entry           balloon delay
923  * checkbutton     show lengths of lines
924  * checkbutton     show point numbers above polyline points
925  * int entry       max image colors
926  * str entry       image editor
927  * str entry       spelling checker
928  * str entry       html browser
929  * str entry       pdf viewer
930  *
931  */
932 
933 typedef struct _global {
934     Boolean	tracking;		/* show mouse tracking in rulers */
935     Boolean	autorefresh;		/* autorefresh mode */
936     Boolean	show_pageborder;	/* show page borders in red on canvas */
937     Boolean	showdepthmanager;	/* show depth manager panel */
938     Boolean	showballoons;		/* show popup messages */
939     Boolean	showlengths;		/* length/width lines */
940     Boolean	shownums;		/* print point numbers  */
941     Boolean	allownegcoords;		/* allow negative x/y coordinates for panning */
942     Boolean	showaxislines;		/* draw lines through 0,0 */
943 }	globalStruct;
944 
945 globalStruct global;
946 
947 void
show_global_settings(Widget w)948 show_global_settings(Widget w)
949 {
950 	/* turn off Compose key LED */
951 	setCompLED(0);
952 
953 	global.tracking = appres.tracking;
954 	global.autorefresh = appres.autorefresh;
955 	global.show_pageborder = appres.show_pageborder;
956 	global.showdepthmanager = appres.showdepthmanager;
957 	global.showballoons = appres.showballoons;
958 	global.showlengths = appres.showlengths;
959 	global.shownums = appres.shownums;
960 	global.allownegcoords = appres.allownegcoords;
961 	global.showaxislines = appres.showaxislines;
962 
963 	popup_global_panel(w);
964 }
965 
966 static Widget show_bal, delay_label;
967 
968 static void
popup_global_panel(Widget w)969 popup_global_panel(Widget w)
970 {
971 	Dimension	 ht;
972 
973 	if (global_popup == 0) {
974 	    create_global_panel(w);
975 	    XtPopup(global_popup, XtGrabNonexclusive);
976 	    (void) XSetWMProtocols(tool_d, XtWindow(global_popup), &wm_delete_window, 1);
977 	    XtUnmanageChild(delay_label);
978 	    /* make the balloon delay label as tall as the checkbutton */
979 	    FirstArg(XtNheight, &ht);
980 	    GetValues(show_bal);
981 	    FirstArg(XtNheight, ht);
982 	    SetValues(delay_label);
983 	    /* remanage the label */
984 	    XtManageChild(delay_label);
985 	    return;
986 	}
987 	XtPopup(global_popup, XtGrabNonexclusive);
988 }
989 
create_global_panel(Widget w)990 void create_global_panel(Widget w)
991 {
992 	DeclareArgs(10);
993 	Widget		 beside, below, n_freehand, freehand, n_recent, recent;
994 	Widget		 delay_form, delay_spinner;
995 	Position	 xposn, yposn;
996 	char		 buf[80];
997 
998 	XtTranslateCoords(tool, (Position) 0, (Position) 0, &xposn, &yposn);
999 
1000 	FirstArg(XtNtitle, "Xfig: Global Settings");
1001 	NextArg(XtNx, xposn+50);
1002 	NextArg(XtNy, yposn+50);
1003 	NextArg(XtNcolormap, tool_cm);
1004 	global_popup = XtCreatePopupShell("global_settings",
1005 					  transientShellWidgetClass,
1006 					  tool, Args, ArgCount);
1007 	XtOverrideTranslations(global_popup,
1008 			   XtParseTranslationTable(global_translations));
1009 	XtAppAddActions(tool_app, global_actions, XtNumber(global_actions));
1010 
1011 	global_panel = XtCreateManagedWidget("global_panel", formWidgetClass,
1012 					     global_popup, NULL, ZERO);
1013 
1014 	below = CreateCheckbutton("Autorefresh figure      ", "auto_refresh",
1015 			global_panel, NULL, NULL, MANAGE, LARGE_CHK,
1016 			&global.autorefresh, 0,0);
1017 	below = CreateCheckbutton("Track mouse in rulers   ", "track_mouse",
1018 			global_panel, NULL, NULL, MANAGE, LARGE_CHK,
1019 			&global.tracking, 0,0);
1020 	below = CreateCheckbutton("Show page borders       ", "page_borders",
1021 			global_panel, below, NULL, MANAGE, LARGE_CHK,
1022 			&global.show_pageborder, 0,0);
1023 	below = CreateCheckbutton("Show depth manager      ", "depth_manager",
1024 			global_panel, below, NULL, MANAGE, LARGE_CHK,
1025 			&global.showdepthmanager, 0,0);
1026 	show_bal = CreateCheckbutton("Show info balloons      ", "show_balloons",
1027 			global_panel, below, NULL, MANAGE, LARGE_CHK,
1028 			&global.showballoons,0,0);
1029 
1030 	/* put the delay label and spinner in a form to group them */
1031 	FirstArg(XtNdefaultDistance, 1);
1032 	NextArg(XtNfromHoriz, show_bal);
1033 	NextArg(XtNfromVert, below);
1034 	NextArg(XtNborderWidth, 0);
1035 	NextArg(XtNtop, XtChainTop);
1036 	NextArg(XtNbottom, XtChainTop);
1037 	NextArg(XtNleft, XtChainLeft);
1038 	NextArg(XtNright, XtChainLeft);
1039 	delay_form = XtCreateManagedWidget("bal_del_form", formWidgetClass, global_panel,
1040 			Args, ArgCount);
1041 
1042 	FirstArg(XtNlabel,"Delay (ms):");
1043 	NextArg(XtNborderWidth, 0);
1044 	NextArg(XtNtop, XtChainTop);
1045 	NextArg(XtNbottom, XtChainTop);
1046 	NextArg(XtNleft, XtChainLeft);
1047 	NextArg(XtNright, XtChainLeft);
1048 	delay_label = beside = XtCreateManagedWidget("balloon_delay", labelWidgetClass,
1049 				delay_form, Args, ArgCount);
1050 	sprintf(buf, "%d", appres.balloon_delay);
1051 	delay_spinner = MakeIntSpinnerEntry(delay_form, &bal_delay, "balloon_delay",
1052 			NULL, beside, (XtCallbackProc) NULL, buf, 0, 100000, 1, 40);
1053 	FirstArg(XtNtop, XtChainTop);
1054 	NextArg(XtNbottom, XtChainTop);
1055 	NextArg(XtNleft, XtChainLeft);
1056 	NextArg(XtNright, XtChainLeft);
1057 	SetValues(delay_spinner);
1058 
1059 	below = CreateCheckbutton("Show line lengths       ", "show_lengths",
1060 			global_panel, show_bal, NULL, MANAGE, LARGE_CHK,
1061 			&global.showlengths, 0, 0);
1062 	below = CreateCheckbutton("Show vertex numbers     ", "show_vertexnums",
1063 			global_panel, below, NULL, MANAGE, LARGE_CHK,
1064 			&global.shownums, 0, 0);
1065 	below = CreateCheckbutton("Allow negative coords   ", "show_vertexnums",
1066 			global_panel, below, NULL, MANAGE, LARGE_CHK,
1067 			&global.allownegcoords, 0, 0);
1068 	below = CreateCheckbutton("Draw axis lines         ", "showaxislines",
1069 			global_panel, below, NULL, MANAGE, LARGE_CHK,
1070 			&global.showaxislines, 0, 0);
1071 
1072 	FirstArg(XtNlabel, "Freehand drawing resolution");
1073 	NextArg(XtNfromVert, below);
1074 	NextArg(XtNborderWidth, 0);
1075 	NextArg(XtNtop, XtChainTop);
1076 	NextArg(XtNbottom, XtChainTop);
1077 	NextArg(XtNleft, XtChainLeft);
1078 	NextArg(XtNright, XtChainLeft);
1079 	freehand = XtCreateManagedWidget("freehand_resolution", labelWidgetClass,
1080 					global_panel, Args, ArgCount);
1081 	sprintf(buf,"%d",appres.freehand_resolution);
1082 	n_freehand = MakeIntSpinnerEntry(global_panel, &n_freehand_resolution, "freehand_res",
1083 			below, freehand, (XtCallbackProc) NULL, buf, 0, 100000, 10, 26);
1084 	below = freehand;
1085 
1086 	FirstArg(XtNlabel, "Recently used files        ");
1087 	NextArg(XtNfromVert, below);
1088 	NextArg(XtNborderWidth, 0);
1089 	NextArg(XtNtop, XtChainTop);
1090 	NextArg(XtNbottom, XtChainTop);
1091 	NextArg(XtNleft, XtChainLeft);
1092 	NextArg(XtNright, XtChainLeft);
1093 	recent = XtCreateManagedWidget("recent_file_entries", labelWidgetClass,
1094 					global_panel, Args, ArgCount);
1095 	sprintf(buf,"%d",max_recent_files);
1096 	n_recent = MakeIntSpinnerEntry(global_panel, &n_recent_files, "max_recent_files",
1097 			below, recent, (XtCallbackProc) NULL, buf, 0, MAX_RECENT_FILES, 1, 26);
1098 	below = recent;
1099 
1100 	sprintf(buf,"%d",appres.max_image_colors);
1101 	below = CreateLabelledAscii(&max_colors, "Maximum image colors       ", "max_image_colors",
1102 			global_panel, below, buf, 40);
1103 	below = CreateLabelledAscii(&image_ed, "Image editor ", "image_editor",
1104 			global_panel, below, cur_image_editor, 340);
1105 	below = CreateLabelledAscii(&spell_chk, "Spell checker", "spell_check",
1106 			global_panel, below, cur_spellchk, 340);
1107 	below = CreateLabelledAscii(&browser, "HTML Browser ", "html_browser",
1108 			global_panel, below, cur_browser, 340);
1109 	below = CreateLabelledAscii(&pdfview, "PDF Viewer   ", "pdf_viewer",
1110 			global_panel, below, cur_pdfviewer, 340);
1111 
1112 	FirstArg(XtNlabel, "Cancel");
1113 	NextArg(XtNfromVert, below);
1114 	NextArg(XtNvertDistance, 15);
1115 	NextArg(XtNheight, 25);
1116 	NextArg(XtNborderWidth, INTERNAL_BW);
1117 	NextArg(XtNtop, XtChainBottom);
1118 	NextArg(XtNbottom, XtChainBottom);
1119 	NextArg(XtNleft, XtChainLeft);
1120 	NextArg(XtNright, XtChainLeft);
1121 	beside = XtCreateManagedWidget("cancel", commandWidgetClass,
1122 					   global_panel, Args, ArgCount);
1123 	XtAddEventHandler(beside, ButtonReleaseMask, False,
1124 			  (XtEventHandler) global_panel_cancel, (XtPointer) NULL);
1125 
1126 	FirstArg(XtNlabel, "  Ok  ");
1127 	NextArg(XtNfromVert, below);
1128 	NextArg(XtNvertDistance, 15);
1129 	NextArg(XtNfromHoriz, beside);
1130 	NextArg(XtNheight, 25);
1131 	NextArg(XtNborderWidth, INTERNAL_BW);
1132 	NextArg(XtNtop, XtChainBottom);
1133 	NextArg(XtNbottom, XtChainBottom);
1134 	NextArg(XtNleft, XtChainLeft);
1135 	NextArg(XtNright, XtChainLeft);
1136 	below = XtCreateManagedWidget("global_ok", commandWidgetClass,
1137 					   global_panel, Args, ArgCount);
1138 	XtAddEventHandler(below, ButtonReleaseMask, False,
1139 			  (XtEventHandler) global_panel_done, (XtPointer) NULL);
1140 
1141 	/* install accelerators for the following functions */
1142 	XtInstallAccelerators(global_panel, below);
1143 
1144 }
1145 
1146 /* make a label and asciiText widget to its right */
1147 
1148 Widget
CreateLabelledAscii(Widget * text_widg,char * label,char * widg_name,Widget parent,Widget below,char * str,int width)1149 CreateLabelledAscii(Widget *text_widg, char *label, char *widg_name, Widget parent, Widget below, char *str, int width)
1150 {
1151     DeclareArgs(10);
1152     Widget	 lab_widg;
1153 
1154     FirstArg(XtNlabel, label);
1155     NextArg(XtNfromVert, below);
1156     NextArg(XtNjustify, XtJustifyLeft);
1157     NextArg(XtNborderWidth, 0);
1158     NextArg(XtNtop, XtChainTop);
1159     NextArg(XtNbottom, XtChainTop);
1160     NextArg(XtNleft, XtChainLeft);
1161     NextArg(XtNright, XtChainLeft);
1162     lab_widg = XtCreateManagedWidget("label", labelWidgetClass,
1163 					parent, Args, ArgCount);
1164 
1165     FirstArg(XtNstring, str);
1166     NextArg(XtNinsertPosition, strlen(str));
1167     NextArg(XtNeditType, XawtextEdit);
1168     NextArg(XtNfromVert, below);
1169     NextArg(XtNfromHoriz, lab_widg);
1170     NextArg(XtNwidth, width);
1171     NextArg(XtNtop, XtChainTop);
1172     NextArg(XtNbottom, XtChainTop);
1173     NextArg(XtNleft, XtChainLeft);
1174     NextArg(XtNright, XtChainLeft);
1175     *text_widg = XtCreateManagedWidget(widg_name, asciiTextWidgetClass,
1176 					parent, Args, ArgCount);
1177     /* install "standard" translations */
1178     XtOverrideTranslations(*text_widg,
1179 			   XtParseTranslationTable(text_translations));
1180     return lab_widg;
1181 }
1182 
1183 static void
global_panel_done(Widget w,XButtonEvent * ev)1184 global_panel_done(Widget w, XButtonEvent *ev)
1185 {
1186 	Boolean	    asp, gsp, adz, gdz;
1187 	int	    temp;
1188 	char	    buf[80];
1189 
1190 	/* copy all new values back to masters */
1191 	appres.tracking = global.tracking;
1192 	if (appres.autorefresh && !global.autorefresh)
1193 	    cancel_autorefresh();
1194 	else if (!appres.autorefresh && global.autorefresh)
1195 	    set_autorefresh();
1196 	appres.autorefresh = global.autorefresh;
1197 	asp = appres.show_pageborder;
1198 	gsp = global.show_pageborder;
1199 	adz = appres.showaxislines;
1200 	gdz = global.showaxislines;
1201 	/* update settings */
1202 	appres.show_pageborder = gsp;
1203 	appres.showaxislines = gdz;
1204 
1205 	/* if show_pageborder or showaxislines WAS on and is now off, redraw */
1206 	if ((asp && !gsp) || (adz && !gdz)) {
1207 	    /* was on, turn off */
1208 	    clear_canvas();
1209 	    redisplay_canvas();
1210 	} else if ((!asp && gsp) || (!adz && gdz)) {
1211 	    /* if show_pageborder or showaxislines WAS off and is now on, draw them */
1212 	    /* was off, turn on */
1213 	    redisplay_pageborder();
1214 	}
1215 	/* see if user toggled the depth manager setting */
1216 	if (appres.showdepthmanager != global.showdepthmanager)
1217 	    toggle_show_depths();
1218 	appres.showdepthmanager = global.showdepthmanager;
1219 	appres.showballoons = global.showballoons;
1220 	temp = atoi(panel_get_value(bal_delay));
1221 	if (temp < 0) {
1222 	    temp = 0;
1223 	    panel_set_int(bal_delay, temp);
1224 	}
1225 	appres.balloon_delay = temp;
1226 	appres.showlengths = global.showlengths;
1227 	appres.shownums = global.shownums;
1228 	appres.allownegcoords = global.allownegcoords;
1229 	appres.showaxislines = global.showaxislines;
1230 	/* go to 0,0 if user turned off neg coords and we're in the negative */
1231 	if (!appres.allownegcoords)
1232 	    if (zoomxoff < 0 || zoomyoff < 0)
1233 		pan_origin();
1234 
1235 	/* get the freehand resolution spinner value */
1236 	temp = atoi(panel_get_value(n_freehand_resolution));
1237 	if (temp < 0) {
1238 	    temp = 0;
1239 	    panel_set_int(n_freehand_resolution, temp);
1240 	}
1241 	appres.freehand_resolution = temp;
1242 
1243 	/* get the number of recent files spinner value */
1244 	temp = atoi(panel_get_value(n_recent_files));
1245 	if (temp > MAX_RECENT_FILES)
1246 	    temp = MAX_RECENT_FILES;
1247 	else if (temp < 0)
1248 	    temp = 0;
1249 	panel_set_int(n_recent_files, temp);
1250 	/* if number of recent files has changed, update it in the .xfigrc file */
1251 	if (max_recent_files != temp) {
1252 	    Widget menu, entry;
1253 	    int i;
1254 	    char id[10];
1255 
1256 	    menu = main_menus[0].menuwidget;
1257 	    max_recent_files = temp;
1258 	    num_recent_files = min2(num_recent_files, max_recent_files);
1259 	    sprintf(buf,"%d",max_recent_files);
1260 	    update_xfigrc("max_recent_files", buf);
1261 	    for (i=0; i<MAX_RECENT_FILES; i++) {
1262 		sprintf(id, "%1d", i + 1);
1263 		entry = XtNameToWidget(menu, id);
1264 		if (i < max_recent_files) {
1265 		    XtManageChild(entry);
1266 		    /* if new entries created, clear them */
1267 		    if (i >= num_recent_files) {
1268 			FirstArg(XtNlabel, id);
1269 			NextArg(XtNsensitive, False);
1270 			SetValues(entry);
1271 		    }
1272 		} else {
1273 		    XtUnmanageChild(entry);
1274 		}
1275 	    }
1276 	    /* remanage menu so it resizes */
1277 	    XtUnmanageChild(main_menus[0].widget);
1278 	    XtManageChild(main_menus[0].widget);
1279 	}
1280 	temp = atoi(panel_get_value(max_colors));
1281 	if (temp <= 0) {
1282 	    temp = 10;
1283 	    panel_set_int(max_colors, temp);
1284 	}
1285 	appres.max_image_colors = temp;
1286 	strcpy(cur_image_editor, panel_get_value(image_ed));
1287 	strcpy(cur_spellchk, panel_get_value(spell_chk));
1288 	strcpy(cur_browser, panel_get_value(browser));
1289 	strcpy(cur_pdfviewer, panel_get_value(pdfview));
1290 
1291 	XtPopdown(global_popup);
1292 
1293 	refresh_view_menu();
1294 }
1295 
1296 static void
global_panel_cancel(Widget w,XButtonEvent * ev)1297 global_panel_cancel(Widget w, XButtonEvent *ev)
1298 {
1299 	XtDestroyWidget(global_popup);
1300 	global_popup = (Widget) 0;
1301 }
1302 
1303 #ifndef XAW3D1_5E
1304 /* come here when the mouse passes over the filename window */
1305 
1306 static	Widget filename_balloon_popup = (Widget) 0;
1307 
1308 static	XtIntervalId fballoon_id = (XtIntervalId) 0;
1309 static	Widget fballoon_w;
1310 
1311 static void file_balloon(void);
1312 
1313 static void
filename_balloon_trigger(Widget widget,XtPointer closure,XEvent * event,Boolean * continue_to_dispatch)1314 filename_balloon_trigger(Widget widget, XtPointer closure, XEvent *event, Boolean *continue_to_dispatch)
1315 {
1316 	if (!appres.showballoons)
1317 		return;
1318 	fballoon_w = widget;
1319 	fballoon_id = XtAppAddTimeOut(tool_app, appres.balloon_delay,
1320 			(XtTimerCallbackProc) file_balloon, (XtPointer) NULL);
1321 }
1322 
1323 static void
file_balloon(void)1324 file_balloon(void)
1325 {
1326 	Position  x, y;
1327 	Dimension w, h;
1328 	Widget box, balloons_label;
1329 
1330 	/* get width and height of this window */
1331 	FirstArg(XtNwidth, &w);
1332 	NextArg(XtNheight, &h);
1333 	GetValues(fballoon_w);
1334 	/* find center and lower edge */
1335 	XtTranslateCoords(fballoon_w, w/2, h+2, &x, &y);
1336 
1337 	/* put popup there */
1338 	FirstArg(XtNx, x);
1339 	NextArg(XtNy, y);
1340 	filename_balloon_popup = XtCreatePopupShell("filename_balloon_popup",
1341 				overrideShellWidgetClass, tool, Args, ArgCount);
1342 	FirstArg(XtNborderWidth, 0);
1343 	NextArg(XtNhSpace, 0);
1344 	NextArg(XtNvSpace, 0);
1345 	box = XtCreateManagedWidget("box", boxWidgetClass,
1346 				filename_balloon_popup, Args, ArgCount);
1347 	FirstArg(XtNborderWidth, 0);
1348 	NextArg(XtNlabel, "Current filename");
1349 	balloons_label = XtCreateManagedWidget("label", labelWidgetClass,
1350 				box, Args, ArgCount);
1351 	XtPopup(filename_balloon_popup,XtGrabNone);
1352 }
1353 
1354 /* come here when the mouse leaves the filename window */
1355 
1356 static void
filename_unballoon(Widget widget,XtPointer closure,XEvent * event,Boolean * continue_to_dispatch)1357 filename_unballoon(Widget widget, XtPointer closure, XEvent *event, Boolean *continue_to_dispatch)
1358 {
1359     if (fballoon_id)
1360 	XtRemoveTimeOut(fballoon_id);
1361     fballoon_id = (XtIntervalId) 0;
1362     if (filename_balloon_popup != (Widget) 0) {
1363 	XtDestroyWidget(filename_balloon_popup);
1364 	filename_balloon_popup = (Widget) 0;
1365     }
1366 }
1367 #endif /* XAW3D1_5E */
1368 
1369 /*
1370  * Update the current filename in the name_panel widget, and the xfig icon.
1371  * Also update the current filename in the File popup (if it has been created).
1372  */
1373 
update_cur_filename(char * newname)1374 void update_cur_filename(char *newname)
1375 {
1376         if (cur_filename!=newname) strcpy(cur_filename,newname);
1377 	/* store the new filename in the name_panel widget */
1378 	FirstArg(XtNlabel, newname);
1379 	SetValues(name_panel);
1380 	if (cfile_text)			/* if the name widget in the file popup exists... */
1381 	    SetValues(cfile_text);
1382 
1383 	/* put the filename being edited in the icon */
1384 	XSetIconName(tool_d, tool_w, xf_basename(cur_filename));
1385 
1386 	update_def_filename();		/* update default filename in export panel */
1387 	update_wm_title(cur_filename);	/* and window title bar */
1388 }
1389 
1390 static void
popup_menu(Widget w,XEvent * event,String * params,Cardinal * nparams)1391 popup_menu(Widget w, XEvent *event, String *params, Cardinal *nparams)
1392 {
1393     int		which;
1394 
1395     which = locate_menu(params, nparams);
1396     if (which < 0)
1397 	return;
1398     XtPopupSpringLoaded(main_menus[which].menuwidget);
1399 }
1400 
1401 static void
place_menu(Widget w,XEvent * event,String * params,Cardinal * nparams)1402 place_menu(Widget w, XEvent *event, String *params, Cardinal *nparams)
1403 {
1404     Position	x, y;
1405     Dimension	height;
1406     int		which;
1407 
1408     which = locate_menu(params, nparams);
1409     if (which < 0)
1410 	return;
1411     /* get the height of the menu button on the command panel */
1412     FirstArg(XtNheight, &height);
1413     GetValues(main_menus[which].widget);
1414     XtTranslateCoords(main_menus[which].widget, (Position) 0, height+4, &x, &y);
1415     /* position the popup menu just under the button */
1416     FirstArg(XtNx, x);
1417     NextArg(XtNy, y);
1418     SetValues(main_menus[which].menuwidget);
1419 }
1420 
1421 int
locate_menu(String * params,Cardinal * nparams)1422 locate_menu(String *params, Cardinal *nparams)
1423 {
1424     int		which;
1425 
1426     if (*nparams < 1)
1427 	return -1;
1428 
1429     /* find which menu the user wants */
1430     for (which=0; which<NUM_CMD_MENUS; which++)
1431 	if (strcmp(params[0],main_menus[which].menu_name)==0)
1432 	    break;
1433     if (which >= NUM_CMD_MENUS)
1434 	return -1;
1435     return which;
1436 }
1437 
1438 /* callback to load a recently used file (from the File menu) */
1439 
1440 static void
load_recent_file(Widget w,XtPointer client_data,XtPointer call_data)1441 load_recent_file(Widget w, XtPointer client_data, XtPointer call_data)
1442 {
1443     int		 which = atoi((char *) client_data);
1444     char	*filename;
1445     char	 dir[PATH_MAX], *c;
1446 
1447     filename = recent_files[which-1].name+2;	/* point past number, space */
1448     /* if file panel is open, popdown it */
1449     if (file_up)
1450 	file_panel_dismiss();
1451 
1452     /* check if modified, unsaved figure on canvas */
1453     if (!query_save(quit_msg))
1454 	return;
1455     /* go to the directory where the figure is in case it has imported pictures */
1456     strcpy(dir,filename);
1457     if (c=strrchr(dir,'/')) {
1458 	*c = '\0';			/* terminate dir at last '/' */
1459 	change_directory(dir);
1460 	strcpy(cur_file_dir, dir);	/* update current directory */
1461 	strcpy(cur_export_dir, dir);	/* and export current directory */
1462     }
1463     /* load the file */
1464     (void) load_file(filename, 0, 0);
1465 }
1466 
1467 /* this one is called by the accelerator (File) 1/2/3... */
1468 
1469 void
acc_load_recent_file(Widget w,XEvent * event,String * params,Cardinal * nparams)1470 acc_load_recent_file(Widget w, XEvent *event, String *params, Cardinal *nparams)
1471 {
1472     /* get file number from passed arg */
1473     int		which = atoi(*params);
1474 
1475     /* turn off Compose key LED */
1476     setCompLED(0);
1477 
1478     /* see if that file exists in the list */
1479     if (which > num_recent_files)
1480 	return;
1481 
1482     /* now load by calling the callback */
1483     load_recent_file(w, (XtPointer) *params, (XtPointer) NULL);
1484 }
1485 
1486 static Widget charmap_font_label;
1487 
1488 /* refresh character map (e.g. when user changes font) by changing the font label and font in the buttons */
1489 
1490 void
refresh_character_panel(void)1491 refresh_character_panel(void)
1492 {
1493 	int	     nchildren, i;
1494 	char	     fname[80];
1495 	XFontStruct *font;
1496 	WidgetList   children;
1497 
1498 	if (!character_map_popup)
1499 	    return;
1500 	sprintf(fname, "%s font characters:",
1501 			using_ps? ps_fontinfo[work_font+1].name: latex_fontinfo[work_font+1].name);
1502 	/* change font name label */
1503 	FirstArg(XtNlabel, fname);
1504 	SetValues(charmap_font_label);
1505 
1506 	/* get the buttons (children) of the form */
1507 	FirstArg(XtNnumChildren, &nchildren);
1508 	NextArg(XtNchildren, &children);
1509 	GetValues(character_map_panel);
1510 	/* get the current font */
1511 	font = lookfont(work_font, 12);
1512 	FirstArg(XtNfont, font);
1513 	/* loop over all but the last button, which is the close button */
1514 	for (i=0; i<nchildren-1; i++) {
1515 	   if (XtClass(*children) == commandWidgetClass)
1516 		SetValues(*children);
1517 	   children++;
1518 	}
1519 }
1520 
1521 static void
character_panel_close(void)1522 character_panel_close(void)
1523 {
1524 	XtDestroyWidget(character_map_popup);
1525 	character_map_popup = (Widget) 0;
1526 }
1527 
1528 /*
1529  * popup a window showing the symbol character map, each char in a
1530  * different button widget so the user paste directly into text
1531  * Activated from the View/Character Map menu
1532  */
1533 
1534 #define LASTCHAR 255
1535 
1536 void
popup_character_map(void)1537 popup_character_map(void)
1538 {
1539 	Widget		 beside, below;
1540 	XFontStruct	*font;
1541 	intptr_t	 i;
1542 	int		 vertDist;
1543 	char		 fname[80], chr[2];
1544 	static Boolean	 actions_added=False;
1545 
1546 	/* only allow one copy */
1547 	if (character_map_popup)
1548 	    return;
1549 
1550 	FirstArg(XtNtitle, "Xfig: Character Map");
1551 	NextArg(XtNcolormap, tool_cm);
1552 	character_map_popup = XtCreatePopupShell("character_map_popup",
1553 					  transientShellWidgetClass,
1554 					  tool, Args, ArgCount);
1555 	XtOverrideTranslations(character_map_popup,
1556 			   XtParseTranslationTable(charmap_translations));
1557 	if (!actions_added) {
1558 	    XtAppAddActions(tool_app, charmap_actions, XtNumber(charmap_actions));
1559 	    actions_added = True;
1560 	}
1561 
1562 	character_map_panel = XtCreateManagedWidget("character_map_panel", formWidgetClass,
1563 					     character_map_popup, NULL, ZERO);
1564 
1565 	sprintf(fname, "%s font characters:",
1566 			using_ps? ps_fontinfo[work_font+1].name: latex_fontinfo[work_font+1].name);
1567 	FirstArg(XtNlabel, fname);
1568 	NextArg(XtNborderWidth, 0);
1569 	charmap_font_label = below = XtCreateManagedWidget("charmap_font_label", labelWidgetClass,
1570 					 character_map_panel, Args, ArgCount);
1571 	/* get the font */
1572 	font = lookfont(work_font, 12);
1573 	beside = (Widget) 0;
1574 	vertDist = 3;
1575 	chr[1] = '\0';
1576 	for (i=32; i<=LASTCHAR; i++) {
1577 	    chr[0] = (char) i;
1578 	    FirstArg(XtNlabel, chr);
1579 	    NextArg(XtNfont, font);
1580 	    NextArg(XtNwidth, 20);
1581 	    NextArg(XtNheight, 20);
1582 	    NextArg(XtNfromVert, below);
1583 	    NextArg(XtNvertDistance, vertDist);
1584 	    NextArg(XtNfromHoriz, beside);
1585 	    beside = XtCreateManagedWidget("char_button", commandWidgetClass,
1586 					     character_map_panel, Args, ArgCount);
1587 	    /* add callback to paste character into current text */
1588 	    XtAddCallback(beside, XtNcallback, paste_char, (XtPointer) i);
1589 	    /* skip empty entries and 127 (delete) */
1590 	    if (i==126) {
1591 		below = beside;
1592 		beside = (Widget) 0;
1593 		i += 33;
1594 		/* and make a gap */
1595 		vertDist = 10;
1596 	    } else if ((i+1)%16 == 0 && i != LASTCHAR) {
1597 		below = beside;
1598 		beside = (Widget) 0;
1599 		vertDist = 3;
1600 	    }
1601 	}
1602 	/* close button */
1603 	FirstArg(XtNlabel, "Close");
1604 	NextArg(XtNfromVert, beside);
1605 	NextArg(XtNvertDistance, 15);
1606 	NextArg(XtNheight, 25);
1607 	NextArg(XtNborderWidth, INTERNAL_BW);
1608 	close_but = XtCreateManagedWidget("close", commandWidgetClass,
1609 					   character_map_panel, Args, ArgCount);
1610 	XtAddEventHandler(close_but, ButtonReleaseMask, False,
1611 			  (XtEventHandler) character_panel_close, (XtPointer) NULL);
1612 	XtPopup(character_map_popup, XtGrabNone);
1613 	(void) XSetWMProtocols(tool_d, XtWindow(character_map_popup), &wm_delete_window, 1);
1614 }
1615 
1616 static void
paste_char(Widget w,XtPointer client_data,XtPointer call_data)1617 paste_char(Widget w, XtPointer client_data, XtPointer call_data)
1618 {
1619 	union	{
1620 		XtPointer	ptr;
1621 		unsigned char	val;
1622 	}	ptr_val = {client_data};
1623 
1624     unsigned char chr = ptr_val.val;
1625 
1626     /* only allow during text input */
1627     if (canvas_kbd_proc != (void (*)())char_handler)
1628 	return;
1629     char_handler((XKeyEvent *) 0, chr, (KeySym) 0);
1630 }
1631 
1632 /* add or remove a checkmark to a menu entry to show that it
1633    is selected or unselected respectively */
1634 
1635 static void
refresh_view_menu_item(char * name,Boolean state)1636 refresh_view_menu_item(char *name, Boolean state)
1637 {
1638     Widget	 menu, w;
1639     Pixmap	 bitmap;
1640     DeclareStaticArgs(10);
1641 
1642     menu = XtNameToWidget(tool, "*viewmenu");
1643     if (menu == NULL) {
1644 	fprintf(stderr, "xfig: refresh_view_menu: can't find *viewmenu\n");
1645     } else {
1646 	w = XtNameToWidget(menu, name);
1647 	if (w == NULL) {
1648 	    fprintf(stderr, "xfig: can't find \"viewmenu%s\"\n", name);
1649 	} else {
1650 	    if (state) {
1651 		if (menu_item_bitmap == None)
1652 		    menu_item_bitmap = XCreateBitmapFromData(XtDisplay(menu),
1653 				RootWindowOfScreen(XtScreen(menu)),
1654 				(char *)menu_item_bitmap_bits,
1655 				menu_item_bitmap_width,
1656 				menu_item_bitmap_height);
1657 		bitmap = menu_item_bitmap;
1658 	    } else {
1659 		bitmap = None;
1660 	    }
1661 	    FirstArg(XtNleftBitmap, bitmap);
1662 	    SetValues(w);
1663 	}
1664     }
1665 }
1666 
1667 /* update the menu entries with or without an asterisk */
1668 
1669 void
refresh_view_menu(void)1670 refresh_view_menu(void)
1671 {
1672 #ifdef XAW3D1_5E
1673     int i;
1674     register main_menu_info *menu;
1675 #endif /* XAW3D1_5E */
1676     /* turn off Compose key LED */
1677     setCompLED(0);
1678 
1679     refresh_view_menu_item(PAGE_BRD_MSG, appres.show_pageborder);
1680     refresh_view_menu_item(DPTH_MGR_MSG, appres.showdepthmanager);
1681     refresh_view_menu_item(INFO_BAL_MSG, appres.showballoons);
1682     refresh_view_menu_item(LINE_LEN_MSG, appres.showlengths);
1683     refresh_view_menu_item(VRTX_NUM_MSG, appres.shownums);
1684     refresh_view_menu_item(AUTO_RFS_MSG, appres.autorefresh);
1685 
1686 #ifdef XAW3D1_5E
1687     if (appres.showballoons) {
1688 	XawTipEnable(name_panel, "Current filename");
1689 	for (i = 0; i < NUM_CMD_MENUS; ++i) {
1690 	    menu = &main_menus[i];
1691 	    XawTipEnable(menu->widget, menu->hint);
1692 	}
1693     } else {
1694 	XawTipDisable(name_panel);
1695 	for (i = 0; i < NUM_CMD_MENUS; ++i) {
1696 	    menu = &main_menus[i];
1697 	    XawTipDisable(menu->widget);
1698 	}
1699     }
1700     update_indpanel(cur_indmask);
1701     update_modepanel();
1702     update_layerpanel();
1703     update_mousepanel();
1704     update_rulerpanel();
1705 #endif /* XAW3D1_5E */
1706 }
1707