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 */
56 #define MAXLINKNAME 80
57 #define HISTORY_SIZE 20
58 #define HELP_WINDOW_WIDTH 62
62 static char *data
; /* Pointer to the loaded data file */
63 static int help_lines
= 18; /* Lines in help viewer */
64 static int history_ptr
; /* For the history queue */
65 static char *main_node
; /* The main node */
66 static char *last_shown
= 0; /* Last byte shown in a screen */
67 static int end_of_node
= 0; /* Flag: the last character of the node shown? */
68 static char *currentpoint
, *startpoint
;
69 static char *selected_item
;
71 /* The widget variables */
72 static Dlg_head
*whelp
;
75 char *page
; /* Pointer to the selected page */
76 char *link
; /* Pointer to the selected link */
77 } history
[HISTORY_SIZE
];
79 /* Link areas for the mouse */
80 typedef struct Link_Area
{
83 struct Link_Area
*next
;
86 static Link_Area
*link_area
= NULL
;
87 static int inside_link_area
= 0;
89 static int help_callback (struct Dlg_head
*h
, int id
, int msg
);
91 #ifdef HAS_ACS_AS_PCCHARS
110 static int acs2pc (int acscode
)
114 for (i
= 0; acs2pc_table
[i
].acscode
!= 0; i
++)
115 if (acscode
== acs2pc_table
[i
].acscode
) {
116 return acs2pc_table
[i
].pccode
;
122 /* returns the position where text was found in the start buffer */
123 /* or 0 if not found */
125 search_string (char *start
, char *text
)
130 /* fmt sometimes replaces a space with a newline in the help file */
131 /* Replace the newlines in the link name with spaces to correct the situation */
138 for (d
= text
; *e
; e
++){
149 /* Searches text in the buffer pointed by start. Search ends */
150 /* if the CHAR_NODE_END is found in the text. Returns 0 on failure */
151 static char *search_string_node (char *start
, char *text
)
159 for (; *e
&& *e
!= CHAR_NODE_END
; e
++){
170 /* Searches the_char in the buffer pointer by start and searches */
171 /* it can search forward (direction = 1) or backward (direction = -1) */
172 static char *search_char_node (char *start
, char the_char
, int direction
)
178 for (; *e
&& (*e
!= CHAR_NODE_END
); e
+= direction
){
185 /* Returns the new current pointer when moved lines lines */
186 static char *move_forward2 (char *c
, int lines
)
192 for (line
= 0, p
= currentpoint
; *p
&& *p
!= CHAR_NODE_END
; p
++){
194 return currentpoint
= p
;
198 return currentpoint
= c
;
201 static char *move_backward2 (char *c
, int lines
)
207 for (line
= 0, p
= currentpoint
; *p
&& p
>= data
; p
--){
208 if (*p
== CHAR_NODE_END
)
210 /* We reached the beginning of the node */
211 /* Skip the node headers */
212 while (*p
!= ']') p
++;
213 return currentpoint
= p
+ 2;
215 if (*(p
- 1) == '\n')
218 return currentpoint
= p
;
220 return currentpoint
= c
;
223 static void move_forward (int i
)
227 currentpoint
= move_forward2 (currentpoint
, i
);
230 static void move_backward (int i
)
232 currentpoint
= move_backward2 (currentpoint
, ++i
);
235 static void move_to_top (int dummy
)
237 while (currentpoint
> data
&& *currentpoint
!= CHAR_NODE_END
)
239 while (*currentpoint
!= ']')
241 currentpoint
= currentpoint
+ 1;
242 selected_item
= NULL
;
245 static void move_to_bottom (int dummy
)
247 while (*currentpoint
&& *currentpoint
!= CHAR_NODE_END
)
250 move_backward (help_lines
- 1);
253 static char *help_follow_link (char *start
, char *selected_item
)
255 char link_name
[MAXLINKNAME
];
262 for (p
= selected_item
; *p
&& *p
!= CHAR_NODE_END
&& *p
!= CHAR_LINK_POINTER
; p
++)
264 if (*p
== CHAR_LINK_POINTER
){
266 for (i
= 1; *p
!= CHAR_LINK_END
&& *p
&& *p
!= CHAR_NODE_END
&& i
< MAXLINKNAME
-3; )
267 link_name
[i
++] = *++p
;
268 link_name
[i
-1] = ']';
270 p
= search_string (data
, link_name
);
274 return _(" Help file format error\n\x4"); /* */
277 static char *select_next_link (char *start
, char *current_link
)
284 p
= search_string_node (current_link
, STRING_LINK_END
);
287 p
= search_string_node (p
, STRING_LINK_START
);
293 static char *select_prev_link (char *start
, char *current_link
)
300 p
= current_link
- 1;
304 p
= search_char_node (p
, CHAR_LINK_START
, -1);
308 static void start_link_area (int x
, int y
, char *link_name
)
312 if (inside_link_area
)
313 message (0, _(" Warning "), _(" Internal bug: Double start of link area "));
315 /* Allocate memory for a new link area */
316 new = g_new (Link_Area
, 1);
317 new->next
= link_area
;
320 /* Save the beginning coordinates of the link area */
324 /* Save the name of the destination anchor */
325 link_area
->link_name
= link_name
;
327 inside_link_area
= 1;
330 static void end_link_area (int x
, int y
)
332 if (inside_link_area
){
333 /* Save the end coordinates of the link area */
337 inside_link_area
= 0;
341 static void clear_link_areas (void)
347 link_area
= current
-> next
;
350 inside_link_area
= 0;
353 static void show (Dlg_head
*h
, char *paint_start
)
358 int acs
; /* Flag: Alternate character set active? */
360 int active_col
, active_line
;/* Active link position */
364 line
= col
= acs
= active_col
= active_line
= repeat_paint
= 0;
367 if (selected_item
< paint_start
)
368 selected_item
= NULL
;
370 for (p
= paint_start
; *p
!= CHAR_NODE_END
&& line
< help_lines
; p
++){
371 c
= (unsigned char)*p
;
373 case CHAR_LINK_START
:
374 if (selected_item
== NULL
)
376 if (p
== selected_item
){
377 attrset (HELP_SLINK_COLOR
);
379 /* Store the coordinates of the link */
380 active_col
= col
+ 2;
381 active_line
= line
+ 2;
384 attrset (HELP_LINK_COLOR
);
385 start_link_area (col
, line
, p
);
387 case CHAR_LINK_POINTER
:
389 end_link_area (col
- 1, line
);
393 attrset (HELP_NORMAL_COLOR
);
402 dlg_move (h
, line
+2, col
+2);
404 col
+= strlen (VERSION
);
407 attrset (HELP_BOLD_COLOR
);
410 attrset (HELP_ITALIC_COLOR
);
413 attrset (HELP_NORMAL_COLOR
);
420 col
= (col
/8 + 1) * 8;
423 case CHAR_TEXTONLY_START
:
424 case CHAR_TEXTONLY_END
:
426 case CHAR_XONLY_START
:
427 while (*p
&& *p
!= CHAR_NODE_END
&& *p
!= CHAR_XONLY_END
)
429 if (*p
== CHAR_NODE_END
|| !*p
)
435 if (col
> HELP_WINDOW_WIDTH
-1)
438 dlg_move (h
, line
+2, col
+2);
440 if (c
== ' ' || c
== '.')
447 SLsmg_draw_object (h
->y
+ line
+ 2, h
->x
+ col
+ 2, c
);
459 end_of_node
= line
< help_lines
;
460 attrset (HELP_NORMAL_COLOR
);
461 if (selected_item
>= last_shown
){
462 if (link_area
!= NULL
){
463 selected_item
= link_area
->link_name
;
467 selected_item
= NULL
;
469 } while (repeat_paint
);
471 /* Position the cursor over a nice link */
473 dlg_move (h
, active_line
, active_col
);
476 static int help_event (Gpm_Event
*event
, Widget
*w
)
478 Link_Area
*current_area
;
480 if (! (event
->type
& GPM_UP
))
483 /* The event is relative to the dialog window, adjust it: */
486 if (event
->buttons
& GPM_B_RIGHT
){
487 currentpoint
= startpoint
= history
[history_ptr
].page
;
488 selected_item
= history
[history_ptr
].link
;
491 history_ptr
= HISTORY_SIZE
-1;
493 help_callback (w
->parent
, 0, DLG_DRAW
);
497 /* Test whether the mouse click is inside one of the link areas */
498 current_area
= link_area
;
501 /* Test one line link area */
502 if (event
->y
== current_area
->y1
&& event
->x
>= current_area
->x1
&&
503 event
->y
== current_area
->y2
&& event
->x
<= current_area
->x2
)
505 /* Test two line link area */
506 if (current_area
->y1
+ 1 == current_area
->y2
){
508 if (event
->y
== current_area
->y1
&& event
->x
>= current_area
->x1
)
510 /* The second line */
511 if (event
->y
== current_area
->y2
&& event
->x
<= current_area
->x2
)
514 /* Mouse will not work with link areas of more than two lines */
516 current_area
= current_area
-> next
;
519 /* Test whether a link area was found */
521 /* The click was inside a link area -> follow the link */
522 history_ptr
= (history_ptr
+1) % HISTORY_SIZE
;
523 history
[history_ptr
].page
= currentpoint
;
524 history
[history_ptr
].link
= current_area
->link_name
;
525 currentpoint
= startpoint
= help_follow_link (currentpoint
, current_area
->link_name
);
526 selected_item
= NULL
;
529 move_backward (help_lines
- 1);
530 else if (event
->y
>= help_lines
)
531 move_forward (help_lines
- 1);
532 else if (event
->y
< help_lines
/2)
538 /* Show the new node */
539 help_callback (w
->parent
, 0, DLG_DRAW
);
546 help_help_cmd (Dlg_head
*h
)
548 history_ptr
= (history_ptr
+1) % HISTORY_SIZE
;
549 history
[history_ptr
].page
= currentpoint
;
550 history
[history_ptr
].link
= selected_item
;
551 currentpoint
= startpoint
= search_string (data
, "[How to use help]") + 1;
552 selected_item
= NULL
;
553 help_callback (h
, 0, DLG_DRAW
);
557 help_index_cmd (Dlg_head
*h
)
561 history_ptr
= (history_ptr
+1) % HISTORY_SIZE
;
562 history
[history_ptr
].page
= currentpoint
;
563 history
[history_ptr
].link
= selected_item
;
564 currentpoint
= startpoint
= search_string (data
, "[Help]") + 1;
566 if (!(new_item
= search_string (data
, "[Contents]")))
567 message (1, MSG_ERROR
, _(" Cannot find node [Contents] in help file "));
569 currentpoint
= startpoint
= new_item
+ 1;
570 selected_item
= NULL
;
571 help_callback (h
, 0, DLG_DRAW
);
574 static void quit_cmd (void *x
)
576 dlg_stop ((Dlg_head
*)x
);
579 static void prev_node_cmd (Dlg_head
*h
)
581 currentpoint
= startpoint
= history
[history_ptr
].page
;
582 selected_item
= history
[history_ptr
].link
;
585 history_ptr
= HISTORY_SIZE
-1;
587 help_callback (h
, 0, DLG_DRAW
);
590 static int md_callback (Dlg_head
*h
, Widget
*w
, int msg
, int par
)
592 return default_proc (h
, msg
, par
);
595 static Widget
*mousedispatch_new (int y
, int x
, int yl
, int xl
)
597 Widget
*w
= g_new (Widget
, 1);
599 init_widget (w
, y
, x
, yl
, xl
,
600 (callback_fn
) md_callback
, 0, (mouse_h
) help_event
, NULL
);
605 static int help_handle_key (struct Dlg_head
*h
, int c
)
609 if (c
!= KEY_UP
&& c
!= KEY_DOWN
&&
610 check_movement_keys (c
, 1, help_lines
, currentpoint
,
611 (movefn
) move_backward2
,
612 (movefn
) move_forward2
,
613 (movefn
) move_to_top
,
614 (movefn
) move_to_bottom
))
626 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
627 /* Is there any reason why the right key would take us
628 * backward if there are no links selected?, I agree
629 * with Torben than doing nothing in this case is better
631 /* If there are no links, go backward in history */
634 history_ptr
= HISTORY_SIZE
-1;
636 currentpoint
= startpoint
= history
[history_ptr
].page
;
637 selected_item
= history
[history_ptr
].link
;
640 history_ptr
= (history_ptr
+1) % HISTORY_SIZE
;
641 history
[history_ptr
].page
= currentpoint
;
642 history
[history_ptr
].link
= selected_item
;
643 currentpoint
= startpoint
= help_follow_link (currentpoint
, selected_item
) + 1;
645 selected_item
= NULL
;
650 /* select next link */
651 new_item
= select_next_link (startpoint
, selected_item
);
653 selected_item
= new_item
;
654 if (selected_item
>= last_shown
){
658 selected_item
= NULL
;
660 } else if (c
== KEY_DOWN
)
663 selected_item
= NULL
;
668 /* select previous link */
669 new_item
= select_prev_link (startpoint
, selected_item
);
670 selected_item
= new_item
;
671 if (selected_item
< currentpoint
|| selected_item
>= last_shown
){
675 if (link_area
!= NULL
)
676 selected_item
= link_area
->link_name
;
678 selected_item
= NULL
;
685 new_item
= currentpoint
;
686 while (*new_item
&& *new_item
!= CHAR_NODE_END
)
688 if (*++new_item
== '['){
689 while (*new_item
!= ']')
691 currentpoint
= new_item
+ 2;
692 selected_item
= NULL
;
698 new_item
= currentpoint
;
699 while (new_item
> data
+ 1 && *new_item
!= CHAR_NODE_END
)
702 while (new_item
> data
&& *new_item
!= CHAR_NODE_END
)
704 while (*new_item
!= ']')
706 currentpoint
= new_item
+ 2;
707 selected_item
= NULL
;
723 help_callback (h
, 0, DLG_DRAW
);
727 static int help_callback (struct Dlg_head
*h
, int id
, int msg
)
731 attrset (HELP_NORMAL_COLOR
);
733 draw_box (h
, 1, 1, help_lines
+2, HELP_WINDOW_WIDTH
+2);
734 attrset (COLOR_HOT_NORMAL
);
735 dlg_move (h
, 1, (HELP_WINDOW_WIDTH
- 1) / 2);
736 addstr (_(" Help "));
737 attrset (HELP_NORMAL_COLOR
);
738 show (h
, currentpoint
);
742 return help_handle_key (h
, id
);
748 interactive_display_finish (void)
755 interactive_display (char *filename
, char *node
)
757 WButtonBar
*help_bar
;
759 char *hlpfile
= filename
;
762 data
= load_file (filename
);
764 data
= load_mc_home_file ("mc.hlp", &hlpfile
);
767 message (1, MSG_ERROR
, _(" Cannot open file %s \n %s "),
768 hlpfile
, unix_error_string (errno
));
777 if (!(main_node
= search_string (data
, node
))){
778 message (1, MSG_ERROR
, _(" Cannot find node %s in help file "), node
);
779 interactive_display_finish ();
783 if (help_lines
> LINES
- 4)
784 help_lines
= LINES
- 4;
786 whelp
= create_dlg (0, 0, help_lines
+4, HELP_WINDOW_WIDTH
+4, dialog_colors
,
787 help_callback
, "[Help]", "help",
788 DLG_TRYUP
| DLG_CENTER
| DLG_WANT_TAB
);
790 selected_item
= search_string_node (main_node
, STRING_LINK_START
) - 1;
791 currentpoint
= startpoint
= main_node
+ 1;
793 for (history_ptr
= HISTORY_SIZE
; history_ptr
;){
795 history
[history_ptr
].page
= currentpoint
;
796 history
[history_ptr
].link
= selected_item
;
799 help_bar
= buttonbar_new (1);
800 help_bar
->widget
.y
-= whelp
->y
;
801 help_bar
->widget
.x
-= whelp
->x
;
803 md
= mousedispatch_new (1, 1, help_lines
, HELP_WINDOW_WIDTH
-2);
805 add_widget (whelp
, help_bar
);
806 add_widget (whelp
, md
);
808 define_label_data (whelp
, (Widget
*)NULL
, 1, _("Help"),
809 (buttonbarfn
) help_help_cmd
, whelp
);
810 define_label_data (whelp
, (Widget
*)NULL
, 2, _("Index"),
811 (buttonbarfn
) help_index_cmd
,whelp
);
812 define_label_data (whelp
, (Widget
*)NULL
, 3, _("Prev"),
813 (buttonbarfn
) prev_node_cmd
, whelp
);
814 define_label (whelp
, (Widget
*) NULL
, 4, "", 0);
815 define_label (whelp
, (Widget
*) NULL
, 5, "", 0);
816 define_label (whelp
, (Widget
*) NULL
, 6, "", 0);
817 define_label (whelp
, (Widget
*) NULL
, 7, "", 0);
818 define_label (whelp
, (Widget
*) NULL
, 8, "", 0);
819 define_label (whelp
, (Widget
*) NULL
, 9, "", 0);
820 define_label_data (whelp
, (Widget
*) NULL
, 10, _("Quit"), quit_cmd
, whelp
);
823 interactive_display_finish ();