1 /*
2  * Copyright (c) 2004 Stefan Ulrich
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
18  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  */
23 
24 /*
25  * `Topics' window framework used by help and preferences windows.
26  * Contains a list of topics on the left-hand side, and corresponding
27  * contents on the right-hand side.
28  */
29 
30 #include "xdvi-config.h"
31 #include "xdvi.h"
32 
33 #if MOTIF
34 # include <Xm/Xm.h>
35 # include <Xm/Form.h>
36 # include <Xm/DialogS.h>
37 # include <Xm/PushB.h>
38 # include <Xm/Frame.h>
39 # include <Xm/PanedW.h>
40 # include <Xm/LabelG.h>
41 # include <Xm/Label.h>
42 # include <Xm/Protocols.h>
43 # include <Xm/List.h>
44 # include <Xm/AtomMgr.h>
45 #else /* MOTIF */
46 # include <X11/Xaw/Paned.h>
47 # include <X11/Xaw/Box.h>
48 # include <X11/Xaw/Form.h>
49 # include <X11/Xaw/Cardinals.h>
50 # include <X11/Xaw/Command.h>
51 # include <X11/Xaw/List.h>
52 #endif /* MOTIF */
53 
54 #include "version.h"
55 #include "topic-window.h"
56 #include "string-utils.h"
57 #include "util.h"
58 #include "x_util.h"
59 
60 #if MOTIF
61 
62 /* special formatting for headings in right window, if desired */
63 const char *const TAG_NORMAL = "NORMAL";
64 const char *const TAG_TOPIC_HEADING = "TOPIC_HEADING";
65 const char *const TAG_TOPIC_LABEL = "TOPIC_LABEL";
66 
67 # define SHELL_WIDGET_CLASS	xmDialogShellWidgetClass
68 # define PANED_WIDGET_CLASS	xmPanedWindowWidgetClass
69 # define FORM_WIDGET_CLASS	xmFormWidgetClass
70 
71 # define FORM_ARGS		XmNhorizontalSpacing, 10,	\
72 				NULL
73 # define HELP_SHELL_ARGS	XmNdeleteResponse, XmDO_NOTHING,	\
74   				XtNmappedWhenManaged, False,		\
75 				NULL
76 # define HELP_PANED_ARGS	XmNsashWidth, 1,	\
77 				XmNuserData, info,	\
78 				XmNsashHeight, 1,	\
79 				NULL
80 # define LEFT_FORM_ARGS		XmNtopAttachment, XmATTACH_FORM,	\
81   				XmNleftAttachment, XmATTACH_FORM,	\
82   				XmNbottomAttachment, XmATTACH_FORM,	\
83 				XmNtopOffset, 9,		\
84 				XmNbottomOffset, 10,		\
85 				NULL
86 # define RIGHT_FORM_ARGS	XmNtopAttachment, XmATTACH_FORM,	\
87   				XmNleftAttachment, XmATTACH_WIDGET,		\
88   				XmNleftWidget, left_form,			\
89   				XmNrightAttachment, XmATTACH_FORM,		\
90   				XmNbottomAttachment, XmATTACH_FORM,		\
91 				XmNbottomOffset, 10,		\
92 				NULL
93 # define ACTION_AREA_ARGS	NULL
94 
95 #else /* MOTIF */
96 
97 # define SHELL_WIDGET_CLASS	transientShellWidgetClass
98 # define PANED_WIDGET_CLASS	panedWidgetClass
99 # define FORM_WIDGET_CLASS	formWidgetClass
100 
101 # define FORM_ARGS		XtNdefaultDistance, 14,			\
102 				NULL
103 # define HELP_SHELL_ARGS	XtNx, 60,				\
104 				XtNy, 80,				\
105   				XtNtransientFor, globals.widgets.top_level,		\
106   				XtNtranslations, xlats,			\
107   				XtNtransientFor, parent,		\
108   				XtNallowShellResize, False,		\
109 				NULL
110 # define HELP_PANED_ARGS	NULL
111 # define LEFT_FORM_ARGS		XtNtop, XtChainTop,		\
112   				XtNbottom, XtChainBottom,	\
113   				XtNleft, XtChainLeft,		\
114   				XtNright, XtChainLeft,		\
115   				XtNborderWidth, 0,		\
116   				NULL
117 # define RIGHT_FORM_ARGS	XtNfromHoriz, left_form,	\
118   				XtNtop, XtChainTop,		\
119   				XtNbottom, XtChainBottom,	\
120   				XtNleft, XtChainLeft,		\
121   				XtNright, XtChainRight,		\
122   				XtNborderWidth, 0,		\
123   				NULL
124 # define ACTION_AREA_ARGS	XtNdefaultDistance, 6,	\
125 				XtNshowGrip, False,	\
126 				XtNskipAdjust, True,	\
127 				NULL
128 #endif /* MOTIF */
129 
130 
131 static void
ok_cb(Widget w,XtPointer client_data,XtPointer call_data)132 ok_cb(Widget w, XtPointer client_data, XtPointer call_data)
133 {
134     struct topic_info *info = (struct topic_info *)client_data;
135 
136     UNUSED(w);
137     UNUSED(call_data);
138 
139     ASSERT(info != NULL, "No info passed to callback!");
140 
141     if (info->ok_callback != NULL)
142 	info->ok_callback(info);
143     XtPopdown(info->shell);
144 }
145 
146 static void
cancel_cb(Widget w,XtPointer client_data,XtPointer call_data)147 cancel_cb(Widget w, XtPointer client_data, XtPointer call_data)
148 {
149     struct topic_info *info = (struct topic_info *)client_data;
150 
151     UNUSED(w);
152     UNUSED(call_data);
153 
154     ASSERT(info != NULL, "No info passed to callback!");
155 
156     if (info->cancel_callback != NULL)
157 	info->cancel_callback(info);
158     XtPopdown(info->shell);
159 }
160 
161 #if MOTIF
162 /*
163  * We are overriding this since otherwise the Text widget would attempt to
164  * first move the invisible cursor instead of the scroll bars.
165  */
166 /* FIXME: This gives a warning:
167    Warning: Actions not found: scroll-one-line-down, scroll-one-line-up
168 */
169 /* "<Key>osfUp: scroll-one-line-down()\n" */
170 /* "<Key>osfDown: scroll-one-line-up()\n" */
171 #endif
172 
173 #if MOTIF
174 static XmString
175 #else
176 static char *
177 #endif
create_label_string(const char * title,const char * subtitle)178 create_label_string(const char *title, const char *subtitle)
179 {
180 #if MOTIF
181     XmString label;
182     if (subtitle == NULL) { /* simple bold label */
183 	label = XmStringCreate((char *)title, (char *)TAG_TOPIC_LABEL);
184     }
185     else { /* two-part title with subheading */
186 	XmString s1, s2, s3, s4;
187 	s1 = XmStringCreate((char *)title, (char *)TAG_TOPIC_HEADING);
188 	s2 = XmStringCreate((char *)"   ", (char *)TAG_NORMAL);
189 	s3 = XmStringCreate((char *)subtitle, (char *)TAG_NORMAL);
190 	s4 = XmStringConcat(s1, s2);
191 	label = XmStringConcat(s4, s3);
192 	XmStringFree(s1);
193 	XmStringFree(s2);
194 	XmStringFree(s3);
195 	XmStringFree(s4);
196     }
197 #else
198     char *label = xstrdup(title);
199     if (subtitle != NULL && strlen(subtitle) > 0) {
200 	label = xstrcat(label, "   -   ");
201 	label = xstrcat(label, subtitle);
202     }
203 #endif
204     return label;
205 }
206 
207 static Widget
create_label_widget(Widget parent,const char * name,struct topic_info * info)208 create_label_widget(Widget parent, const char *name, struct topic_info *info)
209 {
210     Widget label;
211 
212 #if MOTIF
213 
214     XmString label_string = create_label_string(info->items[0].topic, info->items[0].title);
215     label = XtVaCreateWidget(name, xmLabelWidgetClass, parent,
216 			     XmNtopAttachment, XmATTACH_FORM,
217 			     XmNtopOffset, 14,
218 			     XmNleftAttachment, XmATTACH_FORM,
219 			     XmNrightAttachment, XmATTACH_FORM,
220 			     XmNlabelString, label_string,
221 			     XmNalignment, XmALIGNMENT_BEGINNING,
222 			     NULL);
223     XmStringFree(label_string);
224 
225 #else /* MOTIF */
226 
227     char *label_string = create_label_string(info->items[0].topic, info->items[0].title);
228     label = XtVaCreateWidget(name, labelWidgetClass, parent,
229 			     XtNlabel, label_string,
230 			     XtNborderWidth, 0,
231 			     /* XtNinternalHeight, 2, */
232 			     XtNjustify, XtJustifyLeft,
233 			     NULL);
234     free(label_string);
235 
236 #endif /* MOTIF */
237 
238     return label;
239 }
240 
241 void
select_topic(struct topic_info * info,size_t idx)242 select_topic(struct topic_info *info, size_t idx)
243 {
244 #if MOTIF
245     XmString label;
246 #else
247     char *label;
248 #endif
249 
250     /* change the heading in right window */
251     label = create_label_string(info->items[idx].topic, info->items[idx].title);
252     XtVaSetValues(info->topic_label,
253 #if MOTIF
254 		  XmNlabelString,
255 #else
256 		  XtNlabel,
257 #endif
258 		  label, NULL);
259 
260 #if MOTIF
261     XmStringFree(label);
262     XmListSelectPos(info->topics_list, idx + 1, False);
263 #else
264     free(label);
265     XawListHighlight(info->topics_list, idx);
266 #endif
267 
268     if (info->curr_selected != 0) {
269 	XtUnmanageChild(info->curr_selected);
270     }
271     else {
272 	/* Note: doesn't matter if not managed yet */
273 	/* 	XtUnmanageChild(info->items[0].widget); */
274     }
275 
276     XtManageChild(info->items[idx].widget);
277     info->curr_selected = info->items[idx].widget;
278 
279 #if MOTIF
280     XmUpdateDisplay(info->shell);
281 #endif
282 }
283 
284 static void
select_topic_cb(Widget w,XtPointer client_data,XtPointer call_data)285 select_topic_cb(Widget w, XtPointer client_data, XtPointer call_data)
286 {
287     struct topic_info *info = (struct topic_info *)client_data;
288     size_t idx;
289 
290 #if MOTIF
291     idx = ((XmListCallbackStruct *)call_data)->item_position - 1;
292 #else
293     idx = ((XawListReturnStruct *)call_data)->list_index;
294 #endif
295     UNUSED(w);
296 
297     /* ASSERT(idx < info->items_size, "list position exceeds items_size!"); */
298 
299     select_topic(info, idx);
300 }
301 
302 static Widget
create_button(Widget parent,const char * name,Boolean show_as_default,Boolean left_position)303 create_button(Widget parent, const char *name, Boolean show_as_default, Boolean left_position)
304 {
305     Widget button;
306 #if MOTIF
307     XmString s1 = XmStringCreateLocalized((char *)name);
308     button = XtVaCreateWidget(name, xmPushButtonWidgetClass, parent,
309 			      XmNlabelString, s1,
310 			      XmNdefaultButtonShadowThickness, 1,
311 			      XmNtopAttachment, XmATTACH_FORM,
312 			      XmNbottomAttachment, XmATTACH_FORM,
313 			      XmNmarginWidth, 6,
314 			      XmNmarginHeight, 4,
315 			      XmNtopOffset, 10,
316 			      XmNbottomOffset, 10,
317 			      NULL);
318     if (left_position)
319 	XtVaSetValues(button,
320 		      XmNleftAttachment, XmATTACH_FORM,
321 		      XmNleftOffset, 10,
322 		      NULL);
323     else
324 	XtVaSetValues(button,
325 		      XmNrightAttachment, XmATTACH_FORM,
326 		      XmNrightOffset, 10,
327 		      NULL);
328     if (show_as_default)
329 	XtVaSetValues(button, XmNshowAsDefault, True, NULL);
330 
331     XtManageChild(button);
332     XmStringFree(s1);
333 #else /* MOTIF */
334     UNUSED(show_as_default);
335     button = XtVaCreateManagedWidget(name, commandWidgetClass, parent,
336 				     XtNtop, XtChainTop,
337 				     XtNbottom, XtChainBottom,
338 				     NULL);
339     if (left_position)
340 	XtVaSetValues(button,
341 		      XtNleft, XtChainLeft,
342 		      XtNright, XtChainLeft,
343 		      NULL);
344     else
345 	XtVaSetValues(button,
346 		      XtNleft, XtChainRight,
347 		      XtNright, XtChainRight,
348 		      NULL);
349 #endif /* MOTIF */
350     return button;
351 }
352 
353 static void
close_topic_window(Widget w,XEvent * event,String * params,Cardinal * num_params)354 close_topic_window(Widget w, XEvent *event, String *params, Cardinal *num_params)
355 {
356     Widget button;
357     void *ptr;
358     struct topic_info *info;
359 
360     UNUSED(w);
361     UNUSED(event);
362 
363     if (*num_params < 1) {
364 	XDVI_WARNING((stderr, "Wrong argument number (%d) in callback!", *num_params));
365 	return;
366     }
367 
368     sscanf(*params, "%p", &ptr);
369     info = (struct topic_info *)ptr;
370 
371     /* get a callback to close the button, in decreasing order of preference */
372     if (get_widget_by_name(&button, info->shell, "Cancel", False)
373 	|| get_widget_by_name(&button, info->shell, "Close", False)
374 	|| get_widget_by_name(&button, info->shell, "OK", False)) {
375 	XtCallCallbacks(button,
376 #if MOTIF
377 			XmNactivateCallback,
378 #else
379 			XtNcallback,
380 #endif
381 			info);
382     }
383     else {
384 	XDVI_WARNING((stderr, "No button found for widget %p!\n", (void *)info->shell));
385     }
386 }
387 
388 static XtActionsRec popdown_actions[] = {
389 #if !MOTIF
390     { "WM_popdown", close_topic_window },
391 #endif
392     { "close-topic-window", close_topic_window },
393 };
394 
395 static Widget
create_list_widget(Widget parent,const char * w_name,struct topic_info * info)396 create_list_widget(Widget parent, const char *w_name, struct topic_info *info)
397 {
398     Widget list;
399     size_t tnum;
400 #if MOTIF
401     XmString *topics = NULL;
402 #else
403     static char **topics = NULL;
404 #endif
405 
406     for (tnum = 0; info->items[tnum].topic != NULL; tnum++) {
407 	/* 1 more for terminating NULL needed for Xaw */
408 	topics = xrealloc(topics, (tnum + 2) * sizeof *topics);
409 #if MOTIF
410 	topics[tnum] = XmStringCreateLocalized(info->items[tnum].topic);
411 #else
412 	topics[tnum] = xstrdup(info->items[tnum].topic);
413 #endif
414     }
415 
416 #ifndef MOTIF
417     /* list needs to be terminated for Xaw */
418     topics[tnum] = NULL;
419 #endif
420 
421 #if MOTIF
422     {
423 	size_t i;
424 	Arg args[20];
425 	int n = 0;
426 	XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
427 	XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
428 	XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
429 	XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
430 	XtSetArg(args[n], XmNlistSpacing, 4); n++;
431 	XtSetArg(args[n], XmNlistMarginHeight, 4); n++;
432 	XtSetArg(args[n], XmNlistMarginWidth, 4); n++;
433 	XtSetArg(args[n], XmNhighlightThickness, 0); n++;
434 	XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
435 
436 	list = XmCreateScrolledList(parent, (char *)w_name, args, n);
437 
438 #if defined(__GNUC__) && DEVEL_MODE
439 #warning TODO: make up/down scroll the list, and PgUp/PgDown scroll help text
440 #warning TODO: add wheel mouse bindings
441 	/*
442 	 * also fix bug with arrow keys first moving invisible cursor, then scrollbars
443 	 */
444 #endif
445 
446 	XmListDeleteAllItems(list);
447 	XmListAddItems(list, topics, tnum, 0);
448 
449 	for (i = 0; i < tnum; ++i) {
450 	    XmStringFree(topics[i]);
451 	}
452 	free(topics);
453     }
454     XtAddCallback(list, XmNbrowseSelectionCallback, select_topic_cb, info);
455     XmListSelectPos(list, 1, False); /* default position */
456 #else /* MOTIF */
457     list = XtVaCreateWidget(w_name, listWidgetClass, parent,
458 			    XtNlist, topics,
459 			    XtNdefaultColumns, 1,
460 			    XtNforceColumns, True,
461 			    XtNverticalList, True,
462 			    XtNrowSpacing, 4,
463 			    XtNheight, 429,
464 			    NULL);
465     XtAddCallback(list, XtNcallback, select_topic_cb, info);
466     XawListHighlight(list, 0); /* default position */
467 #endif /* MOTIF */
468     XtManageChild(list);
469     return list;
470 }
471 
472 
473 Widget
create_topic_window(Widget parent,const char * window_title,const char * widget_name,struct topic_info * info,void (* init_items_func)(struct topic_info * info),const char * ok_label,const char * cancel_label)474 create_topic_window(Widget parent,
475 		    const char *window_title,
476 		    const char *widget_name,
477 		    struct topic_info *info,
478 		    void (*init_items_func)(struct topic_info *info),
479 		    /* OK button/callbacks */
480 		    const char *ok_label,
481 		    /* Cancel button/callbacks (can be NULL) */
482 		    const char *cancel_label)
483 {
484     Widget topic_window, topics_list, topic_label;
485     Widget pane, form, left_form, right_container_form, right_form;
486     Widget action_area, ok_button, cancel_button;
487     XtTranslations xlats;
488 
489     XtAppContext app = NULL;
490     Atom WM_DELETE_WINDOW;
491 
492     char *translation_str;
493     size_t i;
494 
495 #if !MOTIF
496     translation_str = get_string_va("<Message>WM_PROTOCOLS: WM_popdown(%p)", info);
497     xlats = XtParseTranslationTable(translation_str);
498     free(translation_str);
499 #endif
500 
501     topic_window = XtVaCreatePopupShell(widget_name, SHELL_WIDGET_CLASS,
502 					parent,
503 					XtNtitle, window_title,
504 					HELP_SHELL_ARGS);
505 
506 #if MOTIF
507     /* make the window manager destroy action pop down the window */
508     WM_DELETE_WINDOW = XmInternAtom(XtDisplay(topic_window), "WM_DELETE_WINDOW", False);
509     XmAddWMProtocolCallback(topic_window, WM_DELETE_WINDOW, cancel_cb, info);
510 #else
511     WM_DELETE_WINDOW = XInternAtom(XtDisplay(topic_window), "WM_DELETE_WINDOW", False);
512 #endif
513     app = XtWidgetToApplicationContext(topic_window);
514     XtAppAddActions(app, popdown_actions, XtNumber(popdown_actions));
515 
516     pane = XtVaCreateWidget("topic_pane", PANED_WIDGET_CLASS, topic_window, HELP_PANED_ARGS);
517 
518     form = XtVaCreateWidget("form", FORM_WIDGET_CLASS, pane, FORM_ARGS);
519 
520     /* left pane for topics selection */
521     left_form = XtVaCreateWidget("left_form", FORM_WIDGET_CLASS, form, LEFT_FORM_ARGS);
522 
523     /* right form for topics display */
524     right_container_form = XtVaCreateWidget("right_container_form", FORM_WIDGET_CLASS, form, RIGHT_FORM_ARGS);
525 
526     /*
527       Initialize the topic label and title string with dummy values - the real values
528       are only known inside init_items_func(). For Xaw, the size of the widget will be
529       adjusted below (for Motif it's always the full width of the right form, which is
530       what we want).
531     */
532     /* FIXME: Motif label is still chopped off if it's longer than right form -
533        work around by putting longest possible lable here ... */
534     /* Also, not all children will be sized correctly, e.g. the colors - work around
535        by specifying a rather large dummy text ... */
536     info->items[0].topic = "text text text";
537     info->items[0].title = "text text text text text text text text text text text text text text text text text";
538     topic_label = create_label_widget(right_container_form, "topic_label", info);
539     info->topic_label = topic_label;
540 
541     right_form = XtVaCreateWidget("right_form", FORM_WIDGET_CLASS,
542 				  right_container_form,
543 #if MOTIF
544 				  XmNtopAttachment, XmATTACH_WIDGET,
545 				  XmNtopWidget, topic_label,
546 				  XmNtopOffset, 10,
547 				  XmNleftAttachment, XmATTACH_FORM,
548 				  XmNrightAttachment, XmATTACH_FORM,
549 				  XmNbottomAttachment, XmATTACH_FORM,
550 #else
551 				  XtNborderWidth, 0,
552 				  XtNfromVert, topic_label,
553 				  XtNvertDistance, 6,
554 #endif /* MOTIF */
555 				  NULL);
556     info->right_form = right_form;
557 
558     /*
559       Call the init_items_func callback. This will populate the info->items list
560       with the appropriate info, and create the forms (info->items[i].widget)
561       which are the children of right_form (which is accessed inside init_items_func()
562       through info->right_form), and all their subchildren.
563 
564       It is important that every child form is already managed inside init_items_func
565       so that the total size of the preferences window is set to the maximum size of
566       the children. (The children will be unmanaged again below). All these widgets
567       are never destroyed, and are managed by demand via the list selection callback
568       (select_topic_cb).
569     */
570     init_items_func(info);
571 
572     XtManageChild(topic_label);
573 
574     topics_list = create_list_widget(left_form, "topics_list", info);
575     info->topics_list = topics_list;
576 
577     translation_str = get_string_va("#override \n"
578 				    "<Key>q:close-topic-window(%p)\n"
579 #ifdef MOTIF
580 				    "<Key>osfCancel:close-topic-window(%p)\n"
581 #else
582 				    "<Key>Escape:close-topic-window(%p)\n"
583 #endif
584 				    "<Key>Return:close-topic-window(%p)",
585 				    info, info, info);
586     xlats = XtParseTranslationTable(translation_str);
587     free(translation_str);
588     translation_str = NULL;
589 
590     XtOverrideTranslations(pane, xlats);
591     XtOverrideTranslations(topics_list, xlats);
592 
593     XtManageChild(left_form);
594 
595     XtManageChild(right_form);
596     XtManageChild(right_container_form);
597 
598     /* action area at bottom */
599     action_area = XtVaCreateWidget("action_area", FORM_WIDGET_CLASS, pane, ACTION_AREA_ARGS);
600 
601     ok_button = create_button(action_area, ok_label, True, True);
602     XtOverrideTranslations(ok_button, xlats);
603 
604     if (cancel_label != NULL) {
605 	cancel_button = create_button(action_area, cancel_label, False, False);
606 	adjust_width(ok_button, cancel_button);
607 #if MOTIF
608 	XtAddCallback(cancel_button, XmNactivateCallback, cancel_cb, info);
609 #else
610 	XtAddCallback(cancel_button, XtNcallback, cancel_cb, info);
611 #endif
612 	XtOverrideTranslations(cancel_button, xlats);
613     }
614 
615     XtManageChild(action_area);
616 
617     XtManageChild(form);
618 
619     XtManageChild(pane);
620 
621     XtRealizeWidget(topic_window);
622 
623     /* Now unmanage all children of right_form, as described above. */
624     for (i = 0; info->items[i].topic != NULL; i++) {
625 	XtUnmanageChild(info->items[i].widget);
626     }
627     info->curr_selected = 0;
628 
629 #if MOTIF
630     /* enable OK button */
631     XmProcessTraversal(ok_button, XmTRAVERSE_CURRENT);
632     XtOverrideTranslations(ok_button,
633 			   XtParseTranslationTable("<Key>Return:ArmAndActivate()\n"
634 						   "<Key>q:ArmAndActivate()"));
635     XtAddCallback(ok_button, XmNactivateCallback, ok_cb, info);
636     XmAddWMProtocolCallback(topic_window, WM_DELETE_WINDOW, cancel_cb, info);
637     { /* disable resizing of lower part of pane (and button) */
638 	Dimension h;
639 	XtVaGetValues(ok_button, XmNheight, &h, NULL);
640 	XtVaSetValues(action_area,
641 		      XmNpaneMaximum, h + 20,
642 		      XmNpaneMinimum, h + 20,
643 		      NULL);
644     }
645 #else
646     XSetWMProtocols(XtDisplay(topic_window), XtWindow(topic_window), &WM_DELETE_WINDOW, 1);
647     XtAddCallback(ok_button, XtNcallback, ok_cb, info);
648 #endif
649     return topic_window;
650 }
651