- Rearrange reactos.dff according to rosapps rearrange.
[reactos.git] / rosapps / applications / mc / src / help.c
1 /* Hypertext file browser.
2 Copyright (C) 1994, 1995 Miguel de Icaza.
3 Copyright (C) 1994, 1995 Janne Kukonlehto
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 Implements the hypertext file viewer.
20 The hypertext file is a file that may have one or more nodes. Each
21 node ends with a ^D character and starts with a bracket, then the
22 name of the node and then a closing bracket.
23
24 Links in the hypertext file are specified like this: the text that
25 will be highlighted should have a leading ^A, then it comes the
26 text, then a ^B indicating that highlighting is done, then the name
27 of the node you want to link to and then a ^C.
28
29 The file must contain a ^D at the beginning and at the end of the
30 file or the program will not be able to detect the end of file.
31
32 Lazyness/widgeting attack: This file does use the dialog manager
33 and uses mainly the dialog to achieve the help work. there is only
34 one specialized widget and it's only used to forward the mouse messages
35 to the appropiate routine.
36
37 */
38
39 #include <config.h>
40 #include "tty.h"
41 #include <stdio.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <malloc.h>
45 #include <errno.h>
46 #include <string.h>
47 #include "mad.h"
48 #include "color.h"
49 #include "util.h"
50 #include "dialog.h"
51 #include "win.h"
52 #include "global.h"
53 #include "mouse.h"
54 #include "key.h" /* For mi_getch() */
55 #include "help.h"
56 #include "layout.h" /* keybar_visible */
57 #include "x.h"
58 #include "dlg.h" /* For Dlg_head */
59 #include "widget.h" /* For Widget */
60
61 #ifdef HAVE_TK
62 # include "tkmain.h"
63 #endif
64
65 #define MAXLINKNAME 80
66 #define HISTORY_SIZE 20
67 #define HELP_WINDOW_WIDTH 62
68
69 /* "$Id$" */
70
71 static char *data; /* Pointer to the loaded data file */
72 static int help_lines = 18; /* Lines in help viewer */
73 static int history_ptr; /* For the history queue */
74 static char *main; /* The main node */
75 static char *last_shown = 0; /* Last byte shown in a screen */
76 static int end_of_node = 0; /* Flag: the last character of the node shown? */
77 char *currentpoint, *startpoint;
78 static char *selected_item;
79
80 /* The widget variables */
81 static Dlg_head *whelp;
82
83 static struct {
84 char *page; /* Pointer to the selected page */
85 char *link; /* Pointer to the selected link */
86 } history [HISTORY_SIZE];
87
88 /* Link areas for the mouse */
89 typedef struct Link_Area {
90 int x1, y1, x2, y2;
91 char *link_name;
92 struct Link_Area *next;
93 } Link_Area;
94
95 static Link_Area *link_area = NULL;
96 static int inside_link_area = 0;
97
98 static int help_callback (struct Dlg_head *h, int id, int msg);
99
100 #ifdef OS2_NT
101 struct {
102 int acscode;
103 int pccode;
104 } acs2pc_table [] = {
105 { 'q', 0xC4 },
106 { 'x', 0xB3 },
107 { 'l', 0xDA },
108 { 'k', 0xBF },
109 { 'm', 0xC0 },
110 { 'j', 0xD9 },
111 { 'a', 0xB0 },
112 { 'u', 0xB4 },
113 { 't', 0xC3 },
114 { 'w', 0xC2 },
115 { 'v', 0xC1 },
116 { 'n', 0xC5 },
117 { 0, 0 } };
118
119 static int acs2pc (int acscode)
120 {
121 int i;
122
123 for (i = 0; acs2pc_table[i].acscode != 0; i++)
124 if (acscode == acs2pc_table[i].acscode) {
125 return acs2pc_table[i].pccode;
126 }
127 return 0;
128 }
129 #endif
130
131 /* returns the position where text was found in the start buffer */
132 /* or 0 if not found */
133 char *search_string (char *start, char *text)
134 {
135 char *d = text;
136 char *e = start;
137
138 /* fmt sometimes replaces a space with a newline in the help file */
139 /* Replace the newlines in the link name with spaces to correct the situation */
140 while (*d){
141 if (*d == '\n')
142 *d = ' ';
143 d++;
144 }
145 /* Do search */
146 for (d = text; *e; e++){
147 if (*d == *e)
148 d++;
149 else
150 d = text;
151 if (!*d)
152 return e+1;
153 }
154 return 0;
155 }
156
157 /* Searches text in the buffer pointed by start. Search ends */
158 /* if the CHAR_NODE_END is found in the text. Returns 0 on failure */
159 static char *search_string_node (char *start, char *text)
160 {
161 char *d = text;
162 char *e = start;
163
164 if (!start)
165 return 0;
166
167 for (; *e && *e != CHAR_NODE_END; e++){
168 if (*d == *e)
169 d++;
170 else
171 d = text;
172 if (!*d)
173 return e+1;
174 }
175 return 0;
176 }
177
178 /* Searches the_char in the buffer pointer by start and searches */
179 /* it can search forward (direction = 1) or backward (direction = -1) */
180 static char *search_char_node (char *start, char the_char, int direction)
181 {
182 char *e;
183
184 e = start;
185
186 for (; *e && (*e != CHAR_NODE_END); e += direction){
187 if (*e == the_char)
188 return e;
189 }
190 return 0;
191 }
192
193 /* Returns the new current pointer when moved lines lines */
194 static char *move_forward2 (char *c, int lines)
195 {
196 char *p;
197 int line;
198
199 currentpoint = c;
200 for (line = 0, p = currentpoint; *p && *p != CHAR_NODE_END; p++){
201 if (line == lines)
202 return currentpoint = p;
203 if (*p == '\n')
204 line++;
205 }
206 return currentpoint = c;
207 }
208
209 static char *move_backward2 (char *c, int lines)
210 {
211 char *p;
212 int line;
213
214 currentpoint = c;
215 for (line = 0, p = currentpoint; *p && p >= data; p--){
216 if (*p == CHAR_NODE_END)
217 {
218 /* We reached the beginning of the node */
219 /* Skip the node headers */
220 while (*p != ']') p++;
221 return currentpoint = p + 2;
222 }
223 if (*(p - 1) == '\n')
224 line++;
225 if (line == lines)
226 return currentpoint = p;
227 }
228 return currentpoint = c;
229 }
230
231 static void move_forward (int i)
232 {
233 if (end_of_node)
234 return;
235 currentpoint = move_forward2 (currentpoint, i);
236 }
237
238 static void move_backward (int i)
239 {
240 currentpoint = move_backward2 (currentpoint, ++i);
241 }
242
243 static void move_to_top (int dummy)
244 {
245 while (currentpoint > data && *currentpoint != CHAR_NODE_END)
246 currentpoint--;
247 while (*currentpoint != ']')
248 currentpoint++;
249 currentpoint = currentpoint + 1;
250 selected_item = NULL;
251 }
252
253 static void move_to_bottom (int dummy)
254 {
255 while (*currentpoint && *currentpoint != CHAR_NODE_END)
256 currentpoint++;
257 currentpoint--;
258 move_backward (help_lines - 1);
259 }
260
261 char *help_follow_link (char *start, char *selected_item)
262 {
263 char link_name [MAXLINKNAME];
264 char *p;
265 int i = 0;
266
267 if (!selected_item)
268 return start;
269
270 for (p = selected_item; *p && *p != CHAR_NODE_END && *p != CHAR_LINK_POINTER; p++)
271 ;
272 if (*p == CHAR_LINK_POINTER){
273 link_name [0] = '[';
274 for (i = 1; *p != CHAR_LINK_END && *p && *p != CHAR_NODE_END && i < MAXLINKNAME-3; )
275 link_name [i++] = *++p;
276 link_name [i-1] = ']';
277 link_name [i] = 0;
278 p = search_string (data, link_name);
279 if (p)
280 return p;
281 }
282 return _(" Help file format error\n\x4"); /* */
283 }
284
285 static char *select_next_link (char *start, char *current_link)
286 {
287 char *p;
288
289 if (!current_link)
290 return 0;
291
292 p = search_string_node (current_link, STRING_LINK_END);
293 if (!p)
294 return NULL;
295 p = search_string_node (p, STRING_LINK_START);
296 if (!p)
297 return NULL;
298 return p - 1;
299 }
300
301 static char *select_prev_link (char *start, char *current_link)
302 {
303 char *p;
304
305 if (!current_link)
306 return 0;
307
308 p = current_link - 1;
309 if (p <= start)
310 return 0;
311
312 p = search_char_node (p, CHAR_LINK_START, -1);
313 return p;
314 }
315
316 static void start_link_area (int x, int y, char *link_name)
317 {
318 Link_Area *new;
319
320 if (inside_link_area)
321 message (0, _(" Warning "), _(" Internal bug: Double start of link area "));
322
323 /* Allocate memory for a new link area */
324 new = (Link_Area*) xmalloc (sizeof (Link_Area), "Help, link_area");
325 new->next = link_area;
326 link_area = new;
327
328 /* Save the beginning coordinates of the link area */
329 link_area->x1 = x;
330 link_area->y1 = y;
331
332 /* Save the name of the destination anchor */
333 link_area->link_name = link_name;
334
335 inside_link_area = 1;
336 }
337
338 static void end_link_area (int x, int y)
339 {
340 if (inside_link_area){
341 /* Save the end coordinates of the link area */
342 link_area->x2 = x;
343 link_area->y2 = y;
344
345 inside_link_area = 0;
346 }
347 }
348
349 static void clear_link_areas (void)
350 {
351 Link_Area *current;
352
353 while (link_area){
354 current = link_area;
355 link_area = current -> next;
356 free (current);
357 }
358 inside_link_area = 0;
359 }
360
361 static void show (Dlg_head *h, char *paint_start)
362 {
363 char *p;
364 int col, line, c;
365 int painting = 1;
366 int acs; /* Flag: Alternate character set active? */
367 int repeat_paint;
368 int active_col, active_line;/* Active link position */
369
370 do {
371
372 line = col = acs = active_col = active_line = repeat_paint = 0;
373
374 clear_link_areas ();
375 if (selected_item < paint_start)
376 selected_item = NULL;
377
378 for (p = paint_start; *p != CHAR_NODE_END && line < help_lines; p++){
379 c = *p;
380 switch (c){
381 case CHAR_LINK_START:
382 if (selected_item == NULL)
383 selected_item = p;
384 if (p == selected_item){
385 attrset (HELP_SLINK_COLOR);
386
387 /* Store the coordinates of the link */
388 active_col = col + 2;
389 active_line = line + 2;
390 }
391 else
392 attrset (HELP_LINK_COLOR);
393 start_link_area (col, line, p);
394 break;
395 case CHAR_LINK_POINTER:
396 painting = 0;
397 end_link_area (col - 1, line);
398 break;
399 case CHAR_LINK_END:
400 painting = 1;
401 attrset (HELP_NORMAL_COLOR);
402 break;
403 case CHAR_ALTERNATE:
404 acs = 1;
405 break;
406 case CHAR_NORMAL:
407 acs = 0;
408 break;
409 case CHAR_VERSION:
410 dlg_move (h, line+2, col+2);
411 addstr (VERSION);
412 col += strlen (VERSION);
413 break;
414 case CHAR_BOLD_ON:
415 attrset (HELP_BOLD_COLOR);
416 break;
417 case CHAR_ITALIC_ON:
418 attrset (HELP_ITALIC_COLOR);
419 break;
420 case CHAR_BOLD_OFF:
421 attrset (HELP_NORMAL_COLOR);
422 break;
423 case '\n':
424 line++;
425 col = 0;
426 break;
427 case '\t':
428 col = (col/8 + 1) * 8;
429 break;
430 case CHAR_MCLOGO:
431 case CHAR_TEXTONLY_START:
432 case CHAR_TEXTONLY_END:
433 break;
434 case CHAR_XONLY_START:
435 while (*p && *p != CHAR_NODE_END && *p != CHAR_XONLY_END)
436 p++;
437 if (*p == CHAR_NODE_END || !*p)
438 p--;
439 break;
440 default:
441 if (!painting)
442 continue;
443 if (col > HELP_WINDOW_WIDTH-1)
444 continue;
445
446 dlg_move (h, line+2, col+2);
447 if (acs){
448 if (c == ' ' || c == '.')
449 addch (c);
450 else
451 #ifndef OS2_NT
452 #ifndef HAVE_SLANG
453 addch (acs_map [c]);
454 #else
455 SLsmg_draw_object (h->y + line + 2, h->x + col + 2, c);
456 #endif
457 #else
458 addch (acs2pc (c));
459 #endif /* OS2_NT */
460 } else
461 addch (c);
462 col++;
463 break;
464 }
465 }
466 last_shown = p;
467 end_of_node = line < help_lines;
468 attrset (HELP_NORMAL_COLOR);
469 if (selected_item >= last_shown){
470 if (link_area != NULL){
471 selected_item = link_area->link_name;
472 repeat_paint = 1;
473 }
474 else
475 selected_item = NULL;
476 }
477 } while (repeat_paint);
478
479 /* Position the cursor over a nice link */
480 if (active_col)
481 dlg_move (h, active_line, active_col);
482 }
483
484 static int help_event (Gpm_Event *event, Widget *w)
485 {
486 Link_Area *current_area;
487
488 if (! (event->type & GPM_UP))
489 return 0;
490
491 /* The event is relative to the dialog window, adjust it: */
492 event->y -= 2;
493
494 if (event->buttons & GPM_B_RIGHT){
495 currentpoint = startpoint = history [history_ptr].page;
496 selected_item = history [history_ptr].link;
497 history_ptr--;
498 if (history_ptr < 0)
499 history_ptr = HISTORY_SIZE-1;
500
501 help_callback (w->parent, 0, DLG_DRAW);
502 return 0;
503 }
504
505 /* Test whether the mouse click is inside one of the link areas */
506 current_area = link_area;
507 while (current_area)
508 {
509 /* Test one line link area */
510 if (event->y == current_area->y1 && event->x >= current_area->x1 &&
511 event->y == current_area->y2 && event->x <= current_area->x2)
512 break;
513 /* Test two line link area */
514 if (current_area->y1 + 1 == current_area->y2){
515 /* The first line */
516 if (event->y == current_area->y1 && event->x >= current_area->x1)
517 break;
518 /* The second line */
519 if (event->y == current_area->y2 && event->x <= current_area->x2)
520 break;
521 }
522 /* Mouse will not work with link areas of more than two lines */
523
524 current_area = current_area -> next;
525 }
526
527 /* Test whether a link area was found */
528 if (current_area){
529 /* The click was inside a link area -> follow the link */
530 history_ptr = (history_ptr+1) % HISTORY_SIZE;
531 history [history_ptr].page = currentpoint;
532 history [history_ptr].link = current_area->link_name;
533 currentpoint = startpoint = help_follow_link (currentpoint, current_area->link_name);
534 selected_item = NULL;
535 } else{
536 if (event->y < 0)
537 move_backward (help_lines - 1);
538 else if (event->y >= help_lines)
539 move_forward (help_lines - 1);
540 else if (event->y < help_lines/2)
541 move_backward (1);
542 else
543 move_forward (1);
544 }
545
546 /* Show the new node */
547 help_callback (w->parent, 0, DLG_DRAW);
548
549 return 0;
550 }
551
552 /* show help */
553 void help_help_cmd (Dlg_head *h)
554 {
555 history_ptr = (history_ptr+1) % HISTORY_SIZE;
556 history [history_ptr].page = currentpoint;
557 history [history_ptr].link = selected_item;
558 currentpoint = startpoint = search_string (data, "[How to use help]") + 1;
559 selected_item = NULL;
560 #ifndef HAVE_XVIEW
561 help_callback (h, 0, DLG_DRAW);
562 #endif
563 }
564
565 void help_index_cmd (Dlg_head *h)
566 {
567 char *new_item;
568
569 history_ptr = (history_ptr+1) % HISTORY_SIZE;
570 history [history_ptr].page = currentpoint;
571 history [history_ptr].link = selected_item;
572 currentpoint = startpoint = search_string (data, "[Help]") + 1;
573
574 if (!(new_item = search_string (data, "[Contents]")))
575 message (1, MSG_ERROR, _(" Can't find node [Contents] in help file "));
576 else
577 currentpoint = startpoint = new_item + 1;
578 selected_item = NULL;
579 #ifndef HAVE_XVIEW
580 help_callback (h, 0, DLG_DRAW);
581 #endif
582 }
583
584 static void quit_cmd (void *x)
585 {
586 Dlg_head *h = (Dlg_head *) x;
587
588 dlg_stop (x);
589 }
590
591 static void prev_node_cmd (Dlg_head *h)
592 {
593 currentpoint = startpoint = history [history_ptr].page;
594 selected_item = history [history_ptr].link;
595 history_ptr--;
596 if (history_ptr < 0)
597 history_ptr = HISTORY_SIZE-1;
598
599 #ifndef HAVE_XVIEW
600 help_callback (h, 0, DLG_DRAW);
601 #endif
602 }
603
604 static int md_callback (Dlg_head *h, Widget *w, int msg, int par)
605 {
606 return default_proc (h, msg, par);
607 }
608
609 static Widget *mousedispatch_new (int y, int x, int yl, int xl)
610 {
611 Widget *w = xmalloc (sizeof (Widget), "disp_new");
612
613 init_widget (w, y, x, yl, xl,
614 (callback_fn) md_callback, 0, (mouse_h) help_event, NULL);
615
616 return w;
617 }
618
619 static int help_handle_key (struct Dlg_head *h, int c)
620 {
621 char *new_item;
622
623 if (c != KEY_UP && c != KEY_DOWN &&
624 check_movement_keys (c, 1, help_lines, currentpoint,
625 (movefn) move_backward2,
626 (movefn) move_forward2,
627 (movefn) move_to_top,
628 (movefn) move_to_bottom))
629 /* Nothing */;
630 else switch (c){
631 case 'l':
632 case KEY_LEFT:
633 prev_node_cmd (h);
634 break;
635
636 case '\n':
637 case KEY_RIGHT:
638 /* follow link */
639 if (!selected_item){
640 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
641 /* Is there any reason why the right key would take us
642 * backward if there are no links selected?, I agree
643 * with Torben than doing nothing in this case is better
644 */
645 /* If there are no links, go backward in history */
646 history_ptr--;
647 if (history_ptr < 0)
648 history_ptr = HISTORY_SIZE-1;
649
650 currentpoint = startpoint = history [history_ptr].page;
651 selected_item = history [history_ptr].link;
652 #endif
653 } else {
654 history_ptr = (history_ptr+1) % HISTORY_SIZE;
655 history [history_ptr].page = currentpoint;
656 history [history_ptr].link = selected_item;
657 currentpoint = startpoint = help_follow_link (currentpoint, selected_item) + 1;
658 }
659 selected_item = NULL;
660 break;
661
662 case KEY_DOWN:
663 case '\t':
664 /* select next link */
665 new_item = select_next_link (startpoint, selected_item);
666 if (new_item){
667 selected_item = new_item;
668 if (selected_item >= last_shown){
669 if (c == KEY_DOWN)
670 move_forward (1);
671 else
672 selected_item = NULL;
673 }
674 } else if (c == KEY_DOWN)
675 move_forward (1);
676 else
677 selected_item = NULL;
678 break;
679
680 case KEY_UP:
681 case ALT ('\t'):
682 /* select previous link */
683 new_item = select_prev_link (startpoint, selected_item);
684 selected_item = new_item;
685 if (selected_item < currentpoint || selected_item >= last_shown){
686 if (c == KEY_UP)
687 move_backward (1);
688 else{
689 if (link_area != NULL)
690 selected_item = link_area->link_name;
691 else
692 selected_item = NULL;
693 }
694 }
695 break;
696
697 case 'n':
698 /* Next node */
699 new_item = currentpoint;
700 while (*new_item && *new_item != CHAR_NODE_END)
701 new_item++;
702 if (*++new_item == '['){
703 while (*new_item != ']')
704 new_item++;
705 currentpoint = new_item + 2;
706 selected_item = NULL;
707 }
708 break;
709
710 case 'p':
711 /* Previous node */
712 new_item = currentpoint;
713 while (new_item > data + 1 && *new_item != CHAR_NODE_END)
714 new_item--;
715 new_item--;
716 while (new_item > data && *new_item != CHAR_NODE_END)
717 new_item--;
718 while (*new_item != ']')
719 new_item++;
720 currentpoint = new_item + 2;
721 selected_item = NULL;
722 break;
723
724 case 'c':
725 help_index_cmd (h);
726 break;
727
728 case ESC_CHAR:
729 case XCTRL('g'):
730 dlg_stop (h);
731 break;
732
733 default:
734 return 0;
735
736 }
737 help_callback (h, 0, DLG_DRAW);
738 return 1;
739 }
740
741 static int help_callback (struct Dlg_head *h, int id, int msg)
742 {
743 switch (msg){
744 case DLG_DRAW:
745 attrset (HELP_NORMAL_COLOR);
746 dlg_erase (h);
747 draw_box (h, 1, 1, help_lines+2, HELP_WINDOW_WIDTH+2);
748 attrset (COLOR_HOT_NORMAL);
749 dlg_move (h, 1, (HELP_WINDOW_WIDTH - 1) / 2);
750 addstr (_(" Help "));
751 attrset (HELP_NORMAL_COLOR);
752 show (h, currentpoint);
753 break;
754
755 case DLG_KEY:
756 return help_handle_key (h, id);
757 }
758 return 0;
759 }
760
761 void interactive_display_finish (void)
762 {
763 clear_link_areas ();
764 free (data);
765 }
766
767 void interactive_display (char *filename, char *node)
768 {
769 WButtonBar *help_bar;
770 Widget *md;
771
772 if ((data = load_file (filename)) == 0){
773 message (1, MSG_ERROR, _(" Can't open file %s \n %s "),
774 filename, unix_error_string (errno));
775 return;
776 }
777 if (!(main = search_string (data, node))){
778 message (1, MSG_ERROR, _(" Can't find node %s in help file "), node);
779 interactive_display_finish ();
780 return;
781 }
782
783 #ifndef HAVE_X
784 if (help_lines > LINES - 4)
785 help_lines = LINES - 4;
786
787 whelp = create_dlg (0, 0, help_lines+4, HELP_WINDOW_WIDTH+4, dialog_colors,
788 help_callback, "[Help]", "help", DLG_TRYUP|DLG_CENTER);
789
790 /* allow us to process the tab key */
791 whelp->raw = 1;
792
793 #endif
794 selected_item = search_string_node (main, STRING_LINK_START) - 1;
795 currentpoint = startpoint = main + 1;
796
797 for (history_ptr = HISTORY_SIZE; history_ptr;){
798 history_ptr--;
799 history [history_ptr].page = currentpoint;
800 history [history_ptr].link = selected_item;
801 }
802
803 #ifndef HAVE_X
804 help_bar = buttonbar_new (keybar_visible);
805 help_bar->widget.y -= whelp->y;
806 help_bar->widget.x -= whelp->x;
807
808 md = mousedispatch_new (1, 1, help_lines, HELP_WINDOW_WIDTH-2);
809
810 add_widget (whelp, help_bar);
811 add_widget (whelp, md);
812
813 define_label_data (whelp, (Widget *)NULL, 1, _("Help"),
814 (buttonbarfn) help_help_cmd, whelp);
815 define_label_data (whelp, (Widget *)NULL, 2, _("Index"),
816 (buttonbarfn) help_index_cmd,whelp);
817 define_label_data (whelp, (Widget *)NULL, 3, _("Prev"),
818 (buttonbarfn) prev_node_cmd, whelp);
819 define_label (whelp, (Widget *) NULL, 4, "", 0);
820 define_label (whelp, (Widget *) NULL, 5, "", 0);
821 define_label (whelp, (Widget *) NULL, 6, "", 0);
822 define_label (whelp, (Widget *) NULL, 7, "", 0);
823 define_label (whelp, (Widget *) NULL, 8, "", 0);
824 define_label (whelp, (Widget *) NULL, 9, "", 0);
825 define_label_data (whelp, (Widget *) NULL, 10, _("Quit"), quit_cmd, whelp);
826
827 run_dlg (whelp);
828 interactive_display_finish ();
829 destroy_dlg (whelp);
830 #else
831 x_interactive_display ();
832 #endif
833 }
834