Path: seismo!harvard!talcott!panda!sources-request From: sources-request@panda.UUCP Newsgroups: mod.sources Subject: Amiga file browser Message-ID: <1561@panda.UUCP> Date: 28 Mar 86 11:19:02 GMT Sender: jpn@panda.UUCP Lines: 664 Approved: jpn@panda.UUCP Mod.sources: Volume 4, Issue 40 Submitted by: Mike (I'll be mellow when I'm dead) Meyer # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # # This archive contains: # README browser.c menustack.c echo x - README sed 's/^ //' > "README" << '//E*O*F README//' Here we have a file browser for the Amiga. 50% based on the the PARC browsers,and 50% based on a browser I wrote for Unix long ago (whose name, I now realize, was probably inspired by the PARC browsers). This is a "level 0" version. Useable, but it needs some things done to. I've got more important things to do, so I'm turning it loose to the net, in the hopes that someone else will improve it before I get back to it. Things that need to be fixed: 1) Theres a loose pointer in there somewhere, but it only seem to mangle things if DEBUG is turned on. Not sure what's up. 2) The UP and DOWN gadgets need to turn into a scroll bar gadget. 3) The routine that pages the file needs to be re-written to use the console driver, and handle lines longer than the window is wide in a more intelligent manner. 4) The initial (disks) menu needs to look to see what is really there, instead of just giving you the default three. Putting in the assigned'ed things at the same time might not be a bad idea. Should you fix #1, or add any of #'s 2-4, please send me the source. Likewise, any ideas you have or implement, let me know, and I'll see about getting them into the first real version. "browser.c" << '//E*O*F browser.c//' /* * browser - Rummage around on disks. * * copyright (c) 1986, Mike Meyer * * Permission is hereby granted to distribute this program, so long as this * source file is distributed with it, and this copyright notice is not * removed from the file. * * Locks, general - do I need a stack of locks, one per directory level? * do I have to free the lock in done? */ #include #include #include #include #include #include #define INTUITION_REV 1 #define GRAPHICS_REV 1 #define LONGEST_NAME 80 /* Longest file name we can deal with */ #define LONGEST_LINE 256 /* Longest line we will deal with */ #define AVG_LINE_LENGTH 66 /* A guess, tune it if you need to */ #define UP_GADGET ((unsigned short) 0) #define DOWN_GADGET ((unsigned short) 1) #define GWIDTH 16 /* Width of my two gadgets */ #define GHEIGHT 9 /* and their heights */ /* * Pictures for the up and down arrows */ static USHORT arrows[2][GHEIGHT] = { {0xFE7F, 0xFC3F, 0xFA5F, 0xF66F, /* Up */ 0xFE7F, 0xFE7F, 0xFE7F, 0xFE7F, 0xFE7F}, {0xFE7F, 0xFE7F, 0xFE7F, 0xFE7F, /* Down */ 0xFE7F, 0xF66F, 0xFA5F, 0xFC3F, 0xFE7F} } ; /* * Now, the Image structures that use the arrows */ static struct Image Arrow_Image[2] = { {0, 0, GWIDTH, GHEIGHT, 1, arrows[UP_GADGET], 1, 0, /* Up */ (struct Image *) NULL}, {0, 0, GWIDTH, GHEIGHT, 1, arrows[DOWN_GADGET], 1, 0, /* Down */ (struct Image *) NULL} } ; /* * Now, my two Gadget structures */ static struct Gadget Up_Gadget = { (struct Gadget *) NULL, 0, 1 - GHEIGHT, /* Left, Top */ GWIDTH, GHEIGHT, GRELBOTTOM | GADGIMAGE /* Standard bottom border gadget */ | GADGHCOMP, RELVERIFY | BOTTOMBORDER, /* Messages when released */ BOOLGADGET, /* These be boolean gadgets */ (APTR) &(Arrow_Image[UP_GADGET]), (APTR) NULL, /* No rendering image, using HCOMP */ (struct IntuiText *) NULL, 0, /* No mutex */ (APTR) NULL, /* Nothing special */ UP_GADGET, /* Yes, this is the up gadget */ (APTR) NULL /* And nothing of mine */ } ; static struct Gadget Down_Gadget = { &Up_Gadget, /* Next gadget is Up_Gadget */ GWIDTH + 2, 1 - GHEIGHT, /* Left, Top */ GWIDTH, GHEIGHT, GRELBOTTOM | GADGIMAGE /* Standard bottom border gadget */ | GADGHCOMP, RELVERIFY | BOTTOMBORDER, /* Messages when released */ BOOLGADGET, /* These be boolean gadgets */ (APTR) &(Arrow_Image[DOWN_GADGET]), (APTR) NULL, /* No rendering image, using HCOMP */ (struct IntuiText *) NULL, 0, /* No mutex */ (APTR) NULL, /* Nothing special */ DOWN_GADGET, /* Yes, this is the up gadget */ (APTR) NULL /* And nothing of mine */ } ; /* * Now, the window for it all */ static struct NewWindow New_Window = { 0, 0, 640, 200, /* Full screen */ -1, -1, /* Default pens */ MENUPICK | CLOSEWINDOW /* I want to know about menupicks */ | GADGETUP, /* Window closes and gadgets */ ACTIVATE /* Standard window */ | SMART_REFRESH | NOCAREREFRESH | SIZEBBOTTOM | WINDOWSIZING | WINDOWDEPTH | WINDOWCLOSE | WINDOWDRAG, &Down_Gadget, /* Add my gadgets */ (struct Image *) NULL, "Browser 0.0", /* Title */ (struct Screen *) NULL, (struct BitMap *) NULL, 100, 40, /* Minimum sizes */ 640, 200, /* Maximum sizes */ WBENCHSCREEN /* and put it on the workbench */ } ; /* * My very own variables (mostly for done) */ static struct Window *My_Window = NULL ; static FILE *infile = NULL ; /* Current input file */ static void Page_File(unsigned short) ; /* * And someone else's variables */ struct IntuitionBase *IntuitionBase ; struct GfxBase *GfxBase ; extern struct Menu *AutoMenu ; /* * Finally, declare the string twiddling functions as voids */ void strcat(char *, char *), strcpy(char *, char *) , strncat(char *, char *, int) ; #ifdef DEBUG main() { #else _main() { #endif register struct IntuiMessage *message, *GetMsg(struct MsgPort *) ; register unsigned short class, code ; /* Set up the world this lives in */ if ((IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", INTUITION_REV)) == NULL) done(20, "can't open intuition library") ; if ((GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", GRAPHICS_REV)) == NULL) done(20, "can't open graphics library") ; if ((My_Window = (struct Window *) OpenWindow(&New_Window)) == NULL) done(20, "can't open the window") ; SetAPen(My_Window -> RPort, 1) ; /* Should be default! */ /* Set up the first menu level */ Menu_Init() ; Menu_Add("disks ", TRUE) ; Menu_Item_Add("df0:", ITEMENABLED, 0, 0) ; Menu_Item_Add("df1:", ITEMENABLED, 0, 0) ; Menu_Item_Add("ram:", ITEMENABLED, 0, 0) ; SetMenuStrip(My_Window, AutoMenu) ; /* Now spin on messages, handling them as they arrive */ for (;;) { Wait(1 << My_Window -> UserPort -> mp_SigBit) ; while (message = GetMsg(My_Window -> UserPort)) { class = message -> Class ; code = message -> Code ; ReplyMsg(message) ; switch (class) { case CLOSEWINDOW: done(0, "exit") ; case MENUPICK: if (code != MENUNULL) Examine_Menu_Pick(code) ; break ; case GADGETUP: Page_File(((struct Gadget *) (message -> IAddress)) -> GadgetID) ; break ; default: #ifdef DEBUG printf("browser: intuition event 0x%x\n", class) ; #endif done(20, "unexpected intution event") ; break ; } } } done(20, "broke out of never-breaking loop!") ; } /* * Examine_Menu_Pick - takes a menu pick, and twiddles the state variables * to match that pick. */ static Examine_Menu_Pick(Menu_Number) unsigned short Menu_Number; { register unsigned short level, i, dirp ; register char *cp ; char *name, *strchr(char *, int) ; struct MenuItem *ItemAddress(struct Menu *, unsigned short) ; /* State variables that describe the current directory */ static char Dir_Name[LONGEST_NAME] ; static unsigned short Menu_Level = 0 ; name = ((struct IntuiText *) (ItemAddress(AutoMenu, Menu_Number) -> ItemFill)) -> IText ; level = MENUNUM(Menu_Number) ; /* Got what we want, so clear the menu to avoid confusing the user */ ClearMenuStrip(My_Window) ; /* set dirp to FALSE if the name is not a directory or disk */ dirp = (strchr(name, '/') != NULL || strchr(name, ':') != NULL) ; /* First, set the directory name right */ if (level > Menu_Level) /* Not possible, die */ done(20, "impossible menu value returned") ; else if (level == 0) /* picked a new disk */ Dir_Name[0] = '\0' ; else if (level < Menu_Level) { /* Throw away some levels */ for (i = 1, cp = strchr(Dir_Name, ':'); i < level; i++) { if (cp == NULL) done(20, "broken file name") ; cp = strchr(cp, '/') ; } if (cp == NULL) done(20, "broken file name") ; *++cp = '\0' ; } /* else Menu_Level == level, chose a file at current level */ /* Now, fix up the menu and it's state variable */ while (Menu_Level > level) { Menu_Level-- ; Menu_Pop() ; } /* If we added a directory, tack it onto the name */ if (dirp) { Menu_Level++ ; strncat(Dir_Name, name, LONGEST_NAME - strlen(Dir_Name) - 1) ; } /* Now, tell the user all about it */ if (dirp) Add_Dir(Dir_Name, name) ; else Display_File(Dir_Name, name) ; SetMenuStrip(My_Window, AutoMenu) ; } /* * Add_Dir - given a dir and a name, add the menu name with the files in * dir as entries. */ static Add_Dir(dir, name) char *dir, *name; { register char *last_char ; register struct FileLock *my_lock, *Lock(char *, int) ; unsigned short count ; struct FileInfoBlock File_Info ; static char Name_Buf[LONGEST_NAME] ; /* Fix up the trailing / if it needs it */ last_char = &dir[strlen(dir) - 1] ; if (*last_char == '/') *last_char = '\0' ; /* Now, start on the directory */ if ((my_lock = Lock(dir, ACCESS_READ)) == NULL) { #ifdef DEBUG printf("browser: error trying to lock %s, IoErr says %d\n", dir, IoErr()) ; #endif done(20, "can't get lock on file") ; } if (!Examine(my_lock, &File_Info)) done(20, "can't examine file") ; if (File_Info . fib_DirEntryType < 0) done(20, "Add_Dir called with a non-directory") ; Menu_Add(name, TRUE) ; for (ExNext(my_lock, &File_Info), count = 0; IoErr() != ERROR_NO_MORE_ENTRIES; ExNext(my_lock, &File_Info), count++) if (File_Info . fib_DirEntryType < 0) Menu_Item_Add(File_Info . fib_FileName, ITEMENABLED, 0, 0) ; else { strcpy(Name_Buf, File_Info . fib_FileName) ; strcat(Name_Buf, "/") ; Menu_Item_Add(Name_Buf, ITEMENABLED, 0, 0) ; } if (count == 0) Menu_Item_Add("EMPTY", 0, 0, 0) ; /* Put everything back */ if (*last_char == '\0') *last_char = '/' ; UnLock(my_lock) ; } /* * Display_File - given a directory path and file name, put the first page of * the file in the window. */ static Display_File(dir, name) char *dir, *name; { static char File_Name[LONGEST_NAME] ; FILE *fopen(char *, char *) ; /* Get the file name */ strcpy(File_Name, dir) ; strcat(File_Name, name) ; if (infile != NULL) fclose(infile) ; if ((infile = fopen(File_Name, "r")) == NULL) { #ifdef DEBUG printf("File: %s\n", File_Name) ; #endif done(20, "can't open file") ; } Page_File(DOWN_GADGET) ; /* Down from page 0 */ } /* * Page_File - move the file up or down one "page" */ static void Page_File(direction) int direction; { /* These two should be registers, but it the it breaks the compiler */ short Page_Length, where ; static char buffer[LONGEST_LINE], *blanks ; if (infile == NULL) return ; blanks = " " ; Page_Length = (My_Window -> Height - 20) / 8 ; if (direction == UP_GADGET) { /* Seek back one page */ if (ftell(infile) < AVG_LINE_LENGTH * (Page_Length + 2)) fseek(infile, 0L, 0) ; else { fseek(infile, (long) -Page_Length * AVG_LINE_LENGTH, 1) ; fgets(buffer, LONGEST_LINE, infile) ; } } else if (direction != DOWN_GADGET) done(20, "Illegal argument to Page_File") ; for (where = 17; Page_Length--; where += 8) { Move(My_Window -> RPort, 3, where) ; Text(My_Window -> RPort, blanks, 79) ; Move(My_Window -> RPort, 3, where) ; if (fgets(buffer, LONGEST_LINE, infile) == NULL) return ; if (strlen(buffer) < LONGEST_LINE - 1) Text(My_Window -> RPort, buffer, strlen(buffer) - 1) ; else { Text(My_Window -> RPort, "Long line read!", 15) ; fclose(infile) ; infile = NULL ; return ; } } } /* * done - just close everything that's open, and exit. */ static done(how, why) int how; char *why; { if (My_Window) { ClearMenuStrip(My_Window) ; Menu_Clear() ; CloseWindow(My_Window) ; } if (GfxBase) CloseLibrary(GfxBase) ; if (IntuitionBase) CloseLibrary(IntuitionBase) ; if (infile) fclose(infile) ; #ifdef DEBUG printf("browser: %s\n", why) ; #endif (void) OpenWorkBench() ; exit(how) ; } //E*O*F browser.c// echo x - menustack.c sed 's/^ //' > "menustack.c" << '//E*O*F menustack.c//' /* * Simple menu package. Needs lotsa work to handle some cases. * * Copyright 1985 * Louis A. Mamakos * Software & Stuff * 14813 Ashford Place * Laurel, MD 20707 * * For non-commerical use only. This program, or any modifications, may not * be sold or incorporated into any product without prior permission from the * author. * * Modified by mwm to handle "stacking" menus. * NB - adding item to a menu that's been "popped" back to doesn't work, * and probably never will. * I took at the subitem code at the same time - mwm */ /* Change if your terminal isn't * x 400 */ #define SCREENHEIGHT 200 #include #include #include #include #include #include #include #include #include #include #include #include #include #define MNUM(menu,item,sub) (SHIFTMENU(menu)|SHIFTITEM(item)|SHIFTSUB(sub)) struct Mem_Node { struct Node mn_Node; struct Remember mn_Memory; struct Menu *mn_Menu; } *Top, *RemHead(); static struct List Memory; static int Cur_Menu, Cur_MenuItem, Cur_SubItem; static struct Menu *LastMenu; static struct MenuItem *LastMenuItem; struct Menu *AutoMenu; /* menu struct being dynamically built */ char *strsave(); /* Save a string in the remember list */ Menu_Init() { Memory.lh_Head = (struct Node *) &(Memory.lh_Tail); Memory.lh_TailPred = (struct Node *) &(Memory.lh_Head); Memory.lh_Tail = NULL; Memory.lh_Type = NT_MEMORY; Top = NULL; Cur_Menu = Cur_MenuItem = -1; AutoMenu = LastMenu = NULL; /* no menu chain yet */ LastMenuItem = NULL; } Menu_Clear() { while ((Top = RemHead(&Memory)) != NULL) { FreeRemember(&(Top->mn_Memory), TRUE); FreeMem(Top, sizeof(struct Mem_Node)); } Menu_Init(); /* Just for safeties sake */ } Menu_Pop() { if ((Top = RemHead(&Memory)) == NULL) return NULL; FreeRemember(&(Top->mn_Memory), TRUE); FreeMem(Top, sizeof(struct Mem_Node)); /* Now, set Top back to the real list head */ Top = (struct Mem_Node *) Memory.lh_Head; LastMenu = Top->mn_Menu; LastMenuItem = NULL; /* Wrong, but you can't add items here anyway */ Cur_Menu--; } /* * Add a MENU item. Args are the text of the menu item, and an enable * flag. Returns an Intuition type menu number, with the MenuItem and * Menu SubItem being NOITEM and NOSUB. The MENUITEM part is valid. */ Menu_Add(name, enabled) char *name; int enabled; { register struct Menu *m; if ((Top = (struct Mem_Node *) AllocMem( sizeof(struct Mem_Node), MEMF_PUBLIC | MEMF_CLEAR)) == NULL) return NULL; Top->mn_Node.ln_Type = NT_MEMORY; if ((m = (struct Menu *)AllocRemember(&(Top->mn_Memory), sizeof (struct Menu), MEMF_PUBLIC | MEMF_CLEAR)) == NULL) return NULL; Top->mn_Menu = m; if (LastMenu == NULL) AutoMenu = m; /* first menu on list */ else LastMenu->NextMenu = m; /* link it in */ LastMenuItem = NULL; /* end of previous MenuItem list */ Cur_MenuItem = -1; /* reset item numbers */ if (LastMenu == NULL) m->LeftEdge = 0; else m->LeftEdge = LastMenu->LeftEdge + LastMenu->Width; m->TopEdge = 0; m->Width = strlen(name) * 8; Top->mn_Node.ln_Name = m->MenuName = strsave(name); m->Height = 0; m->Flags = enabled ? MENUENABLED : 0; m->FirstItem = NULL; LastMenu = m; AddHead(&Memory, Top); return MNUM(++Cur_Menu, NOITEM, NOSUB); } /* * Add a menu item to the current MENU. Note that Add_Menu *must* be * called before this function. */ Menu_Item_Add(name, flags, mux, ch) char *name; /* name of menu item */ USHORT flags; LONG mux; /* mutual exclusion mask */ BYTE ch; /* command sequence character, if COMMSEQ */ { register struct MenuItem *m, *n; register struct IntuiText *it; flags &= CHECKIT|CHECKED|COMMSEQ|MENUTOGGLE|ITEMENABLED|HIGHCOMP|HIGHBOX; if (LastMenu == NULL) return MNUM(NOMENU, NOITEM, NOSUB); if ((m = (struct MenuItem *) AllocRemember(&(Top->mn_Memory), sizeof(struct MenuItem), MEMF_PUBLIC | MEMF_CLEAR)) == NULL) return MNUM(NOMENU, NOITEM, NOSUB); if (LastMenuItem == NULL) LastMenu->FirstItem = m; else LastMenuItem->NextItem = m; m->Flags = flags | ITEMTEXT; /* * Check for highlight mode: if none selected, use HIGHCOMP */ if ((m->Flags & (HIGHCOMP | HIGHBOX)) == 0) m->Flags |= HIGHCOMP; m->Command = ch; m->MutualExclude = mux; m->SubItem = NULL; m->ItemFill = (APTR) AllocRemember(&(Top->mn_Memory), sizeof(struct IntuiText), MEMF_PUBLIC | MEMF_CLEAR); it = (struct IntuiText *) m->ItemFill; it->FrontPen = AUTOFRONTPEN; it->BackPen = AUTOBACKPEN; it->DrawMode = JAM2; if (flags & CHECKIT) it->LeftEdge = CHECKWIDTH + 1; else it->LeftEdge = 1; it->TopEdge = 1; it->ITextFont = NULL; /* default font */ it->IText = strsave(name); it->NextText = NULL; m->Width = 0; if (LastMenuItem == NULL) { m->TopEdge = 2; m->LeftEdge = 0; } else if (LastMenuItem->TopEdge + 40 > SCREENHEIGHT) { m->TopEdge = 2; m->LeftEdge = LastMenuItem->LeftEdge + LastMenuItem->Width + 12; } else { m->TopEdge = LastMenuItem->TopEdge + 12; m->LeftEdge = LastMenuItem->LeftEdge; } if (flags & CHECKIT) m->Width += CHECKWIDTH; if (flags & COMMSEQ) m->Width += COMMWIDTH + 20; m->Width += IntuiTextLength(m->ItemFill); m->Height = 10; /* * Check last menu item's width to see if it is larger than this * item's. If new item is larger, then update width of all other * items. */ if (LastMenuItem) { if (LastMenuItem->Width > m->Width) m->Width = LastMenuItem->Width; else { register short delta = m->Width - LastMenuItem->Width; for (n = LastMenu->FirstItem; n != m; n = n->NextItem) { n->Width = m->Width; if (n->LeftEdge > 0) n->LeftEdge += delta; } if (m->LeftEdge > 0) m->LeftEdge += delta; } } LastMenuItem = m; return MNUM(Cur_Menu, ++Cur_MenuItem, NOSUB); } char * strsave(string) char *string; { char *out ; out = (char *) AllocRemember(&(Top->mn_Memory), strlen(string) + 1, MEMF_PUBLIC) ; if (out == NULL) return NULL ; (void) strcpy(out, string) ; return out ; } //E*O*F menustack.c// exit 0