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