269 lines
8.2 KiB
C
269 lines
8.2 KiB
C
/* footnotes.c -- Some functions for manipulating footnotes.
|
|
$Id: footnotes.c,v 1.4 2004/04/11 17:56:45 karl Exp $
|
|
|
|
Copyright (C) 1993, 1997, 1998, 1999, 2002, 2004 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 2, 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, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
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 = (NODE *)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, (WINDOW *)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 = (char *)xmalloc (reflen + 1);
|
|
|
|
strcpy (refname, node->nodename);
|
|
strcat (refname, "-Footnotes");
|
|
|
|
for (i = 0; refs[i]; i++)
|
|
if ((refs[i]->nodename != (char *)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 ((NODE *)NULL);
|
|
|
|
/* Make the new node. */
|
|
result = (NODE *)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 = (char *)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 = (char *)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 ((char *) _("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 ((char *) msg_no_foot_node, NULL, NULL);
|
|
break;
|
|
|
|
case FN_UNABLE:
|
|
info_error ((char *) msg_win_too_small, NULL, NULL);
|
|
break;
|
|
}
|
|
}
|
|
}
|