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., 675 Mass Ave, Cambridge, MA 02139, 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.
42 #include <sys/types.h>
54 #include "key.h" /* For mi_getch() */
56 #include "layout.h" /* keybar_visible */
58 #include "dlg.h" /* For Dlg_head */
59 #include "widget.h" /* For Widget */
65 #define MAXLINKNAME 80
66 #define HISTORY_SIZE 20
67 #define HELP_WINDOW_WIDTH 62
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
;
80 /* The widget variables */
81 static Dlg_head
*whelp
;
84 char *page
; /* Pointer to the selected page */
85 char *link
; /* Pointer to the selected link */
86 } history
[HISTORY_SIZE
];
88 /* Link areas for the mouse */
89 typedef struct Link_Area
{
92 struct Link_Area
*next
;
95 static Link_Area
*link_area
= NULL
;
96 static int inside_link_area
= 0;
98 static int help_callback (struct Dlg_head
*h
, int id
, int msg
);
104 } acs2pc_table
[] = {
119 static int acs2pc (int acscode
)
123 for (i
= 0; acs2pc_table
[i
].acscode
!= 0; i
++)
124 if (acscode
== acs2pc_table
[i
].acscode
) {
125 return acs2pc_table
[i
].pccode
;
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
)
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 */
146 for (d
= text
; *e
; e
++){
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
)
167 for (; *e
&& *e
!= CHAR_NODE_END
; e
++){
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
)
186 for (; *e
&& (*e
!= CHAR_NODE_END
); e
+= direction
){
193 /* Returns the new current pointer when moved lines lines */
194 static char *move_forward2 (char *c
, int lines
)
200 for (line
= 0, p
= currentpoint
; *p
&& *p
!= CHAR_NODE_END
; p
++){
202 return currentpoint
= p
;
206 return currentpoint
= c
;
209 static char *move_backward2 (char *c
, int lines
)
215 for (line
= 0, p
= currentpoint
; *p
&& p
>= data
; p
--){
216 if (*p
== CHAR_NODE_END
)
218 /* We reached the beginning of the node */
219 /* Skip the node headers */
220 while (*p
!= ']') p
++;
221 return currentpoint
= p
+ 2;
223 if (*(p
- 1) == '\n')
226 return currentpoint
= p
;
228 return currentpoint
= c
;
231 static void move_forward (int i
)
235 currentpoint
= move_forward2 (currentpoint
, i
);
238 static void move_backward (int i
)
240 currentpoint
= move_backward2 (currentpoint
, ++i
);
243 static void move_to_top (int dummy
)
245 while (currentpoint
> data
&& *currentpoint
!= CHAR_NODE_END
)
247 while (*currentpoint
!= ']')
249 currentpoint
= currentpoint
+ 1;
250 selected_item
= NULL
;
253 static void move_to_bottom (int dummy
)
255 while (*currentpoint
&& *currentpoint
!= CHAR_NODE_END
)
258 move_backward (help_lines
- 1);
261 char *help_follow_link (char *start
, char *selected_item
)
263 char link_name
[MAXLINKNAME
];
270 for (p
= selected_item
; *p
&& *p
!= CHAR_NODE_END
&& *p
!= CHAR_LINK_POINTER
; p
++)
272 if (*p
== CHAR_LINK_POINTER
){
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] = ']';
278 p
= search_string (data
, link_name
);
282 return _(" Help file format error\n\x4"); /* */
285 static char *select_next_link (char *start
, char *current_link
)
292 p
= search_string_node (current_link
, STRING_LINK_END
);
295 p
= search_string_node (p
, STRING_LINK_START
);
301 static char *select_prev_link (char *start
, char *current_link
)
308 p
= current_link
- 1;
312 p
= search_char_node (p
, CHAR_LINK_START
, -1);
316 static void start_link_area (int x
, int y
, char *link_name
)
320 if (inside_link_area
)
321 message (0, _(" Warning "), _(" Internal bug: Double start of link area "));
323 /* Allocate memory for a new link area */
324 new = (Link_Area
*) xmalloc (sizeof (Link_Area
), "Help, link_area");
325 new->next
= link_area
;
328 /* Save the beginning coordinates of the link area */
332 /* Save the name of the destination anchor */
333 link_area
->link_name
= link_name
;
335 inside_link_area
= 1;
338 static void end_link_area (int x
, int y
)
340 if (inside_link_area
){
341 /* Save the end coordinates of the link area */
345 inside_link_area
= 0;
349 static void clear_link_areas (void)
355 link_area
= current
-> next
;
358 inside_link_area
= 0;
361 static void show (Dlg_head
*h
, char *paint_start
)
366 int acs
; /* Flag: Alternate character set active? */
368 int active_col
, active_line
;/* Active link position */
372 line
= col
= acs
= active_col
= active_line
= repeat_paint
= 0;
375 if (selected_item
< paint_start
)
376 selected_item
= NULL
;
378 for (p
= paint_start
; *p
!= CHAR_NODE_END
&& line
< help_lines
; p
++){
381 case CHAR_LINK_START
:
382 if (selected_item
== NULL
)
384 if (p
== selected_item
){
385 attrset (HELP_SLINK_COLOR
);
387 /* Store the coordinates of the link */
388 active_col
= col
+ 2;
389 active_line
= line
+ 2;
392 attrset (HELP_LINK_COLOR
);
393 start_link_area (col
, line
, p
);
395 case CHAR_LINK_POINTER
:
397 end_link_area (col
- 1, line
);
401 attrset (HELP_NORMAL_COLOR
);
410 dlg_move (h
, line
+2, col
+2);
412 col
+= strlen (VERSION
);
415 attrset (HELP_BOLD_COLOR
);
418 attrset (HELP_ITALIC_COLOR
);
421 attrset (HELP_NORMAL_COLOR
);
428 col
= (col
/8 + 1) * 8;
431 case CHAR_TEXTONLY_START
:
432 case CHAR_TEXTONLY_END
:
434 case CHAR_XONLY_START
:
435 while (*p
&& *p
!= CHAR_NODE_END
&& *p
!= CHAR_XONLY_END
)
437 if (*p
== CHAR_NODE_END
|| !*p
)
443 if (col
> HELP_WINDOW_WIDTH
-1)
446 dlg_move (h
, line
+2, col
+2);
448 if (c
== ' ' || c
== '.')
455 SLsmg_draw_object (h
->y
+ line
+ 2, h
->x
+ col
+ 2, c
);
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
;
475 selected_item
= NULL
;
477 } while (repeat_paint
);
479 /* Position the cursor over a nice link */
481 dlg_move (h
, active_line
, active_col
);
484 static int help_event (Gpm_Event
*event
, Widget
*w
)
486 Link_Area
*current_area
;
488 if (! (event
->type
& GPM_UP
))
491 /* The event is relative to the dialog window, adjust it: */
494 if (event
->buttons
& GPM_B_RIGHT
){
495 currentpoint
= startpoint
= history
[history_ptr
].page
;
496 selected_item
= history
[history_ptr
].link
;
499 history_ptr
= HISTORY_SIZE
-1;
501 help_callback (w
->parent
, 0, DLG_DRAW
);
505 /* Test whether the mouse click is inside one of the link areas */
506 current_area
= link_area
;
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
)
513 /* Test two line link area */
514 if (current_area
->y1
+ 1 == current_area
->y2
){
516 if (event
->y
== current_area
->y1
&& event
->x
>= current_area
->x1
)
518 /* The second line */
519 if (event
->y
== current_area
->y2
&& event
->x
<= current_area
->x2
)
522 /* Mouse will not work with link areas of more than two lines */
524 current_area
= current_area
-> next
;
527 /* Test whether a link area was found */
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
;
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)
546 /* Show the new node */
547 help_callback (w
->parent
, 0, DLG_DRAW
);
553 void help_help_cmd (Dlg_head
*h
)
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
;
561 help_callback (h
, 0, DLG_DRAW
);
565 void help_index_cmd (Dlg_head
*h
)
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;
574 if (!(new_item
= search_string (data
, "[Contents]")))
575 message (1, MSG_ERROR
, _(" Can't find node [Contents] in help file "));
577 currentpoint
= startpoint
= new_item
+ 1;
578 selected_item
= NULL
;
580 help_callback (h
, 0, DLG_DRAW
);
584 static void quit_cmd (void *x
)
586 Dlg_head
*h
= (Dlg_head
*) x
;
591 static void prev_node_cmd (Dlg_head
*h
)
593 currentpoint
= startpoint
= history
[history_ptr
].page
;
594 selected_item
= history
[history_ptr
].link
;
597 history_ptr
= HISTORY_SIZE
-1;
600 help_callback (h
, 0, DLG_DRAW
);
604 static int md_callback (Dlg_head
*h
, Widget
*w
, int msg
, int par
)
606 return default_proc (h
, msg
, par
);
609 static Widget
*mousedispatch_new (int y
, int x
, int yl
, int xl
)
611 Widget
*w
= xmalloc (sizeof (Widget
), "disp_new");
613 init_widget (w
, y
, x
, yl
, xl
,
614 (callback_fn
) md_callback
, 0, (mouse_h
) help_event
, NULL
);
619 static int help_handle_key (struct Dlg_head
*h
, int c
)
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
))
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
645 /* If there are no links, go backward in history */
648 history_ptr
= HISTORY_SIZE
-1;
650 currentpoint
= startpoint
= history
[history_ptr
].page
;
651 selected_item
= history
[history_ptr
].link
;
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;
659 selected_item
= NULL
;
664 /* select next link */
665 new_item
= select_next_link (startpoint
, selected_item
);
667 selected_item
= new_item
;
668 if (selected_item
>= last_shown
){
672 selected_item
= NULL
;
674 } else if (c
== KEY_DOWN
)
677 selected_item
= NULL
;
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
){
689 if (link_area
!= NULL
)
690 selected_item
= link_area
->link_name
;
692 selected_item
= NULL
;
699 new_item
= currentpoint
;
700 while (*new_item
&& *new_item
!= CHAR_NODE_END
)
702 if (*++new_item
== '['){
703 while (*new_item
!= ']')
705 currentpoint
= new_item
+ 2;
706 selected_item
= NULL
;
712 new_item
= currentpoint
;
713 while (new_item
> data
+ 1 && *new_item
!= CHAR_NODE_END
)
716 while (new_item
> data
&& *new_item
!= CHAR_NODE_END
)
718 while (*new_item
!= ']')
720 currentpoint
= new_item
+ 2;
721 selected_item
= NULL
;
737 help_callback (h
, 0, DLG_DRAW
);
741 static int help_callback (struct Dlg_head
*h
, int id
, int msg
)
745 attrset (HELP_NORMAL_COLOR
);
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
);
756 return help_handle_key (h
, id
);
761 void interactive_display_finish (void)
767 void interactive_display (char *filename
, char *node
)
769 WButtonBar
*help_bar
;
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
));
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 ();
784 if (help_lines
> LINES
- 4)
785 help_lines
= LINES
- 4;
787 whelp
= create_dlg (0, 0, help_lines
+4, HELP_WINDOW_WIDTH
+4, dialog_colors
,
788 help_callback
, "[Help]", "help", DLG_TRYUP
|DLG_CENTER
);
790 /* allow us to process the tab key */
794 selected_item
= search_string_node (main
, STRING_LINK_START
) - 1;
795 currentpoint
= startpoint
= main
+ 1;
797 for (history_ptr
= HISTORY_SIZE
; history_ptr
;){
799 history
[history_ptr
].page
= currentpoint
;
800 history
[history_ptr
].link
= selected_item
;
804 help_bar
= buttonbar_new (keybar_visible
);
805 help_bar
->widget
.y
-= whelp
->y
;
806 help_bar
->widget
.x
-= whelp
->x
;
808 md
= mousedispatch_new (1, 1, help_lines
, HELP_WINDOW_WIDTH
-2);
810 add_widget (whelp
, help_bar
);
811 add_widget (whelp
, md
);
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
);
828 interactive_display_finish ();
831 x_interactive_display ();