home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / OS2 / gnuinfo.zip / info / nodemenu.c < prev    next >
C/C++ Source or Header  |  1997-07-24  |  9KB  |  340 lines

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