1/* footnotes.c -- Some functions for manipulating footnotes. 2 $Id: footnotes.c,v 1.4 2004/04/11 17:56:45 karl Exp $ 3 4 Copyright (C) 1993, 1997, 1998, 1999, 2002, 2004 Free Software 5 Foundation, Inc. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2, or (at your option) 10 any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 21 Originally written by Brian Fox (bfox@ai.mit.edu). */ 22 23#include "info.h" 24 25/* Nonzero means attempt to show footnotes when displaying a new window. */ 26int auto_footnotes_p = 0; 27 28static char *footnote_nodename = "*Footnotes*"; 29 30NODE * make_footnotes_node (NODE *node); 31 32#define FOOTNOTE_HEADER_FORMAT \ 33 "*** Footnotes appearing in the node `%s' ***\n" 34 35/* Find the window currently showing footnotes. */ 36static WINDOW * 37find_footnotes_window (void) 38{ 39 WINDOW *win; 40 41 /* Try to find an existing window first. */ 42 for (win = windows; win; win = win->next) 43 if (internal_info_node_p (win->node) && 44 (strcmp (win->node->nodename, footnote_nodename) == 0)) 45 break; 46 47 return (win); 48} 49 50/* Manufacture a node containing the footnotes of this node, and 51 return the manufactured node. If NODE has no footnotes, return a 52 NULL pointer. */ 53NODE * 54make_footnotes_node (NODE *node) 55{ 56 NODE *fn_node, *result = (NODE *)NULL; 57 long fn_start; 58 59 /* Make the initial assumption that the footnotes appear as simple 60 text within this windows node. */ 61 fn_node = node; 62 63 /* See if this node contains the magic footnote label. */ 64 fn_start = 65 info_search_in_node (FOOTNOTE_LABEL, node, 0, (WINDOW *)NULL, 1, 0); 66 67 /* If it doesn't, check to see if it has an associated footnotes node. */ 68 if (fn_start == -1) 69 { 70 REFERENCE **refs; 71 72 refs = info_xrefs_of_node (node); 73 74 if (refs) 75 { 76 register int i; 77 char *refname; 78 int reflen = strlen ("-Footnotes") + strlen (node->nodename); 79 80 refname = (char *)xmalloc (reflen + 1); 81 82 strcpy (refname, node->nodename); 83 strcat (refname, "-Footnotes"); 84 85 for (i = 0; refs[i]; i++) 86 if ((refs[i]->nodename != (char *)NULL) && 87 /* Support both the older "foo-Footnotes" and the new 88 style "foo-Footnote-NN" references. */ 89 (strcmp (refs[i]->nodename, refname) == 0 || 90 (strncmp (refs[i]->nodename, refname, reflen - 1) == 0 && 91 refs[i]->nodename[reflen - 1] == '-' && 92 isdigit (refs[i]->nodename[reflen])))) 93 { 94 char *filename; 95 96 filename = node->parent; 97 if (!filename) 98 filename = node->filename; 99 100 fn_node = info_get_node (filename, refname); 101 102 if (fn_node) 103 fn_start = 0; 104 105 break; 106 } 107 108 free (refname); 109 info_free_references (refs); 110 } 111 } 112 113 /* If we never found the start of a footnotes area, quit now. */ 114 if (fn_start == -1) 115 return ((NODE *)NULL); 116 117 /* Make the new node. */ 118 result = (NODE *)xmalloc (sizeof (NODE)); 119 result->flags = 0; 120 result->display_pos = 0; 121 122 /* Get the size of the footnotes appearing within this node. */ 123 { 124 char *header; 125 long text_start = fn_start; 126 127 header = (char *)xmalloc 128 (1 + strlen (node->nodename) + strlen (FOOTNOTE_HEADER_FORMAT)); 129 sprintf (header, FOOTNOTE_HEADER_FORMAT, node->nodename); 130 131 /* Move the start of the displayed text to right after the first line. 132 This effectively skips either "---- footno...", or "File: foo...". */ 133 while (text_start < fn_node->nodelen) 134 if (fn_node->contents[text_start++] == '\n') 135 break; 136 137 result->nodelen = strlen (header) + fn_node->nodelen - text_start; 138 139 /* Set the contents of this node. */ 140 result->contents = (char *)xmalloc (1 + result->nodelen); 141 sprintf (result->contents, "%s", header); 142 memcpy (result->contents + strlen (header), 143 fn_node->contents + text_start, fn_node->nodelen - text_start); 144 145 name_internal_node (result, footnote_nodename); 146 free (header); 147 } 148 149#if defined (NOTDEF) 150 /* If the footnotes were gleaned from the node that we were called with, 151 shorten the calling node's display length. */ 152 if (fn_node == node) 153 narrow_node (node, 0, fn_start); 154#endif /* NOTDEF */ 155 156 return (result); 157} 158 159/* Create or delete the footnotes window depending on whether footnotes 160 exist in WINDOW's node or not. Returns FN_FOUND if footnotes were found 161 and displayed. Returns FN_UNFOUND if there were no footnotes found 162 in WINDOW's node. Returns FN_UNABLE if there were footnotes, but the 163 window to show them couldn't be made. */ 164int 165info_get_or_remove_footnotes (WINDOW *window) 166{ 167 WINDOW *fn_win; 168 NODE *new_footnotes; 169 170 fn_win = find_footnotes_window (); 171 172 /* If we are in the footnotes window, change nothing. */ 173 if (fn_win == window) 174 return (FN_FOUND); 175 176 /* Try to find footnotes for this window's node. */ 177 new_footnotes = make_footnotes_node (window->node); 178 179 /* If there was a window showing footnotes, and there are no footnotes 180 for the current window, delete the old footnote window. */ 181 if (fn_win && !new_footnotes) 182 { 183 if (windows->next) 184 info_delete_window_internal (fn_win); 185 } 186 187 /* If there are footnotes for this window's node, but no window around 188 showing footnotes, try to make a new window. */ 189 if (new_footnotes && !fn_win) 190 { 191 WINDOW *old_active; 192 WINDOW *last, *win; 193 194 /* Always make this window be the last one appearing in the list. Find 195 the last window in the chain. */ 196 for (win = windows, last = windows; win; last = win, win = win->next); 197 198 /* Try to split this window, and make the split window the one to 199 contain the footnotes. */ 200 old_active = active_window; 201 active_window = last; 202 fn_win = window_make_window (new_footnotes); 203 active_window = old_active; 204 205 if (!fn_win) 206 { 207 free (new_footnotes->contents); 208 free (new_footnotes); 209 210 /* If we are hacking automatic footnotes, and there are footnotes 211 but we couldn't display them, print a message to that effect. */ 212 if (auto_footnotes_p) 213 inform_in_echo_area ((char *) _("Footnotes could not be displayed")); 214 return (FN_UNABLE); 215 } 216 } 217 218 /* If there are footnotes, and there is a window to display them, 219 make that window be the number of lines appearing in the footnotes. */ 220 if (new_footnotes && fn_win) 221 { 222 window_set_node_of_window (fn_win, new_footnotes); 223 224 window_change_window_height 225 (fn_win, fn_win->line_count - fn_win->height); 226 227 remember_window_and_node (fn_win, new_footnotes); 228 add_gcable_pointer (new_footnotes->contents); 229 } 230 231 if (!new_footnotes) 232 return (FN_UNFOUND); 233 else 234 return (FN_FOUND); 235} 236 237/* Show the footnotes associated with this node in another window. */ 238DECLARE_INFO_COMMAND (info_show_footnotes, 239 _("Show the footnotes associated with this node in another window")) 240{ 241 /* A negative argument means just make the window go away. */ 242 if (count < 0) 243 { 244 WINDOW *fn_win = find_footnotes_window (); 245 246 /* If there is an old footnotes window, and it isn't the only window 247 on the screen, delete it. */ 248 if (fn_win && windows->next) 249 info_delete_window_internal (fn_win); 250 } 251 else 252 { 253 int result; 254 255 result = info_get_or_remove_footnotes (window); 256 257 switch (result) 258 { 259 case FN_UNFOUND: 260 info_error ((char *) msg_no_foot_node, NULL, NULL); 261 break; 262 263 case FN_UNABLE: 264 info_error ((char *) msg_win_too_small, NULL, NULL); 265 break; 266 } 267 } 268} 269