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 (HELP_TEXT_WIDTH + 4)
61 #define STRING_LINK_START "\01"
62 #define STRING_LINK_POINTER "\02"
63 #define STRING_LINK_END "\03"
64 #define STRING_NODE_END "\04"
66 static char *data
; /* Pointer to the loaded data file */
67 static int help_lines
; /* Lines in help viewer */
68 static int history_ptr
; /* For the history queue */
69 static char *main_node
; /* The main node */
70 static char *last_shown
= 0; /* Last byte shown in a screen */
71 static int end_of_node
= 0; /* Flag: the last character of the node shown? */
72 static char *currentpoint
, *startpoint
;
73 static char *selected_item
;
75 /* The widget variables */
76 static Dlg_head
*whelp
;
79 char *page
; /* Pointer to the selected page */
80 char *link
; /* Pointer to the selected link */
81 } history
[HISTORY_SIZE
];
83 /* Link areas for the mouse */
84 typedef struct Link_Area
{
87 struct Link_Area
*next
;
90 static Link_Area
*link_area
= NULL
;
91 static int inside_link_area
= 0;
93 static int help_callback (struct Dlg_head
*h
, int id
, int msg
);
95 #ifdef HAS_ACS_AS_PCCHARS
114 static int acs2pc (int acscode
)
118 for (i
= 0; acs2pc_table
[i
].acscode
!= 0; i
++)
119 if (acscode
== acs2pc_table
[i
].acscode
) {
120 return acs2pc_table
[i
].pccode
;
126 /* returns the position where text was found in the start buffer */
127 /* or 0 if not found */
129 search_string (char *start
, char *text
)
134 /* fmt sometimes replaces a space with a newline in the help file */
135 /* Replace the newlines in the link name with spaces to correct the situation */
142 for (d
= text
; *e
; e
++){
153 /* Searches text in the buffer pointed by start. Search ends */
154 /* if the CHAR_NODE_END is found in the text. Returns 0 on failure */
155 static char *search_string_node (char *start
, char *text
)
163 for (; *e
&& *e
!= CHAR_NODE_END
; e
++){
174 /* Searches the_char in the buffer pointer by start and searches */
175 /* it can search forward (direction = 1) or backward (direction = -1) */
176 static char *search_char_node (char *start
, char the_char
, int direction
)
182 for (; *e
&& (*e
!= CHAR_NODE_END
); e
+= direction
){
189 /* Returns the new current pointer when moved lines lines */
190 static char *move_forward2 (char *c
, int lines
)
196 for (line
= 0, p
= currentpoint
; *p
&& *p
!= CHAR_NODE_END
; p
++){
198 return currentpoint
= p
;
202 return currentpoint
= c
;
205 static char *move_backward2 (char *c
, int lines
)
211 for (line
= 0, p
= currentpoint
; *p
&& p
>= data
; p
--){
212 if (*p
== CHAR_NODE_END
)
214 /* We reached the beginning of the node */
215 /* Skip the node headers */
216 while (*p
!= ']') p
++;
217 return currentpoint
= p
+ 2;
219 if (*(p
- 1) == '\n')
222 return currentpoint
= p
;
224 return currentpoint
= c
;
227 static void move_forward (int i
)
231 currentpoint
= move_forward2 (currentpoint
, i
);
234 static void move_backward (int i
)
236 currentpoint
= move_backward2 (currentpoint
, ++i
);
239 static void move_to_top (int dummy
)
241 while (currentpoint
> data
&& *currentpoint
!= CHAR_NODE_END
)
243 while (*currentpoint
!= ']')
245 currentpoint
= currentpoint
+ 1;
246 selected_item
= NULL
;
249 static void move_to_bottom (int dummy
)
251 while (*currentpoint
&& *currentpoint
!= CHAR_NODE_END
)
254 move_backward (help_lines
- 1);
257 static char *help_follow_link (char *start
, char *selected_item
)
259 char link_name
[MAXLINKNAME
];
266 for (p
= selected_item
; *p
&& *p
!= CHAR_NODE_END
&& *p
!= CHAR_LINK_POINTER
; p
++)
268 if (*p
== CHAR_LINK_POINTER
){
270 for (i
= 1; *p
!= CHAR_LINK_END
&& *p
&& *p
!= CHAR_NODE_END
&& i
< MAXLINKNAME
-3; )
271 link_name
[i
++] = *++p
;
272 link_name
[i
-1] = ']';
274 p
= search_string (data
, link_name
);
279 /* Create a replacement page with the error message */
280 return _(" Help file format error\n");
283 static char *select_next_link (char *start
, char *current_link
)
290 p
= search_string_node (current_link
, STRING_LINK_END
);
293 p
= search_string_node (p
, STRING_LINK_START
);
299 static char *select_prev_link (char *start
, char *current_link
)
306 p
= current_link
- 1;
310 p
= search_char_node (p
, CHAR_LINK_START
, -1);
314 static void start_link_area (int x
, int y
, char *link_name
)
318 if (inside_link_area
)
319 message (0, _("Warning"), _(" Internal bug: Double start of link area "));
321 /* Allocate memory for a new link area */
322 new = g_new (Link_Area
, 1);
323 new->next
= link_area
;
326 /* Save the beginning coordinates of the link area */
330 /* Save the name of the destination anchor */
331 link_area
->link_name
= link_name
;
333 inside_link_area
= 1;
336 static void end_link_area (int x
, int y
)
338 if (inside_link_area
){
339 /* Save the end coordinates of the link area */
343 inside_link_area
= 0;
347 static void clear_link_areas (void)
353 link_area
= current
-> next
;
356 inside_link_area
= 0;
359 static void help_show (Dlg_head
*h
, char *paint_start
)
364 int acs
; /* Flag: Alternate character set active? */
366 int active_col
, active_line
;/* Active link position */
368 attrset (HELP_NORMAL_COLOR
);
371 line
= col
= acs
= active_col
= active_line
= repeat_paint
= 0;
374 if (selected_item
< paint_start
)
375 selected_item
= NULL
;
377 for (p
= paint_start
; *p
&& *p
!= CHAR_NODE_END
&& line
< help_lines
; p
++) {
378 c
= (unsigned char)*p
;
380 case CHAR_LINK_START
:
381 if (selected_item
== NULL
)
383 if (p
== selected_item
){
384 attrset (HELP_SLINK_COLOR
);
386 /* Store the coordinates of the link */
387 active_col
= col
+ 2;
388 active_line
= line
+ 2;
391 attrset (HELP_LINK_COLOR
);
392 start_link_area (col
, line
, p
);
394 case CHAR_LINK_POINTER
:
396 end_link_area (col
- 1, line
);
400 attrset (HELP_NORMAL_COLOR
);
409 dlg_move (h
, line
+2, col
+2);
411 col
+= strlen (VERSION
);
414 attrset (HELP_BOLD_COLOR
);
416 case CHAR_FONT_ITALIC
:
417 attrset (HELP_ITALIC_COLOR
);
419 case CHAR_FONT_NORMAL
:
420 attrset (HELP_NORMAL_COLOR
);
427 col
= (col
/8 + 1) * 8;
432 if (col
> HELP_WINDOW_WIDTH
-1)
435 dlg_move (h
, line
+2, col
+2);
437 if (c
== ' ' || c
== '.')
444 SLsmg_draw_object (h
->y
+ line
+ 2, h
->x
+ col
+ 2, c
);
448 #endif /* NATIVE_WIN32 */
456 end_of_node
= line
< help_lines
;
457 attrset (HELP_NORMAL_COLOR
);
458 if (selected_item
>= last_shown
){
459 if (link_area
!= NULL
){
460 selected_item
= link_area
->link_name
;
464 selected_item
= NULL
;
466 } while (repeat_paint
);
468 /* Position the cursor over a nice link */
470 dlg_move (h
, active_line
, active_col
);
473 static int help_event (Gpm_Event
*event
, Widget
*w
)
475 Link_Area
*current_area
;
477 if (! (event
->type
& GPM_UP
))
480 /* The event is relative to the dialog window, adjust it: */
483 if (event
->buttons
& GPM_B_RIGHT
){
484 currentpoint
= startpoint
= history
[history_ptr
].page
;
485 selected_item
= history
[history_ptr
].link
;
488 history_ptr
= HISTORY_SIZE
-1;
490 help_callback (w
->parent
, 0, DLG_DRAW
);
494 /* Test whether the mouse click is inside one of the link areas */
495 current_area
= link_area
;
498 /* Test one line link area */
499 if (event
->y
== current_area
->y1
&& event
->x
>= current_area
->x1
&&
500 event
->y
== current_area
->y2
&& event
->x
<= current_area
->x2
)
502 /* Test two line link area */
503 if (current_area
->y1
+ 1 == current_area
->y2
){
505 if (event
->y
== current_area
->y1
&& event
->x
>= current_area
->x1
)
507 /* The second line */
508 if (event
->y
== current_area
->y2
&& event
->x
<= current_area
->x2
)
511 /* Mouse will not work with link areas of more than two lines */
513 current_area
= current_area
-> next
;
516 /* Test whether a link area was found */
518 /* The click was inside a link area -> follow the link */
519 history_ptr
= (history_ptr
+1) % HISTORY_SIZE
;
520 history
[history_ptr
].page
= currentpoint
;
521 history
[history_ptr
].link
= current_area
->link_name
;
522 currentpoint
= startpoint
= help_follow_link (currentpoint
, current_area
->link_name
);
523 selected_item
= NULL
;
526 move_backward (help_lines
- 1);
527 else if (event
->y
>= help_lines
)
528 move_forward (help_lines
- 1);
529 else if (event
->y
< help_lines
/2)
535 /* Show the new node */
536 help_callback (w
->parent
, 0, DLG_DRAW
);
543 help_help_cmd (Dlg_head
*h
)
545 history_ptr
= (history_ptr
+1) % HISTORY_SIZE
;
546 history
[history_ptr
].page
= currentpoint
;
547 history
[history_ptr
].link
= selected_item
;
548 currentpoint
= startpoint
= search_string (data
, "[How to use help]") + 1;
549 selected_item
= NULL
;
550 help_callback (h
, 0, DLG_DRAW
);
554 help_index_cmd (Dlg_head
* h
)
558 if (!(new_item
= search_string (data
, "[Contents]"))) {
559 message (1, MSG_ERROR
, _(" Cannot find node %s in help file "),
564 history_ptr
= (history_ptr
+ 1) % HISTORY_SIZE
;
565 history
[history_ptr
].page
= currentpoint
;
566 history
[history_ptr
].link
= selected_item
;
568 currentpoint
= startpoint
= new_item
+ 1;
569 selected_item
= NULL
;
570 help_callback (h
, 0, DLG_DRAW
);
573 static void help_quit_cmd (void *x
)
575 dlg_stop ((Dlg_head
*)x
);
578 static void prev_node_cmd (Dlg_head
*h
)
580 currentpoint
= startpoint
= history
[history_ptr
].page
;
581 selected_item
= history
[history_ptr
].link
;
584 history_ptr
= HISTORY_SIZE
-1;
586 help_callback (h
, 0, DLG_DRAW
);
589 static int md_callback (Widget
*w
, int msg
, int par
)
591 return default_proc (msg
, par
);
594 static Widget
*mousedispatch_new (int y
, int x
, int yl
, int xl
)
596 Widget
*w
= g_new (Widget
, 1);
598 init_widget (w
, y
, x
, yl
, xl
,
599 (callback_fn
) md_callback
, 0, (mouse_h
) help_event
, NULL
);
604 static int help_handle_key (struct Dlg_head
*h
, int c
)
608 if (c
!= KEY_UP
&& c
!= KEY_DOWN
&&
609 check_movement_keys (c
, 1, help_lines
, currentpoint
,
610 (movefn
) move_backward2
,
611 (movefn
) move_forward2
,
612 (movefn
) move_to_top
,
613 (movefn
) move_to_bottom
))
625 #ifdef WE_WANT_TO_GO_BACKWARD_ON_KEY_RIGHT
626 /* Is there any reason why the right key would take us
627 * backward if there are no links selected?, I agree
628 * with Torben than doing nothing in this case is better
630 /* If there are no links, go backward in history */
633 history_ptr
= HISTORY_SIZE
-1;
635 currentpoint
= startpoint
= history
[history_ptr
].page
;
636 selected_item
= history
[history_ptr
].link
;
639 history_ptr
= (history_ptr
+1) % HISTORY_SIZE
;
640 history
[history_ptr
].page
= currentpoint
;
641 history
[history_ptr
].link
= selected_item
;
642 currentpoint
= startpoint
= help_follow_link (currentpoint
, selected_item
) + 1;
644 selected_item
= NULL
;
649 /* select next link */
650 new_item
= select_next_link (startpoint
, selected_item
);
652 selected_item
= new_item
;
653 if (selected_item
>= last_shown
){
657 selected_item
= NULL
;
659 } else if (c
== KEY_DOWN
)
662 selected_item
= NULL
;
667 /* select previous link */
668 new_item
= select_prev_link (startpoint
, selected_item
);
669 selected_item
= new_item
;
670 if (selected_item
< currentpoint
|| selected_item
>= last_shown
){
674 if (link_area
!= NULL
)
675 selected_item
= link_area
->link_name
;
677 selected_item
= NULL
;
684 new_item
= currentpoint
;
685 while (*new_item
&& *new_item
!= CHAR_NODE_END
)
687 if (*++new_item
== '['){
688 while (*++new_item
) {
689 if (*new_item
== ']' && *++new_item
&& *++new_item
) {
690 currentpoint
= new_item
;
691 selected_item
= NULL
;
700 new_item
= currentpoint
;
701 while (new_item
> data
+ 1 && *new_item
!= CHAR_NODE_END
)
704 while (new_item
> data
&& *new_item
!= CHAR_NODE_END
)
706 while (*new_item
!= ']')
708 currentpoint
= new_item
+ 2;
709 selected_item
= NULL
;
725 help_callback (h
, 0, DLG_DRAW
);
729 static int help_callback (struct Dlg_head
*h
, int id
, int msg
)
733 common_dialog_repaint (h
);
734 help_show (h
, currentpoint
);
738 return help_handle_key (h
, id
);
744 interactive_display_finish (void)
751 interactive_display (char *filename
, char *node
)
753 WButtonBar
*help_bar
;
755 char *hlpfile
= filename
;
758 data
= load_file (filename
);
760 data
= load_mc_home_file ("mc.hlp", &hlpfile
);
763 message (1, MSG_ERROR
, _(" Cannot open file %s \n %s "), hlpfile
,
764 unix_error_string (errno
));
776 if (!(main_node
= search_string (data
, node
))) {
777 message (1, MSG_ERROR
, _(" Cannot find node %s in help file "),
780 /* Fallback to [main], return if it also cannot be found */
781 main_node
= search_string (data
, "[main]");
783 interactive_display_finish ();
788 help_lines
= min (LINES
- 4, max (2 * LINES
/ 3, 18));
791 create_dlg (0, 0, help_lines
+ 4, HELP_WINDOW_WIDTH
+ 4,
792 dialog_colors
, help_callback
, "[Help]", _("Help"),
793 DLG_TRYUP
| DLG_CENTER
| DLG_WANT_TAB
);
795 selected_item
= search_string_node (main_node
, STRING_LINK_START
) - 1;
796 currentpoint
= startpoint
= main_node
+ 1;
798 for (history_ptr
= HISTORY_SIZE
; history_ptr
;) {
800 history
[history_ptr
].page
= currentpoint
;
801 history
[history_ptr
].link
= selected_item
;
804 help_bar
= buttonbar_new (1);
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
, 1, _("Help"), (buttonbarfn
) help_help_cmd
,
815 define_label_data (whelp
, 2, _("Index"), (buttonbarfn
) help_index_cmd
,
817 define_label_data (whelp
, 3, _("Prev"), (buttonbarfn
) prev_node_cmd
,
819 define_label (whelp
, 4, "", 0);
820 define_label (whelp
, 5, "", 0);
821 define_label (whelp
, 6, "", 0);
822 define_label (whelp
, 7, "", 0);
823 define_label (whelp
, 8, "", 0);
824 define_label (whelp
, 9, "", 0);
825 define_label_data (whelp
, 10, _("Quit"), help_quit_cmd
, whelp
);
828 interactive_display_finish ();