1 /* Hypertext file browser.
2 Copyright (C) 1994, 1995 Miguel de Icaza.
3 Copyright (C) 1994, 1995 Janne Kukonlehto
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
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.
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.
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.
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.
41 #include <sys/types.h>
51 #include "key.h" /* For mi_getch() */
53 #include "dlg.h" /* For Dlg_head */
54 #include "widget.h" /* For Widget */
55 #include "wtools.h" /* For common_dialog_repaint() */
57 #define MAXLINKNAME 80
58 #define HISTORY_SIZE 20
59 #define HELP_WINDOW_WIDTH 62
63 static char *data
; /* Pointer to the loaded data file */
64 static int help_lines
; /* Lines in help viewer */
65 static int history_ptr
; /* For the history queue */
66 static char *main_node
; /* The main node */
67 static char *last_shown
= 0; /* Last byte shown in a screen */
68 static int end_of_node
= 0; /* Flag: the last character of the node shown? */
69 static char *currentpoint
, *startpoint
;
70 static char *selected_item
;
72 /* The widget variables */
73 static Dlg_head
*whelp
;
76 char *page
; /* Pointer to the selected page */
77 char *link
; /* Pointer to the selected link */
78 } history
[HISTORY_SIZE
];
80 /* Link areas for the mouse */
81 typedef struct Link_Area
{
84 struct Link_Area
*next
;
87 static Link_Area
*link_area
= NULL
;
88 static int inside_link_area
= 0;
90 static int help_callback (struct Dlg_head
*h
, int id
, int msg
);
92 #ifdef HAS_ACS_AS_PCCHARS
111 static int acs2pc (int acscode
)
115 for (i
= 0; acs2pc_table
[i
].acscode
!= 0; i
++)
116 if (acscode
== acs2pc_table
[i
].acscode
) {
117 return acs2pc_table
[i
].pccode
;
123 /* returns the position where text was found in the start buffer */
124 /* or 0 if not found */
126 search_string (char *start
, char *text
)
131 /* fmt sometimes replaces a space with a newline in the help file */
132 /* Replace the newlines in the link name with spaces to correct the situation */
139 for (d
= text
; *e
; e
++){
150 /* Searches text in the buffer pointed by start. Search ends */
151 /* if the CHAR_NODE_END is found in the text. Returns 0 on failure */
152 static char *search_string_node (char *start
, char *text
)
160 for (; *e
&& *e
!= CHAR_NODE_END
; e
++){
171 /* Searches the_char in the buffer pointer by start and searches */
172 /* it can search forward (direction = 1) or backward (direction = -1) */
173 static char *search_char_node (char *start
, char the_char
, int direction
)
179 for (; *e
&& (*e
!= CHAR_NODE_END
); e
+= direction
){
186 /* Returns the new current pointer when moved lines lines */
187 static char *move_forward2 (char *c
, int lines
)
193 for (line
= 0, p
= currentpoint
; *p
&& *p
!= CHAR_NODE_END
; p
++){
195 return currentpoint
= p
;
199 return currentpoint
= c
;
202 static char *move_backward2 (char *c
, int lines
)
208 for (line
= 0, p
= currentpoint
; *p
&& p
>= data
; p
--){
209 if (*p
== CHAR_NODE_END
)
211 /* We reached the beginning of the node */
212 /* Skip the node headers */
213 while (*p
!= ']') p
++;
214 return currentpoint
= p
+ 2;
216 if (*(p
- 1) == '\n')
219 return currentpoint
= p
;
221 return currentpoint
= c
;
224 static void move_forward (int i
)
228 currentpoint
= move_forward2 (currentpoint
, i
);
231 static void move_backward (int i
)
233 currentpoint
= move_backward2 (currentpoint
, ++i
);
236 static void move_to_top (int dummy
)
238 while (currentpoint
> data
&& *currentpoint
!= CHAR_NODE_END
)
240 while (*currentpoint
!= ']')
242 currentpoint
= currentpoint
+ 1;
243 selected_item
= NULL
;
246 static void move_to_bottom (int dummy
)
248 while (*currentpoint
&& *currentpoint
!= CHAR_NODE_END
)
251 move_backward (help_lines
- 1);
254 static char *help_follow_link (char *start
, char *selected_item
)
256 char link_name
[MAXLINKNAME
];
263 for (p
= selected_item
; *p
&& *p
!= CHAR_NODE_END
&& *p
!= CHAR_LINK_POINTER
; p
++)
265 if (*p
== CHAR_LINK_POINTER
){
267 for (i
= 1; *p
!= CHAR_LINK_END
&& *p
&& *p
!= CHAR_NODE_END
&& i
< MAXLINKNAME
-3; )
268 link_name
[i
++] = *++p
;
269 link_name
[i
-1] = ']';
271 p
= search_string (data
, link_name
);
275 return _(" Help file format error\n\x4"); /* */
278 static char *select_next_link (char *start
, char *current_link
)
285 p
= search_string_node (current_link
, STRING_LINK_END
);
288 p
= search_string_node (p
, STRING_LINK_START
);
294 static char *select_prev_link (char *start
, char *current_link
)
301 p
= current_link
- 1;
305 p
= search_char_node (p
, CHAR_LINK_START
, -1);
309 static void start_link_area (int x
, int y
, char *link_name
)
313 if (inside_link_area
)
314 message (0, _(" Warning "), _(" Internal bug: Double start of link area "));
316 /* Allocate memory for a new link area */
317 new = g_new (Link_Area
, 1);
318 new->next
= link_area
;
321 /* Save the beginning coordinates of the link area */
325 /* Save the name of the destination anchor */
326 link_area
->link_name
= link_name
;
328 inside_link_area
= 1;
331 static void end_link_area (int x
, int y
)
333 if (inside_link_area
){
334 /* Save the end coordinates of the link area */
338 inside_link_area
= 0;
342 static void clear_link_areas (void)
348 link_area
= current
-> next
;
351 inside_link_area
= 0;
354 static void show (Dlg_head
*h
, char *paint_start
)
359 int acs
; /* Flag: Alternate character set active? */
361 int active_col
, active_line
;/* Active link position */
363 attrset (HELP_NORMAL_COLOR
);
366 line
= col
= acs
= active_col
= active_line
= repeat_paint
= 0;
369 if (selected_item
< paint_start
)
370 selected_item
= NULL
;
372 for (p
= paint_start
; *p
&& *p
!= CHAR_NODE_END
&& line
< help_lines
; p
++) {
373 c
= (unsigned char)*p
;
375 case CHAR_LINK_START
:
376 if (selected_item
== NULL
)
378 if (p
== selected_item
){
379 attrset (HELP_SLINK_COLOR
);
381 /* Store the coordinates of the link */
382 active_col
= col
+ 2;
383 active_line
= line
+ 2;
386 attrset (HELP_LINK_COLOR
);
387 start_link_area (col
, line
, p
);
389 case CHAR_LINK_POINTER
:
391 end_link_area (col
- 1, line
);
395 attrset (HELP_NORMAL_COLOR
);
404 dlg_move (h
, line
+2, col
+2);
406 col
+= strlen (VERSION
);
409 attrset (HELP_BOLD_COLOR
);
412 attrset (HELP_ITALIC_COLOR
);
415 attrset (HELP_NORMAL_COLOR
);
422 col
= (col
/8 + 1) * 8;
425 case CHAR_TEXTONLY_START
:
426 case CHAR_TEXTONLY_END
:
428 case CHAR_XONLY_START
:
429 while (*p
&& *p
!= CHAR_NODE_END
&& *p
!= CHAR_XONLY_END
)
431 if (*p
== CHAR_NODE_END
|| !*p
)
437 if (col
> HELP_WINDOW_WIDTH
-1)
440 dlg_move (h
, line
+2, col
+2);
442 if (c
== ' ' || c
== '.')
449 SLsmg_draw_object (h
->y
+ line
+ 2, h
->x
+ col
+ 2, c
);
453 #endif /* NATIVE_WIN32 */
461 end_of_node
= line
< help_lines
;
462 attrset (HELP_NORMAL_COLOR
);
463 if (selected_item
>= last_shown
){
464 if (link_area
!= NULL
){
465 selected_item
= link_area
->link_name
;
469 selected_item
= NULL
;
471 } while (repeat_paint
);
473 /* Position the cursor over a nice link */
475 dlg_move (h
, active_line
, active_col
);
478 static int help_event (Gpm_Event
*event
, Widget
*w
)
480 Link_Area
*current_area
;
482 if (! (event
->type
& GPM_UP
))
485 /* The event is relative to the dialog window, adjust it: */
488 if (event
->buttons
& GPM_B_RIGHT
){
489 currentpoint
= startpoint
= history
[history_ptr
].page
;
490 selected_item
= history
[history_ptr
].link
;
493 history_ptr
= HISTORY_SIZE
-1;
495 help_callback (w
->parent
, 0, DLG_DRAW
);
499 /* Test whether the mouse click is inside one of the link areas */
500 current_area
= link_area
;
503 /* Test one line link area */
504 if (event
->y
== current_area
->y1
&& event
->x
>= current_area
->x1
&&
505 event
->y
== current_area
->y2
&& event
->x
<= current_area
->x2
)
507 /* Test two line link area */
508 if (current_area
->y1
+ 1 == current_area
->y2
){
510 if (event
->y
== current_area
->y1
&& event
->x
>= current_area
->x1
)
512 /* The second line */
513 if (event
->y
== current_area
->y2
&& event
->x
<= current_area
->x2
)
516 /* Mouse will not work with link areas of more than two lines */
518 current_area
= current_area
-> next
;
521 /* Test whether a link area was found */
523 /* The click was inside a link area -> follow the link */
524 history_ptr
= (history_ptr
+1) % HISTORY_SIZE
;
525 history
[history_ptr
].page
= currentpoint
;
526 history
[history_ptr
].link
= current_area
->link_name
;
527 currentpoint
= startpoint
= help_follow_link (currentpoint
, current_area
->link_name
);
528 selected_item
= NULL
;
531 move_backward (help_lines
- 1);
532 else if (event
->y
>= help_lines
)
533 move_forward (help_lines
- 1);
534 else if (event
->y
< help_lines
/2)
540 /* Show the new node */
541 help_callback (w
->parent
, 0, DLG_DRAW
);
548 help_help_cmd (Dlg_head
*h
)
550 history_ptr
= (history_ptr
+1) % HISTORY_SIZE
;
551 history
[history_ptr
].page
= currentpoint
;
552 history
[history_ptr
].link
= selected_item
;
553 currentpoint
= startpoint
= search_string (data
, "[How to use help]") + 1;
554 selected_item
= NULL
;
555 help_callback (h
, 0, DLG_DRAW
);
559 help_index_cmd (Dlg_head
* h
)
563 if (!(new_item
= search_string (data
, "[Contents]"))) {
564 message (1, MSG_ERROR
, _(" Cannot find node %s in help file "),
569 history_ptr
= (history_ptr
+ 1) % HISTORY_SIZE
;
570 history
[history_ptr
].page
= currentpoint
;
571 history
[history_ptr
].link
= selected_item
;
573 currentpoint
= startpoint
= new_item
+ 1;
574 selected_item
= NULL
;
575 help_callback (h
, 0, DLG_DRAW
);
578 static void quit_cmd (void *x
)
580 dlg_stop ((Dlg_head
*)x
);
583 static void prev_node_cmd (Dlg_head
*h
)
585 currentpoint
= startpoint
= history
[history_ptr
].page
;
586 selected_item
= history
[history_ptr
].link
;
589 history_ptr
= HISTORY_SIZE
-1;
591 help_callback (h
, 0, DLG_DRAW
);
594 static int md_callback (Dlg_head
*h
, Widget
*w
, int msg
, int par
)
596 return default_proc (h
, msg
, par
);
599 static Widget
*mousedispatch_new (int y
, int x
, int yl
, int xl
)
601 Widget
*w
= g_new (Widget
, 1);
603 init_widget (w
, y
, x
, yl
, xl
,
604 (callback_fn
) md_callback
, 0, (mouse_h
) help_event
, NULL
);
609 static int help_handle_key (struct Dlg_head
*h
, int c
)
613 if (c
!= KEY_UP
&& c
!= KEY_DOWN
&&
614 check_movement_keys (c
, 1, help_lines
, currentpoint
,
615 (movefn
) move_backward2
,
616 (movefn
) move_forward2
,
617 (movefn
) move_to_top
,
618 (movefn
) move_to_bottom
))
630 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
631 /* Is there any reason why the right key would take us
632 * backward if there are no links selected?, I agree
633 * with Torben than doing nothing in this case is better
635 /* If there are no links, go backward in history */
638 history_ptr
= HISTORY_SIZE
-1;
640 currentpoint
= startpoint
= history
[history_ptr
].page
;
641 selected_item
= history
[history_ptr
].link
;
644 history_ptr
= (history_ptr
+1) % HISTORY_SIZE
;
645 history
[history_ptr
].page
= currentpoint
;
646 history
[history_ptr
].link
= selected_item
;
647 currentpoint
= startpoint
= help_follow_link (currentpoint
, selected_item
) + 1;
649 selected_item
= NULL
;
654 /* select next link */
655 new_item
= select_next_link (startpoint
, selected_item
);
657 selected_item
= new_item
;
658 if (selected_item
>= last_shown
){
662 selected_item
= NULL
;
664 } else if (c
== KEY_DOWN
)
667 selected_item
= NULL
;
672 /* select previous link */
673 new_item
= select_prev_link (startpoint
, selected_item
);
674 selected_item
= new_item
;
675 if (selected_item
< currentpoint
|| selected_item
>= last_shown
){
679 if (link_area
!= NULL
)
680 selected_item
= link_area
->link_name
;
682 selected_item
= NULL
;
689 new_item
= currentpoint
;
690 while (*new_item
&& *new_item
!= CHAR_NODE_END
)
692 if (*++new_item
== '['){
693 while (*++new_item
) {
694 if (*new_item
== ']' && *++new_item
&& *++new_item
) {
695 currentpoint
= new_item
;
696 selected_item
= NULL
;
705 new_item
= currentpoint
;
706 while (new_item
> data
+ 1 && *new_item
!= CHAR_NODE_END
)
709 while (new_item
> data
&& *new_item
!= CHAR_NODE_END
)
711 while (*new_item
!= ']')
713 currentpoint
= new_item
+ 2;
714 selected_item
= NULL
;
730 help_callback (h
, 0, DLG_DRAW
);
734 static int help_callback (struct Dlg_head
*h
, int id
, int msg
)
738 common_dialog_repaint (h
);
739 show (h
, currentpoint
);
743 return help_handle_key (h
, id
);
749 interactive_display_finish (void)
756 interactive_display (char *filename
, char *node
)
758 WButtonBar
*help_bar
;
760 char *hlpfile
= filename
;
763 data
= load_file (filename
);
765 data
= load_mc_home_file ("mc.hlp", &hlpfile
);
768 message (1, MSG_ERROR
, _(" Cannot open file %s \n %s "), hlpfile
,
769 unix_error_string (errno
));
781 if (!(main_node
= search_string (data
, node
))) {
782 message (1, MSG_ERROR
, _(" Cannot find node %s in help file "),
785 /* Fallback to [main], return if it also cannot be found */
786 main_node
= search_string (data
, "[main]");
788 interactive_display_finish ();
793 help_lines
= min (LINES
- 4, max (2 * LINES
/ 3, 18));
796 create_dlg (0, 0, help_lines
+ 4, HELP_WINDOW_WIDTH
+ 4,
797 dialog_colors
, help_callback
, "[Help]", _("Help"),
798 DLG_TRYUP
| DLG_CENTER
| DLG_WANT_TAB
);
800 selected_item
= search_string_node (main_node
, STRING_LINK_START
) - 1;
801 currentpoint
= startpoint
= main_node
+ 1;
803 for (history_ptr
= HISTORY_SIZE
; history_ptr
;) {
805 history
[history_ptr
].page
= currentpoint
;
806 history
[history_ptr
].link
= selected_item
;
809 help_bar
= buttonbar_new (1);
810 help_bar
->widget
.y
-= whelp
->y
;
811 help_bar
->widget
.x
-= whelp
->x
;
813 md
= mousedispatch_new (1, 1, help_lines
, HELP_WINDOW_WIDTH
- 2);
815 add_widget (whelp
, help_bar
);
816 add_widget (whelp
, md
);
818 define_label_data (whelp
, (Widget
*) NULL
, 1, _("Help"),
819 (buttonbarfn
) help_help_cmd
, whelp
);
820 define_label_data (whelp
, (Widget
*) NULL
, 2, _("Index"),
821 (buttonbarfn
) help_index_cmd
, whelp
);
822 define_label_data (whelp
, (Widget
*) NULL
, 3, _("Prev"),
823 (buttonbarfn
) prev_node_cmd
, whelp
);
824 define_label (whelp
, (Widget
*) NULL
, 4, "", 0);
825 define_label (whelp
, (Widget
*) NULL
, 5, "", 0);
826 define_label (whelp
, (Widget
*) NULL
, 6, "", 0);
827 define_label (whelp
, (Widget
*) NULL
, 7, "", 0);
828 define_label (whelp
, (Widget
*) NULL
, 8, "", 0);
829 define_label (whelp
, (Widget
*) NULL
, 9, "", 0);
830 define_label_data (whelp
, (Widget
*) NULL
, 10, _("Quit"), quit_cmd
,
834 interactive_display_finish ();