home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume4 / browser < prev    next >
Text File  |  1986-11-30  |  20KB  |  671 lines

  1. Subject: Amiga file browser
  2. From: Mike Meyer <harvard!topaz!hplabs!ucbvax!ucbopal.berkeley.edu!mwm>
  3. Newsgroups: mod.sources
  4. Approved: jpn@panda.UUCP
  5.  
  6. Mod.sources:  Volume 4, Issue 40
  7. Submitted by: Mike (I'll be mellow when I'm dead) Meyer <ucbvax!ucbopal!mwm>
  8.  
  9. # This is a shell archive.  Remove anything before this line, then
  10. # unpack it by saving it in a file and typing "sh file".  (Files
  11. # unpacked will be owned by you and have default permissions.)
  12. #
  13. # This archive contains:
  14. # README browser.c menustack.c
  15.  
  16. echo x - README
  17. sed 's/^    //' > "README" << '//E*O*F README//'
  18.     Here we have a file browser for the Amiga. 50% based on the the PARC
  19.     browsers,and 50% based on a browser I wrote for Unix long ago (whose
  20.     name, I now realize, was probably inspired by the PARC browsers).
  21.     
  22.     This is a "level 0" version. Useable, but it needs some things done
  23.     to. I've got more important things to do, so I'm turning it loose to
  24.     the net, in the hopes that someone else will improve it before I get
  25.     back to it.
  26.     
  27.     Things that need to be fixed:
  28.     
  29.         1) Theres a loose pointer in there somewhere, but it only seem
  30.         to mangle things if DEBUG is turned on. Not sure what's up.
  31.     
  32.         2) The UP and DOWN gadgets need to turn into a scroll bar gadget.
  33.     
  34.         3) The routine that pages the file needs to be re-written to
  35.         use the console driver, and handle lines longer than the
  36.         window is wide in a more intelligent manner.
  37.     
  38.         4) The initial (disks) menu needs to look to see what is
  39.         really there, instead of just giving you the default three.
  40.         Putting in the assigned'ed things at the same time might not
  41.         be a bad idea.
  42.     
  43.     Should you fix #1, or add any of #'s 2-4, please send me the source.
  44.     Likewise, any ideas you have or implement, let me know, and I'll see
  45.     about getting them into the first real version.
  46.     
  47.         <mike
  48.     
  49.     
  50. //E*O*F README//
  51.  
  52. echo x - browser.c
  53. sed 's/^    //' > "browser.c" << '//E*O*F browser.c//'
  54.     /*
  55.      * browser - Rummage around on disks.
  56.      *
  57.      *    copyright (c) 1986, Mike Meyer
  58.      *
  59.      * Permission is hereby granted to distribute this program, so long as this
  60.      * source file is distributed with it, and this copyright notice is not
  61.      * removed from the file.
  62.      *
  63.      * Locks, general -    do I need a stack of locks, one per directory level?
  64.      *            do I have to free the lock in done?
  65.      */
  66.     
  67.     #include <exec/types.h>
  68.     #include <graphics/gfxbase.h>
  69.     #include <libraries/dos.h>
  70.     #include <libraries/dosextens.h>
  71.     #include <intuition/intuition.h>
  72.     #include <stdio.h>
  73.     
  74.     #define INTUITION_REV        1
  75.     #define GRAPHICS_REV        1
  76.     
  77.     #define LONGEST_NAME        80    /* Longest file name we can deal with */
  78.     #define LONGEST_LINE        256    /* Longest line we will deal with */
  79.     #define AVG_LINE_LENGTH        66    /* A guess, tune it if you need to */
  80.     
  81.     #define UP_GADGET        ((unsigned short) 0)
  82.     #define DOWN_GADGET        ((unsigned short) 1)
  83.     #define GWIDTH            16    /* Width of my two gadgets */
  84.     #define GHEIGHT            9    /* and their heights */
  85.     
  86.     /*
  87.      * Pictures for the up and down arrows
  88.      */
  89.     static USHORT arrows[2][GHEIGHT] = {
  90.         {0xFE7F, 0xFC3F, 0xFA5F, 0xF66F,    /* Up */
  91.          0xFE7F, 0xFE7F, 0xFE7F, 0xFE7F, 0xFE7F},
  92.         {0xFE7F, 0xFE7F, 0xFE7F, 0xFE7F,    /* Down */
  93.          0xFE7F, 0xF66F, 0xFA5F, 0xFC3F, 0xFE7F}
  94.         } ;
  95.     /*
  96.      * Now, the Image structures that use the arrows
  97.      */
  98.     static struct Image Arrow_Image[2] = {
  99.         {0, 0, GWIDTH, GHEIGHT, 1, arrows[UP_GADGET], 1, 0,  /* Up */
  100.             (struct Image *) NULL},
  101.         {0, 0, GWIDTH, GHEIGHT, 1, arrows[DOWN_GADGET], 1, 0,  /* Down */
  102.             (struct Image *) NULL}
  103.         } ;
  104.     /*
  105.      * Now, my two Gadget structures
  106.      */
  107.     static struct Gadget Up_Gadget = {
  108.         (struct Gadget *) NULL,
  109.         0, 1 - GHEIGHT,            /* Left, Top */
  110.         GWIDTH, GHEIGHT,
  111.         GRELBOTTOM | GADGIMAGE        /* Standard bottom border gadget */
  112.         | GADGHCOMP,
  113.         RELVERIFY | BOTTOMBORDER,    /* Messages when released */
  114.         BOOLGADGET,            /* These be boolean gadgets */
  115.         (APTR) &(Arrow_Image[UP_GADGET]),
  116.         (APTR) NULL,            /* No rendering image, using HCOMP */
  117.         (struct IntuiText *) NULL,
  118.         0,                /* No mutex */
  119.         (APTR) NULL,            /* Nothing special */
  120.         UP_GADGET,            /* Yes, this is the up gadget */
  121.         (APTR) NULL            /* And nothing of mine */
  122.         } ;
  123.     
  124.     static struct Gadget Down_Gadget = {
  125.         &Up_Gadget,            /* Next gadget is Up_Gadget */
  126.         GWIDTH + 2, 1 - GHEIGHT,    /* Left, Top */
  127.         GWIDTH, GHEIGHT,
  128.         GRELBOTTOM | GADGIMAGE        /* Standard bottom border gadget */
  129.         | GADGHCOMP,
  130.         RELVERIFY | BOTTOMBORDER,    /* Messages when released */
  131.         BOOLGADGET,            /* These be boolean gadgets */
  132.         (APTR) &(Arrow_Image[DOWN_GADGET]),
  133.         (APTR) NULL,            /* No rendering image, using HCOMP */
  134.         (struct IntuiText *) NULL,
  135.         0,                /* No mutex */
  136.         (APTR) NULL,            /* Nothing special */
  137.         DOWN_GADGET,            /* Yes, this is the up gadget */
  138.         (APTR) NULL            /* And nothing of mine */
  139.         } ;
  140.     /*
  141.      * Now, the window for it all
  142.      */
  143.     static struct NewWindow New_Window = {
  144.         0, 0, 640, 200,            /* Full screen */
  145.         -1, -1,                /* Default pens */
  146.         MENUPICK | CLOSEWINDOW        /* I want to know about menupicks */
  147.         | GADGETUP,            /* Window closes and gadgets */
  148.         ACTIVATE            /* Standard window */
  149.         | SMART_REFRESH | NOCAREREFRESH | SIZEBBOTTOM
  150.         | WINDOWSIZING | WINDOWDEPTH | WINDOWCLOSE | WINDOWDRAG,
  151.         &Down_Gadget,            /* Add my gadgets */
  152.         (struct Image *) NULL,
  153.         "Browser 0.0",            /* Title */
  154.         (struct Screen *) NULL,
  155.         (struct BitMap *) NULL,
  156.         100, 40,            /* Minimum sizes */
  157.         640, 200,            /* Maximum sizes */
  158.         WBENCHSCREEN            /* and put it on the workbench */
  159.         } ;
  160.     /*
  161.      * My very own variables (mostly for done)
  162.      */
  163.     static struct Window    *My_Window = NULL ;
  164.     static FILE        *infile = NULL ;    /* Current input file */
  165.     static void        Page_File(unsigned short) ;
  166.     
  167.     /*
  168.      * And someone else's variables
  169.      */
  170.     struct IntuitionBase    *IntuitionBase ;
  171.     struct GfxBase        *GfxBase ;
  172.     extern struct Menu    *AutoMenu ;
  173.     
  174.     /*
  175.      * Finally, declare the string twiddling functions as voids
  176.      */
  177.     void    strcat(char *, char *), strcpy(char *, char *) ,
  178.         strncat(char *, char *, int) ;
  179.     
  180.     #ifdef    DEBUG
  181.     main() {
  182.     #else
  183.     _main() {
  184.     #endif
  185.         register struct IntuiMessage    *message, *GetMsg(struct MsgPort *) ;
  186.         register unsigned short        class, code ;
  187.     
  188.         /* Set up the world this lives in */
  189.     
  190.         if ((IntuitionBase = (struct IntuitionBase *)
  191.             OpenLibrary("intuition.library", INTUITION_REV)) == NULL)
  192.             done(20, "can't open intuition library") ;
  193.     
  194.         if ((GfxBase = (struct GfxBase *)
  195.             OpenLibrary("graphics.library", GRAPHICS_REV)) == NULL)
  196.             done(20, "can't open graphics library") ;
  197.     
  198.         if ((My_Window = (struct Window *) OpenWindow(&New_Window)) == NULL)
  199.             done(20, "can't open the window") ;
  200.     
  201.         SetAPen(My_Window -> RPort, 1) ;    /* Should be default! */
  202.     
  203.         /* Set up the first menu level */
  204.         Menu_Init() ;
  205.         Menu_Add("disks ", TRUE) ;
  206.         Menu_Item_Add("df0:", ITEMENABLED, 0, 0) ;
  207.         Menu_Item_Add("df1:", ITEMENABLED, 0, 0) ;
  208.         Menu_Item_Add("ram:", ITEMENABLED, 0, 0) ;
  209.         SetMenuStrip(My_Window, AutoMenu) ;
  210.     
  211.         /* Now spin on messages, handling them as they arrive */
  212.         for (;;) {
  213.             Wait(1 << My_Window -> UserPort -> mp_SigBit) ;
  214.             while (message = GetMsg(My_Window -> UserPort)) {
  215.                 class = message -> Class ;
  216.                 code = message -> Code ;
  217.                 ReplyMsg(message) ;
  218.     
  219.                 switch (class) {
  220.                     case CLOSEWINDOW:
  221.                         done(0, "exit") ;
  222.     
  223.                     case MENUPICK:
  224.                         if (code != MENUNULL)
  225.                             Examine_Menu_Pick(code) ;
  226.                         break ;
  227.     
  228.                     case GADGETUP:
  229.                         Page_File(((struct Gadget *) (message -> IAddress)) -> GadgetID) ;
  230.                         break ;
  231.     
  232.                     default:
  233.     #ifdef    DEBUG
  234.                         printf("browser: intuition event 0x%x\n", class) ;
  235.     #endif
  236.                         done(20, "unexpected intution event") ;
  237.                         break ;
  238.                     }
  239.                 }
  240.             }
  241.         done(20, "broke out of never-breaking loop!") ;
  242.         }
  243.     /*
  244.      * Examine_Menu_Pick - takes a menu pick, and twiddles the state variables
  245.      *    to match that pick.
  246.      */
  247.     static
  248.     Examine_Menu_Pick(Menu_Number) unsigned short Menu_Number; {
  249.         register unsigned short    level, i, dirp ;
  250.         register char        *cp ;
  251.         char            *name, *strchr(char *, int) ;
  252.         struct MenuItem        *ItemAddress(struct Menu *, unsigned short) ;
  253.         /* State variables that describe the current directory */
  254.         static char        Dir_Name[LONGEST_NAME] ;
  255.         static unsigned short    Menu_Level = 0 ;
  256.     
  257.         name = ((struct IntuiText *)
  258.             (ItemAddress(AutoMenu, Menu_Number) -> ItemFill)) -> IText ;
  259.         level = MENUNUM(Menu_Number) ;
  260.     
  261.         /* Got what we want, so clear the menu to avoid confusing the user */
  262.         ClearMenuStrip(My_Window) ;
  263.     
  264.         /* set dirp to FALSE if the name is not a directory or disk */
  265.         dirp = (strchr(name, '/') != NULL || strchr(name, ':') != NULL) ;
  266.     
  267.         /* First, set the directory name right */
  268.         if (level > Menu_Level)            /* Not possible, die */
  269.             done(20, "impossible menu value returned") ;
  270.         else if (level == 0)            /* picked a new disk */
  271.             Dir_Name[0] = '\0' ;
  272.         else if (level < Menu_Level) {        /* Throw away some levels */
  273.             for (i = 1, cp = strchr(Dir_Name, ':'); i < level; i++) {
  274.                 if (cp == NULL) done(20, "broken file name") ;
  275.                 cp = strchr(cp, '/') ;
  276.                 }
  277.             if (cp == NULL) done(20, "broken file name") ;
  278.             *++cp = '\0' ;
  279.             }
  280.         /* else Menu_Level == level, chose a file at current level */
  281.     
  282.         /* Now, fix up the menu and it's state variable */
  283.         while (Menu_Level > level) {
  284.             Menu_Level-- ;
  285.             Menu_Pop() ;
  286.             }
  287.     
  288.         /* If we added a directory, tack it onto the name */
  289.         if (dirp) {
  290.             Menu_Level++ ;
  291.             strncat(Dir_Name, name,    LONGEST_NAME - strlen(Dir_Name) - 1) ;
  292.             }
  293.     
  294.         /* Now, tell the user all about it */
  295.         if (dirp) Add_Dir(Dir_Name, name) ;
  296.         else Display_File(Dir_Name, name) ;
  297.         SetMenuStrip(My_Window, AutoMenu) ;
  298.         }
  299.     /*
  300.      * Add_Dir - given a dir and a name, add the menu name with the files in
  301.      *    dir as entries.
  302.      */
  303.     static
  304.     Add_Dir(dir, name) char *dir, *name; {
  305.         register char            *last_char ;
  306.         register struct FileLock    *my_lock, *Lock(char *, int) ;
  307.         unsigned short            count ;
  308.         struct FileInfoBlock        File_Info ;
  309.         static char            Name_Buf[LONGEST_NAME] ;
  310.     
  311.         /* Fix up the trailing / if it needs it */
  312.         last_char = &dir[strlen(dir) - 1] ;
  313.         if (*last_char == '/') *last_char = '\0' ;
  314.     
  315.         /* Now, start on the directory */
  316.         if ((my_lock = Lock(dir, ACCESS_READ)) == NULL) {
  317.     #ifdef    DEBUG
  318.             printf("browser: error trying to lock %s, IoErr says %d\n",
  319.                     dir, IoErr()) ;
  320.     #endif
  321.             done(20, "can't get lock on file") ;
  322.             }
  323.     
  324.         if (!Examine(my_lock, &File_Info)) done(20, "can't examine file") ;
  325.         if (File_Info . fib_DirEntryType < 0)
  326.             done(20, "Add_Dir called with a non-directory") ;
  327.     
  328.         Menu_Add(name, TRUE) ;
  329.         for (ExNext(my_lock, &File_Info), count = 0;
  330.              IoErr() != ERROR_NO_MORE_ENTRIES;
  331.              ExNext(my_lock, &File_Info), count++)
  332.             if (File_Info . fib_DirEntryType < 0)
  333.                 Menu_Item_Add(File_Info . fib_FileName, ITEMENABLED,
  334.                     0, 0) ;
  335.             else {
  336.                 strcpy(Name_Buf, File_Info . fib_FileName) ;
  337.                 strcat(Name_Buf, "/") ;
  338.                 Menu_Item_Add(Name_Buf, ITEMENABLED, 0, 0) ;
  339.                 }
  340.         if (count == 0) Menu_Item_Add("EMPTY", 0, 0, 0) ;
  341.     
  342.         /* Put everything back */
  343.         if (*last_char == '\0') *last_char = '/' ;
  344.         UnLock(my_lock) ;
  345.         }
  346.     /*
  347.      * Display_File - given a directory path and file name, put the first page of
  348.      *    the file in the window.
  349.      */
  350.     static
  351.     Display_File(dir, name) char *dir, *name; {
  352.         static char    File_Name[LONGEST_NAME] ;
  353.         FILE        *fopen(char *, char *) ;
  354.     
  355.         /* Get the file name */
  356.         strcpy(File_Name, dir) ;
  357.         strcat(File_Name, name) ;
  358.     
  359.         if (infile != NULL) fclose(infile) ;
  360.         if ((infile = fopen(File_Name, "r")) == NULL) {
  361.     #ifdef    DEBUG
  362.             printf("File: %s\n", File_Name) ;
  363.     #endif
  364.             done(20, "can't open file") ;
  365.             }
  366.         Page_File(DOWN_GADGET) ;        /* Down from page 0 */
  367.         }
  368.     /*
  369.      * Page_File - move the file up or down one "page"
  370.      */
  371.     static void
  372.     Page_File(direction) int direction; {
  373.         /* These two should be registers, but it the it breaks the compiler */
  374.         short    Page_Length, where ;
  375.         static char    buffer[LONGEST_LINE], *blanks ;
  376.     
  377.         if (infile == NULL) return ;
  378.     
  379.         blanks =
  380.     "                                                                                 " ;
  381.     
  382.         Page_Length = (My_Window -> Height - 20) / 8 ;
  383.     
  384.         if (direction == UP_GADGET) {        /* Seek back one page */
  385.             if (ftell(infile) < AVG_LINE_LENGTH * (Page_Length + 2))
  386.                 fseek(infile, 0L, 0) ;
  387.             else {
  388.                 fseek(infile, (long) -Page_Length * AVG_LINE_LENGTH, 1) ;
  389.                 fgets(buffer, LONGEST_LINE, infile) ;
  390.                 }
  391.             }
  392.         else if (direction != DOWN_GADGET)
  393.             done(20, "Illegal argument to Page_File") ;
  394.     
  395.         for (where = 17; Page_Length--; where += 8) {
  396.             Move(My_Window -> RPort, 3, where) ;
  397.             Text(My_Window -> RPort, blanks, 79) ;
  398.             Move(My_Window -> RPort, 3, where) ;
  399.             if (fgets(buffer, LONGEST_LINE, infile) == NULL)
  400.                 return ;
  401.             if (strlen(buffer) < LONGEST_LINE - 1)
  402.                 Text(My_Window -> RPort, buffer, strlen(buffer) - 1) ;
  403.             else {
  404.                 Text(My_Window -> RPort, "Long line read!", 15) ;
  405.                 fclose(infile) ;
  406.                 infile = NULL ;
  407.                 return ;
  408.                 }
  409.             }
  410.         }
  411.     /*
  412.      * done - just close everything that's open, and exit.
  413.      */
  414.     static
  415.     done(how, why) int how; char *why; {
  416.     
  417.         if (My_Window) {
  418.             ClearMenuStrip(My_Window) ;
  419.             Menu_Clear() ;
  420.             CloseWindow(My_Window) ;
  421.             }
  422.         if (GfxBase) CloseLibrary(GfxBase) ;
  423.         if (IntuitionBase) CloseLibrary(IntuitionBase) ;
  424.         if (infile) fclose(infile) ;
  425.     
  426.     #ifdef    DEBUG
  427.         printf("browser: %s\n", why) ;
  428.     #endif
  429.         (void) OpenWorkBench() ;
  430.         exit(how) ;
  431.         }
  432.     
  433. //E*O*F browser.c//
  434.  
  435. echo x - menustack.c
  436. sed 's/^    //' > "menustack.c" << '//E*O*F menustack.c//'
  437.     /*
  438.      *  Simple menu package.  Needs lotsa work to handle some cases.
  439.      *
  440.      *  Copyright 1985
  441.      *  Louis A. Mamakos
  442.      *  Software & Stuff
  443.      *  14813 Ashford Place
  444.      *  Laurel, MD  20707
  445.      *
  446.      *  For non-commerical use only.  This program, or any modifications, may not
  447.      *  be sold or incorporated into any product without prior permission from the
  448.      *  author.
  449.      *
  450.      *  Modified by mwm to handle "stacking" menus.
  451.      *    NB - adding item to a menu that's been "popped" back to doesn't work,
  452.      *    and probably never will.
  453.      *     I took at the subitem code at the same time - mwm
  454.      */
  455.     
  456.     /* Change if your terminal isn't * x 400 */
  457.     #define SCREENHEIGHT        200
  458.     
  459.     #include <exec/types.h>
  460.     #include <exec/nodes.h>
  461.     #include <exec/lists.h>
  462.     #include <exec/ports.h>
  463.     #include <exec/devices.h>
  464.     #include <exec/memory.h>
  465.     #include <hardware/blit.h>
  466.     #include <graphics/copper.h>
  467.     #include <graphics/regions.h>
  468.     #include <graphics/rastport.h>
  469.     #include <graphics/gfxbase.h>
  470.     #include <graphics/gels.h>
  471.     #include <intuition/intuition.h>
  472.     
  473.     #define MNUM(menu,item,sub) (SHIFTMENU(menu)|SHIFTITEM(item)|SHIFTSUB(sub))
  474.     
  475.     struct    Mem_Node {
  476.        struct Node mn_Node;
  477.        struct Remember mn_Memory;
  478.        struct Menu *mn_Menu;
  479.        } *Top, *RemHead();
  480.     
  481.     static struct List Memory;
  482.     static int Cur_Menu, Cur_MenuItem, Cur_SubItem;
  483.     static struct Menu *LastMenu;
  484.     static struct MenuItem *LastMenuItem;
  485.     
  486.     struct Menu *AutoMenu;      /* menu struct being dynamically built */
  487.     
  488.     char *strsave();        /* Save a string in the remember list */
  489.     
  490.     Menu_Init()
  491.     {
  492.        Memory.lh_Head = (struct Node *) &(Memory.lh_Tail);
  493.        Memory.lh_TailPred = (struct Node *) &(Memory.lh_Head);
  494.        Memory.lh_Tail = NULL;
  495.        Memory.lh_Type = NT_MEMORY;
  496.        Top = NULL;
  497.        Cur_Menu = Cur_MenuItem = -1;
  498.        AutoMenu = LastMenu = NULL;     /* no menu chain yet */
  499.        LastMenuItem = NULL;
  500.     }
  501.     
  502.     Menu_Clear()
  503.     {
  504.     
  505.        while ((Top = RemHead(&Memory)) != NULL) {
  506.           FreeRemember(&(Top->mn_Memory), TRUE);
  507.           FreeMem(Top, sizeof(struct Mem_Node));
  508.       }
  509.       Menu_Init();            /* Just for safeties sake */
  510.     }
  511.     
  512.     Menu_Pop()
  513.     {
  514.     
  515.        if ((Top = RemHead(&Memory)) == NULL) return NULL;
  516.        FreeRemember(&(Top->mn_Memory), TRUE);
  517.        FreeMem(Top, sizeof(struct Mem_Node));
  518.        /* Now, set Top back to the real list head */
  519.        Top = (struct Mem_Node *) Memory.lh_Head;
  520.        LastMenu = Top->mn_Menu;
  521.        LastMenuItem = NULL;        /* Wrong, but you can't add items here anyway */
  522.        Cur_Menu--;
  523.     }
  524.     /*
  525.      *  Add a MENU item.  Args are the text of the menu item, and an enable
  526.      *  flag.  Returns an Intuition type menu number, with the MenuItem and
  527.      *  Menu SubItem being NOITEM and NOSUB.  The MENUITEM part is valid.
  528.      */
  529.     Menu_Add(name, enabled)
  530.        char *name;
  531.        int enabled;
  532.     {
  533.        register struct Menu *m;
  534.     
  535.        if ((Top = (struct Mem_Node *) AllocMem(
  536.              sizeof(struct Mem_Node), MEMF_PUBLIC | MEMF_CLEAR)) == NULL)
  537.           return NULL;
  538.        Top->mn_Node.ln_Type = NT_MEMORY;
  539.     
  540.        if ((m = (struct Menu *)AllocRemember(&(Top->mn_Memory),
  541.                      sizeof (struct Menu), MEMF_PUBLIC | MEMF_CLEAR)) == NULL)
  542.           return NULL;
  543.        Top->mn_Menu = m;
  544.     
  545.        if (LastMenu == NULL)
  546.           AutoMenu = m;     /* first menu on list */
  547.        else
  548.           LastMenu->NextMenu = m;      /* link it in */
  549.     
  550.        LastMenuItem = NULL;            /* end of previous MenuItem list */
  551.        Cur_MenuItem = -1; /* reset item numbers */
  552.        if (LastMenu == NULL)
  553.           m->LeftEdge = 0;
  554.        else
  555.           m->LeftEdge = LastMenu->LeftEdge + LastMenu->Width;
  556.        m->TopEdge = 0;
  557.        m->Width = strlen(name) * 8;
  558.        Top->mn_Node.ln_Name = m->MenuName = strsave(name);
  559.        m->Height = 0;
  560.        m->Flags = enabled ? MENUENABLED : 0;
  561.        m->FirstItem = NULL;
  562.        LastMenu = m;
  563.     
  564.        AddHead(&Memory, Top);
  565.        return MNUM(++Cur_Menu, NOITEM, NOSUB);
  566.     }
  567.     
  568.     /*
  569.      *  Add a menu item to the current MENU.  Note that Add_Menu *must* be
  570.      *  called before this function.
  571.      */
  572.     Menu_Item_Add(name, flags, mux, ch)
  573.        char *name;       /* name of menu item */
  574.        USHORT flags;
  575.        LONG mux;         /* mutual exclusion mask */
  576.        BYTE ch;        /* command sequence character, if COMMSEQ */
  577.     {
  578.        register struct MenuItem *m, *n;
  579.        register struct IntuiText *it;
  580.     
  581.        flags &= CHECKIT|CHECKED|COMMSEQ|MENUTOGGLE|ITEMENABLED|HIGHCOMP|HIGHBOX;
  582.        if (LastMenu == NULL)
  583.           return MNUM(NOMENU, NOITEM, NOSUB);
  584.     
  585.        if ((m = (struct MenuItem *) AllocRemember(&(Top->mn_Memory),
  586.                      sizeof(struct MenuItem), MEMF_PUBLIC | MEMF_CLEAR)) == NULL)
  587.           return MNUM(NOMENU, NOITEM, NOSUB);
  588.     
  589.        if (LastMenuItem == NULL)
  590.           LastMenu->FirstItem  = m;
  591.        else
  592.           LastMenuItem->NextItem = m;
  593.        m->Flags = flags | ITEMTEXT;
  594.        /*
  595.         *  Check for highlight mode:  if none selected, use HIGHCOMP
  596.         */
  597.        if ((m->Flags & (HIGHCOMP | HIGHBOX)) == 0)
  598.           m->Flags |= HIGHCOMP;
  599.        m->Command = ch;
  600.        m->MutualExclude = mux;
  601.        m->SubItem = NULL;
  602.        m->ItemFill = (APTR) AllocRemember(&(Top->mn_Memory),
  603.                           sizeof(struct IntuiText), MEMF_PUBLIC | MEMF_CLEAR);
  604.        it = (struct IntuiText *) m->ItemFill;
  605.        it->FrontPen = AUTOFRONTPEN;
  606.        it->BackPen = AUTOBACKPEN;
  607.        it->DrawMode = JAM2;
  608.        if (flags & CHECKIT)
  609.           it->LeftEdge = CHECKWIDTH + 1;
  610.        else
  611.           it->LeftEdge = 1;
  612.        it->TopEdge = 1;
  613.        it->ITextFont = NULL;      /* default font */
  614.        it->IText = strsave(name);
  615.        it->NextText = NULL;
  616.        m->Width = 0;
  617.        if (LastMenuItem == NULL) {
  618.           m->TopEdge = 2;
  619.           m->LeftEdge = 0;
  620.        } else if (LastMenuItem->TopEdge + 40 > SCREENHEIGHT) {
  621.           m->TopEdge = 2;
  622.           m->LeftEdge = LastMenuItem->LeftEdge + LastMenuItem->Width + 12;
  623.        } else {
  624.           m->TopEdge = LastMenuItem->TopEdge + 12;
  625.           m->LeftEdge = LastMenuItem->LeftEdge;
  626.        }
  627.        if (flags & CHECKIT)
  628.           m->Width += CHECKWIDTH;
  629.        if (flags & COMMSEQ)
  630.           m->Width += COMMWIDTH + 20;
  631.        m->Width += IntuiTextLength(m->ItemFill);
  632.        m->Height = 10;
  633.        /*
  634.         *  Check last menu item's width to see if it is larger than this
  635.         *  item's.  If new item is larger, then update width of all other
  636.         *  items.
  637.         */
  638.        if (LastMenuItem) {
  639.           if (LastMenuItem->Width > m->Width)
  640.             m->Width = LastMenuItem->Width;
  641.           else {
  642.              register short delta = m->Width - LastMenuItem->Width;
  643.     
  644.          for (n = LastMenu->FirstItem; n != m; n = n->NextItem) {
  645.             n->Width = m->Width;
  646.             if (n->LeftEdge > 0) n->LeftEdge += delta;
  647.          }
  648.          if (m->LeftEdge > 0) m->LeftEdge += delta;
  649.           }
  650.        }
  651.        LastMenuItem = m;
  652.        return MNUM(Cur_Menu, ++Cur_MenuItem, NOSUB);
  653.     }
  654.     
  655.     
  656.     
  657.     char *
  658.     strsave(string) char *string; {
  659.        char *out ;
  660.     
  661.        out = (char *) AllocRemember(&(Top->mn_Memory), strlen(string) + 1,
  662.         MEMF_PUBLIC) ;
  663.        if (out == NULL) return NULL ;
  664.        (void) strcpy(out, string) ;
  665.        return out ;
  666.     }
  667. //E*O*F menustack.c//
  668.  
  669. exit 0
  670.  
  671.