home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / texinfo-3.7-src.tgz / tar.out / fsf / texinfo / info / nodemenu.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  8KB  |  330 lines

  1. /* nodemenu.c -- Produce a menu of all visited nodes. */
  2.  
  3. /* This file is part of GNU Info, a program for reading online documentation
  4.    stored in Info format.
  5.  
  6.    Copyright (C) 1993 Free Software Foundation, Inc.
  7.  
  8.    This program is free software; you can redistribute it and/or modify
  9.    it under the terms of the GNU General Public License as published by
  10.    the Free Software Foundation; either version 2, or (at your option)
  11.    any later version.
  12.  
  13.    This program is distributed in the hope that it will be useful,
  14.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.    GNU General Public License for more details.
  17.  
  18.    You should have received a copy of the GNU General Public License
  19.    along with this program; if not, write to the Free Software
  20.    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21.  
  22.    Written by Brian Fox (bfox@ai.mit.edu). */
  23.  
  24. #include "info.h"
  25.  
  26. /* Return a line describing the format of a node information line. */
  27. static char *
  28. nodemenu_format_info ()
  29. {
  30.   return ("\n\
  31. * Menu:\n\
  32.   (File)Node                        Lines   Size   Containing File\n\
  33.   ----------                        -----   ----   ---------------");
  34. }
  35.  
  36. /* Produce a formatted line of information about NODE.  Here is what we want
  37.    the output listing to look like:
  38.  
  39. * Menu:
  40.   (File)Node                        Lines   Size   Containing File
  41.   ----------                        -----   ----   ---------------
  42. * (emacs)Buffers::                  48      2230   /usr/gnu/info/emacs/emacs-1
  43. * (autoconf)Writing configure.in::  123     58789  /usr/gnu/info/autoconf/autoconf-1
  44. * (dir)Top::                40      589    /usr/gnu/info/dir
  45. */
  46. static char *
  47. format_node_info (node)
  48.      NODE *node;
  49. {
  50.   register int i, len;
  51.   char *parent, *containing_file;
  52.   static char *line_buffer = (char *)NULL;
  53.  
  54.   if (!line_buffer)
  55.     line_buffer = (char *)xmalloc (1000);
  56.  
  57.   if (node->parent)
  58.     {
  59.       parent = filename_non_directory (node->parent);
  60.       if (!parent)
  61.     parent = node->parent;
  62.     }
  63.   else
  64.     parent = (char *)NULL;
  65.  
  66.   containing_file = node->filename;
  67.  
  68.   if (!parent && !*containing_file)
  69.     sprintf (line_buffer, "* %s::", node->nodename);
  70.   else
  71.     {
  72.       char *file = (char *)NULL;
  73.  
  74.       if (parent)
  75.     file = parent;
  76.       else
  77.     file = filename_non_directory (containing_file);
  78.  
  79.       if (!file)
  80.     file = containing_file;
  81.  
  82.       if (!*file)
  83.     file = "dir";
  84.  
  85.       sprintf (line_buffer, "* (%s)%s::", file, node->nodename);
  86.     }
  87.  
  88.   len = pad_to (36, line_buffer);
  89.  
  90.   {
  91.     int lines = 1;
  92.  
  93.     for (i = 0; i < node->nodelen; i++)
  94.       if (node->contents[i] == '\n')
  95.     lines++;
  96.  
  97.     sprintf (line_buffer + len, "%d", lines);
  98.   }
  99.  
  100.   len = pad_to (44, line_buffer);
  101.   sprintf (line_buffer + len, "%d", node->nodelen);
  102.  
  103.   if (node->filename && *(node->filename))
  104.     {
  105.       len = pad_to (51, line_buffer);
  106.       sprintf (line_buffer + len, node->filename);
  107.     }
  108.  
  109.   return (strdup (line_buffer));
  110. }
  111.  
  112. /* Little string comparison routine for qsort (). */
  113. static int
  114. compare_strings (string1, string2)
  115.      char **string1, **string2;
  116. {
  117.   return (strcasecmp (*string1, *string2));
  118. }
  119.  
  120. /* The name of the nodemenu node. */
  121. static char *nodemenu_nodename = "*Node Menu*";
  122.  
  123. /* Produce an informative listing of all the visited nodes, and return it
  124.    in a node.  If FILTER_FUNC is non-null, it is a function which filters
  125.    which nodes will appear in the listing.  FILTER_FUNC takes an argument
  126.    of NODE, and returns non-zero if the node should appear in the listing. */
  127. NODE *
  128. get_visited_nodes (filter_func)
  129.      Function *filter_func;
  130. {
  131.   register int i, iw_index;
  132.   INFO_WINDOW *info_win;
  133.   NODE *node;
  134.   char **lines = (char **)NULL;
  135.   int lines_index = 0, lines_slots = 0;
  136.  
  137.   if (!info_windows)
  138.     return ((NODE *)NULL);
  139.  
  140.   for (iw_index = 0; info_win = info_windows[iw_index]; iw_index++)
  141.     {
  142.       for (i = 0; i < info_win->nodes_index; i++)
  143.     {
  144.       node = info_win->nodes[i];
  145.  
  146.       /* We skip mentioning "*Node Menu*" nodes. */
  147.       if (internal_info_node_p (node) &&
  148.           (strcmp (node->nodename, nodemenu_nodename) == 0))
  149.         continue;
  150.  
  151.       if (node && (!filter_func || (*filter_func) (node)))
  152.         {
  153.           char *line;
  154.  
  155.           line = format_node_info (node);
  156.           add_pointer_to_array
  157.         (line, lines_index, lines, lines_slots, 20, char *);
  158.         }
  159.     }
  160.     }
  161.  
  162.   /* Sort the array of information lines, if there are any. */
  163.   if (lines)
  164.     {
  165.       register int j, newlen;
  166.       char **temp;
  167.  
  168.       qsort (lines, lines_index, sizeof (char *), compare_strings);
  169.  
  170.       /* Delete duplicates. */
  171.       for (i = 0, newlen = 1; i < lines_index - 1; i++)
  172.     {
  173.       if (strcmp (lines[i], lines[i + 1]) == 0)
  174.         {
  175.           free (lines[i]);
  176.           lines[i] = (char *)NULL;
  177.         }
  178.       else
  179.         newlen++;
  180.     }
  181.  
  182.       /* We have free ()'d and marked all of the duplicate slots.
  183.      Copy the live slots rather than pruning the dead slots. */
  184.       temp = (char **)xmalloc ((1 + newlen) * sizeof (char *));
  185.       for (i = 0, j = 0; i < lines_index; i++)
  186.     if (lines[i])
  187.       temp[j++] = lines[i];
  188.  
  189.       temp[j] = (char *)NULL;
  190.       free (lines);
  191.       lines = temp;
  192.       lines_index = newlen;
  193.     }
  194.  
  195.   initialize_message_buffer ();
  196.  
  197.   printf_to_message_buffer
  198.     ("%s", replace_in_documentation
  199.      ("Here is the menu of nodes you have recently visited.\n\
  200. Select one from this menu, or use `\\[history-node]' in another window.\n"));
  201.  
  202.   printf_to_message_buffer ("%s\n", nodemenu_format_info ());
  203.  
  204.   for (i = 0; (lines != (char **)NULL) && (i < lines_index); i++)
  205.     {
  206.       printf_to_message_buffer ("%s\n", lines[i]);
  207.       free (lines[i]);
  208.     }
  209.  
  210.   if (lines)
  211.     free (lines);
  212.  
  213.   node = message_buffer_to_node ();
  214.   add_gcable_pointer (node->contents);
  215.   return (node);
  216. }
  217.  
  218. DECLARE_INFO_COMMAND (list_visited_nodes,
  219.    "Make a window containing a menu of all of the currently visited nodes")
  220. {
  221.   WINDOW *new;
  222.   NODE *node;
  223.  
  224.   set_remembered_pagetop_and_point (window);
  225.  
  226.   /* If a window is visible and showing the buffer list already, re-use it. */
  227.   for (new = windows; new; new = new->next)
  228.     {
  229.       node = new->node;
  230.  
  231.       if (internal_info_node_p (node) &&
  232.       (strcmp (node->nodename, nodemenu_nodename) == 0))
  233.     break;
  234.     }
  235.  
  236.   /* If we couldn't find an existing window, try to use the next window
  237.      in the chain. */
  238.   if (!new && window->next)
  239.     new = window->next;
  240.  
  241.   /* If we still don't have a window, make a new one to contain the list. */
  242.   if (!new)
  243.     {
  244.       WINDOW *old_active;
  245.  
  246.       old_active = active_window;
  247.       active_window = window;
  248.       new = window_make_window ((NODE *)NULL);
  249.       active_window = old_active;
  250.     }
  251.  
  252.   /* If we couldn't make a new window, use this one. */
  253.   if (!new)
  254.     new = window;
  255.  
  256.   /* Lines do not wrap in this window. */
  257.   new->flags |= W_NoWrap;
  258.   node = get_visited_nodes ((Function *)NULL);
  259.   name_internal_node (node, nodemenu_nodename);
  260.  
  261.   /* Even if this is an internal node, we don't want the window
  262.      system to treat it specially.  So we turn off the internalness
  263.      of it here. */
  264.   node->flags &= ~N_IsInternal;
  265.  
  266.   /* If this window is already showing a node menu, reuse the existing node
  267.      slot. */
  268.   {
  269.     int remember_me = 1;
  270.  
  271. #if defined (NOTDEF)
  272.     if (internal_info_node_p (new->node) &&
  273.     (strcmp (new->node->nodename, nodemenu_nodename) == 0))
  274.       remember_me = 0;
  275. #endif /* NOTDEF */
  276.  
  277.     window_set_node_of_window (new, node);
  278.  
  279.     if (remember_me)
  280.       remember_window_and_node (new, node);
  281.   }
  282.  
  283.   active_window = new;
  284. }
  285.  
  286. DECLARE_INFO_COMMAND (select_visited_node,
  287.       "Select a node which has been previously visited in a visible window")
  288. {
  289.   char *line;
  290.   NODE *node;
  291.   REFERENCE **menu;
  292.  
  293.   node = get_visited_nodes ((Function *)NULL);
  294.  
  295.   menu = info_menu_of_node (node);
  296.   free (node);
  297.  
  298.   line =
  299.     info_read_completing_in_echo_area (window, "Select visited node: ", menu);
  300.  
  301.   window = active_window;
  302.  
  303.   /* User aborts, just quit. */
  304.   if (!line)
  305.     {
  306.       info_abort_key (window, 0, 0);
  307.       info_free_references (menu);
  308.       return;
  309.     }
  310.  
  311.   if (*line)
  312.     {
  313.       REFERENCE *entry;
  314.  
  315.       /* Find the selected label in the references. */
  316.       entry = info_get_labeled_reference (line, menu);
  317.  
  318.       if (!entry)
  319.     info_error ("The reference disappeared! (%s).", line);
  320.       else
  321.     info_select_reference (window, entry);
  322.     }
  323.  
  324.   free (line);
  325.   info_free_references (menu);
  326.  
  327.   if (!info_error_was_printed)
  328.     window_clear_echo_area ();
  329. }
  330.