footnotes.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. /* footnotes.c -- Some functions for manipulating footnotes.
  2. $Id$
  3. Copyright 1993, 1997, 1998, 1999, 2002, 2004, 2007, 2008, 2011, 2013,
  4. 2014 Free Software Foundation, Inc.
  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 3 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. Originally written by Brian Fox. */
  16. #include "info.h"
  17. #include "session.h"
  18. #include "info-utils.h"
  19. #include "footnotes.h"
  20. /* Nonzero means attempt to show footnotes when displaying a new window. */
  21. int auto_footnotes_p = 0;
  22. static char *footnote_nodename = "*Footnotes*";
  23. /* Find the window currently showing footnotes. */
  24. static WINDOW *
  25. find_footnotes_window (void)
  26. {
  27. WINDOW *win;
  28. /* Try to find an existing window first. */
  29. for (win = windows; win; win = win->next)
  30. if (internal_info_node_p (win->node) &&
  31. (strcmp (win->node->nodename, footnote_nodename) == 0))
  32. break;
  33. return win;
  34. }
  35. /* Manufacture a node containing the footnotes of this node, and
  36. return the manufactured node. If NODE has no footnotes, return a
  37. NULL pointer. */
  38. NODE *
  39. make_footnotes_node (NODE *node)
  40. {
  41. NODE *fn_node, *footnotes_node = NULL, *result = NULL;
  42. long fn_start = -1;
  43. char *fnptr;
  44. /* Make the initial assumption that the footnotes appear as simple
  45. text within this windows node. */
  46. fn_node = node;
  47. /* See if this node contains the magic footnote label. */
  48. {
  49. char saved = node->contents[node->nodelen];
  50. node->contents[node->nodelen] = '\0';
  51. fnptr = strstr (node->contents, FOOTNOTE_LABEL);
  52. node->contents[node->nodelen] = saved;
  53. }
  54. if (fnptr)
  55. fn_start = fnptr - node->contents;
  56. /* If it doesn't, check to see if it has an associated footnotes node. */
  57. if (!fnptr)
  58. {
  59. REFERENCE **refs;
  60. refs = node->references;
  61. if (refs)
  62. {
  63. register int i;
  64. char *refname;
  65. int reflen = strlen ("-Footnotes") + strlen (node->nodename);
  66. refname = xmalloc (reflen + 1);
  67. strcpy (refname, node->nodename);
  68. strcat (refname, "-Footnotes");
  69. for (i = 0; refs[i]; i++)
  70. if (refs[i]->type == REFERENCE_XREF
  71. && (refs[i]->nodename != NULL)
  72. /* Support both the older "foo-Footnotes" and the new
  73. style "foo-Footnote-NN" references. */
  74. && (strcmp (refs[i]->nodename, refname) == 0 ||
  75. (strncmp (refs[i]->nodename, refname, reflen - 1) == 0 &&
  76. refs[i]->nodename[reflen - 1] == '-' &&
  77. isdigit (refs[i]->nodename[reflen]))))
  78. {
  79. footnotes_node = info_get_node (node->fullpath, refname);
  80. if (footnotes_node)
  81. {
  82. fn_node = footnotes_node;
  83. fn_start = 0;
  84. }
  85. break;
  86. }
  87. free (refname);
  88. }
  89. }
  90. /* If we never found the start of a footnotes area, quit now. */
  91. if (fn_start == -1)
  92. return NULL;
  93. /* Make the new node. */
  94. result = info_create_node ();
  95. /* Get the size of the footnotes appearing within this node. */
  96. {
  97. char *header;
  98. long text_start = fn_start;
  99. asprintf (&header,
  100. "*** Footnotes appearing in the node '%s' ***\n",
  101. node->nodename);
  102. /* Move the start of the displayed text to right after the first line.
  103. This effectively skips either "---- footno...", or "File: foo...". */
  104. while (text_start < fn_node->nodelen)
  105. if (fn_node->contents[text_start++] == '\n')
  106. break;
  107. result->nodelen = strlen (header) + fn_node->nodelen - text_start;
  108. /* Set the contents of this node. */
  109. result->contents = xmalloc (1 + result->nodelen);
  110. sprintf (result->contents, "%s", header);
  111. memcpy (result->contents + strlen (header),
  112. fn_node->contents + text_start, fn_node->nodelen - text_start);
  113. result->contents[strlen (header) + fn_node->nodelen - text_start] = '\0';
  114. /* Copy and adjust references that appear in footnotes section. */
  115. {
  116. REFERENCE **ref = fn_node->references;
  117. for (; *ref; ref++)
  118. {
  119. if ((*ref)->start > text_start)
  120. break;
  121. }
  122. result->references = info_copy_references (ref);
  123. for (ref = result->references; *ref; ref++)
  124. {
  125. (*ref)->start -= text_start - strlen (header);
  126. (*ref)->end -= text_start - strlen (header);
  127. }
  128. }
  129. result->nodename = xstrdup (footnote_nodename);
  130. result->flags |= N_IsInternal | N_WasRewritten;
  131. /* Needed in case the user follows a reference in the footnotes window. */
  132. result->fullpath = fn_node->fullpath;
  133. result->subfile = fn_node->subfile;
  134. free (header);
  135. }
  136. free_history_node (footnotes_node);
  137. return result;
  138. }
  139. /* Create or delete the footnotes window depending on whether footnotes
  140. exist in WINDOW's node or not. Returns FN_FOUND if footnotes were found
  141. and displayed. Returns FN_UNFOUND if there were no footnotes found
  142. in WINDOW's node. Returns FN_UNABLE if there were footnotes, but the
  143. window to show them couldn't be made. */
  144. int
  145. info_get_or_remove_footnotes (WINDOW *window)
  146. {
  147. WINDOW *fn_win;
  148. NODE *new_footnotes = 0;
  149. fn_win = find_footnotes_window ();
  150. /* If we are in the footnotes window, change nothing. */
  151. if (fn_win == window)
  152. return FN_FOUND;
  153. /* Don't display footnotes for the "*" node (entire contents of file) or
  154. for nodes without a name like completion windows. */
  155. if (window->node->nodename && strcmp ("*", window->node->nodename))
  156. /* Try to find footnotes for this window's node. */
  157. new_footnotes = make_footnotes_node (window->node);
  158. if (!new_footnotes)
  159. {
  160. /* If there was a window showing footnotes, and there are no footnotes
  161. for the current window, delete the old footnote window. */
  162. if (fn_win && windows->next)
  163. info_delete_window_internal (fn_win);
  164. return FN_UNFOUND;
  165. }
  166. /* If there is no window around showing footnotes, try
  167. to make a new window. */
  168. if (!fn_win)
  169. {
  170. WINDOW *old_active;
  171. WINDOW *last, *win;
  172. /* Always make this window be the last one appearing in the list. Find
  173. the last window in the chain. */
  174. for (win = windows, last = windows; win; last = win, win = win->next);
  175. /* Try to split this window, and make the split window the one to
  176. contain the footnotes. */
  177. old_active = active_window;
  178. active_window = last;
  179. fn_win = window_make_window ();
  180. active_window = old_active;
  181. /* If we are hacking automatic footnotes, and there are footnotes
  182. but we couldn't display them, print a message to that effect. */
  183. if (!fn_win)
  184. {
  185. if (auto_footnotes_p)
  186. info_error (_("Footnotes could not be displayed"));
  187. return FN_UNABLE;
  188. }
  189. }
  190. /* Note that info_set_node_of_window calls this function
  191. (info_get_or_remove_footnotes), but we do not recurse indefinitely
  192. because we check if we are in the footnote window above. */
  193. info_set_node_of_window (fn_win, new_footnotes);
  194. fn_win->flags |= W_TempWindow;
  195. /* Make the height be the number of lines appearing in the footnotes. */
  196. if (new_footnotes)
  197. window_change_window_height (fn_win, fn_win->line_count - fn_win->height);
  198. return FN_FOUND;
  199. }
  200. /* Show the footnotes associated with this node in another window. */
  201. DECLARE_INFO_COMMAND (info_show_footnotes,
  202. _("Show the footnotes associated with this node in another window"))
  203. {
  204. /* A negative argument means just make the window go away. */
  205. if (count < 0)
  206. {
  207. WINDOW *fn_win = find_footnotes_window ();
  208. /* If there is an old footnotes window, and it isn't the only window
  209. on the screen, delete it. */
  210. if (fn_win && windows->next)
  211. info_delete_window_internal (fn_win);
  212. }
  213. else
  214. {
  215. int result;
  216. result = info_get_or_remove_footnotes (window);
  217. switch (result)
  218. {
  219. case FN_UNFOUND:
  220. info_error ("%s", msg_no_foot_node);
  221. break;
  222. case FN_UNABLE:
  223. info_error ("%s", msg_win_too_small);
  224. break;
  225. }
  226. }
  227. }