summaryrefslogtreecommitdiff
path: root/info/footnotes.c
blob: 552ae2a1ade53e325470ae959a5e614275eacf7b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/* footnotes.c -- Some functions for manipulating footnotes.
   $Id: footnotes.c,v 1.9 2008/06/11 09:55:42 gray Exp $

   Copyright (C) 1993, 1997, 1998, 1999, 2002, 2004, 2007,
   2008 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

   Originally written by Brian Fox (bfox@ai.mit.edu). */

#include "info.h"

/* Nonzero means attempt to show footnotes when displaying a new window. */
int auto_footnotes_p = 0;

static char *footnote_nodename = "*Footnotes*";

NODE * make_footnotes_node (NODE *node);

#define FOOTNOTE_HEADER_FORMAT \
   "*** Footnotes appearing in the node `%s' ***\n"

/* Find the window currently showing footnotes. */
static WINDOW *
find_footnotes_window (void)
{
  WINDOW *win;

  /* Try to find an existing window first. */
  for (win = windows; win; win = win->next)
    if (internal_info_node_p (win->node) &&
        (strcmp (win->node->nodename, footnote_nodename) == 0))
      break;

  return win;
}

/* Manufacture a node containing the footnotes of this node, and
   return the manufactured node.  If NODE has no footnotes, return a 
   NULL pointer. */
NODE *
make_footnotes_node (NODE *node)
{
  NODE *fn_node, *result = NULL;
  long fn_start;

  /* Make the initial assumption that the footnotes appear as simple
     text within this windows node. */
  fn_node = node;

  /* See if this node contains the magic footnote label. */
  fn_start =
    info_search_in_node (FOOTNOTE_LABEL, node, 0, NULL, 1, 0);

  /* If it doesn't, check to see if it has an associated footnotes node. */
  if (fn_start == -1)
    {
      REFERENCE **refs;

      refs = info_xrefs_of_node (node);

      if (refs)
        {
          register int i;
          char *refname;
          int reflen = strlen ("-Footnotes") + strlen (node->nodename);

          refname = xmalloc (reflen + 1);

          strcpy (refname, node->nodename);
          strcat (refname, "-Footnotes");

          for (i = 0; refs[i]; i++)
            if ((refs[i]->nodename != NULL) &&
                /* Support both the older "foo-Footnotes" and the new
                   style "foo-Footnote-NN" references.  */
                (strcmp (refs[i]->nodename, refname) == 0 ||
                 (strncmp (refs[i]->nodename, refname, reflen - 1) == 0 &&
                  refs[i]->nodename[reflen - 1] == '-' &&
                  isdigit (refs[i]->nodename[reflen]))))
              {
                char *filename;

                filename = node->parent;
                if (!filename)
                  filename = node->filename;

                fn_node = info_get_node (filename, refname);

                if (fn_node)
                  fn_start = 0;

                break;
              }

          free (refname);
          info_free_references (refs);
        }
    }

  /* If we never found the start of a footnotes area, quit now. */
  if (fn_start == -1)
    return NULL;

  /* Make the new node. */
  result = xmalloc (sizeof (NODE));
  result->flags = 0;
  result->display_pos = 0;

  /* Get the size of the footnotes appearing within this node. */
  {
    char *header;
    long text_start = fn_start;

    header = xmalloc
      (1 + strlen (node->nodename) + strlen (FOOTNOTE_HEADER_FORMAT));
    sprintf (header, FOOTNOTE_HEADER_FORMAT, node->nodename);

    /* Move the start of the displayed text to right after the first line.
       This effectively skips either "---- footno...", or "File: foo...". */
    while (text_start < fn_node->nodelen)
      if (fn_node->contents[text_start++] == '\n')
        break;
  
    result->nodelen = strlen (header) + fn_node->nodelen - text_start;

    /* Set the contents of this node. */
    result->contents = xmalloc (1 + result->nodelen);
    sprintf (result->contents, "%s", header);
    memcpy (result->contents + strlen (header),
            fn_node->contents + text_start, fn_node->nodelen - text_start);

    name_internal_node (result, footnote_nodename);
    free (header);
  }

#if defined (NOTDEF)
  /* If the footnotes were gleaned from the node that we were called with,
     shorten the calling node's display length. */
  if (fn_node == node)
    narrow_node (node, 0, fn_start);
#endif /* NOTDEF */

  return result;
}

/* Create or delete the footnotes window depending on whether footnotes
   exist in WINDOW's node or not.  Returns FN_FOUND if footnotes were found
   and displayed.  Returns FN_UNFOUND if there were no footnotes found
   in WINDOW's node.  Returns FN_UNABLE if there were footnotes, but the
   window to show them couldn't be made. */
int
info_get_or_remove_footnotes (WINDOW *window)
{
  WINDOW *fn_win;
  NODE *new_footnotes;

  fn_win = find_footnotes_window ();

  /* If we are in the footnotes window, change nothing. */
  if (fn_win == window)
    return FN_FOUND;

  /* Try to find footnotes for this window's node. */
  new_footnotes = make_footnotes_node (window->node);

  /* If there was a window showing footnotes, and there are no footnotes
     for the current window, delete the old footnote window. */
  if (fn_win && !new_footnotes)
    {
      if (windows->next)
        info_delete_window_internal (fn_win);
    }

  /* If there are footnotes for this window's node, but no window around
     showing footnotes, try to make a new window. */
  if (new_footnotes && !fn_win)
    {
      WINDOW *old_active;
      WINDOW *last, *win;

      /* Always make this window be the last one appearing in the list.  Find
         the last window in the chain. */
      for (win = windows, last = windows; win; last = win, win = win->next);

      /* Try to split this window, and make the split window the one to
         contain the footnotes. */
      old_active = active_window;
      active_window = last;
      fn_win = window_make_window (new_footnotes);
      active_window = old_active;

      if (!fn_win)
        {
          free (new_footnotes->contents);
          free (new_footnotes);

          /* If we are hacking automatic footnotes, and there are footnotes
             but we couldn't display them, print a message to that effect. */
          if (auto_footnotes_p)
            inform_in_echo_area (_("Footnotes could not be displayed"));
          return FN_UNABLE;
        }
    }

  /* If there are footnotes, and there is a window to display them,
     make that window be the number of lines appearing in the footnotes. */
  if (new_footnotes && fn_win)
    {
      window_set_node_of_window (fn_win, new_footnotes);

      window_change_window_height
        (fn_win, fn_win->line_count - fn_win->height);

      remember_window_and_node (fn_win, new_footnotes);
      add_gcable_pointer (new_footnotes->contents);
    }

  if (!new_footnotes)
    return FN_UNFOUND;
  else
    return FN_FOUND;
}

/* Show the footnotes associated with this node in another window. */
DECLARE_INFO_COMMAND (info_show_footnotes,
   _("Show the footnotes associated with this node in another window"))
{
  /* A negative argument means just make the window go away. */
  if (count < 0)
    {
      WINDOW *fn_win = find_footnotes_window ();

      /* If there is an old footnotes window, and it isn't the only window
         on the screen, delete it. */
      if (fn_win && windows->next)
        info_delete_window_internal (fn_win);
    }
  else
    {
      int result;

      result = info_get_or_remove_footnotes (window);

      switch (result)
        {
        case FN_UNFOUND:
          info_error (msg_no_foot_node, NULL, NULL);
          break;

        case FN_UNABLE:
          info_error (msg_win_too_small, NULL, NULL);
          break;
        }
    }
}